Initial commit: JuanitoSport - Muay Thai & Yog4Fight PWA
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Header from "@/components/Header";
|
||||
import { signOut } from "next-auth/react";
|
||||
|
||||
export default function YogaDashboard({ user, messages, unreadCount }: any) {
|
||||
const router = useRouter();
|
||||
const profile = user?.yoga;
|
||||
const attendances = profile?.attendances || [];
|
||||
const remaining = profile?.sessionsRemaining ?? 0;
|
||||
|
||||
const pricePerSession = 9;
|
||||
const sessionsDone = attendances.length;
|
||||
const totalAttendedCost = sessionsDone * pricePerSession;
|
||||
|
||||
return (
|
||||
<main className="min-h-screen pb-16">
|
||||
<Header />
|
||||
<div className="mx-auto max-w-3xl px-4 py-6">
|
||||
<div className="mb-6 flex items-start justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-extrabold">
|
||||
🧘 Salut, {user.name.split(" ")[0]} !
|
||||
</h1>
|
||||
<p className="mt-1 text-sm text-white/60">
|
||||
Ton espace Yog4Fight
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => signOut({ callbackUrl: "/" })}
|
||||
className="rounded-xl border border-white/10 bg-white/5 p-2 text-sm text-white/60"
|
||||
title="Déconnexion"
|
||||
>
|
||||
🚪
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Compteur de séances */}
|
||||
<section className="mb-6">
|
||||
<div className="card text-center">
|
||||
<p className="text-sm text-white/60">Séances restantes</p>
|
||||
<div className="my-2 flex items-center justify-center gap-1 text-5xl font-extrabold text-emerald-300">
|
||||
{remaining}
|
||||
<span className="text-xl">
|
||||
séance{remaining > 1 ? "s" : ""}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/50">
|
||||
{sessionsDone} séance{sessionsDone > 1 ? "s" : ""} suivie
|
||||
{sessionsDone > 1 ? "s" : ""}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Coût */}
|
||||
<section className="mb-6">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="card text-center">
|
||||
<p className="text-xs text-white/50">Prix par séance</p>
|
||||
<p className="mt-1 text-xl font-bold text-emerald-300">
|
||||
{pricePerSession} €
|
||||
</p>
|
||||
</div>
|
||||
<div className="card text-center">
|
||||
<p className="text-xs text-white/50">Coût si assidu</p>
|
||||
<p className="mt-1 text-xl font-bold text-emerald-300">
|
||||
Tarif dégressif
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-2 text-center text-xs text-white/50">
|
||||
Demande à Juanito le tarif dégressif qui s'applique à
|
||||
l'assiduité 💪
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Messages de Juanito */}
|
||||
<section className="mb-6">
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<h2 className="text-lg font-bold">
|
||||
💬 Messages de Juanito{" "}
|
||||
{unreadCount > 0 && (
|
||||
<span className="ml-1 rounded-full bg-emerald-600 px-2 py-0.5 text-xs font-bold text-white">
|
||||
{unreadCount} nouveau(x)
|
||||
</span>
|
||||
)}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{messages.length === 0 && (
|
||||
<p className="card text-sm text-white/50">
|
||||
Aucun message pour le moment.
|
||||
</p>
|
||||
)}
|
||||
{messages.map((msg: any) => (
|
||||
<div
|
||||
key={msg.id}
|
||||
className={`card ${!msg.isRead ? "border-emerald-500/40 bg-emerald-500/5" : ""}`}
|
||||
onClick={() => {
|
||||
if (!msg.isRead) {
|
||||
fetch("/api/messages", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ messageId: msg.id }),
|
||||
}).then(() => router.refresh());
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h3 className="font-semibold">{msg.title}</h3>
|
||||
<p className="mt-1 whitespace-pre-line text-sm text-white/75">
|
||||
{msg.content}
|
||||
</p>
|
||||
<p className="mt-2 text-xs text-white/40">
|
||||
{new Date(msg.createdAt).toLocaleDateString("fr-FR")}
|
||||
</p>
|
||||
</div>
|
||||
{!msg.isRead && (
|
||||
<span className="mt-1 h-3 w-3 shrink-0 rounded-full bg-emerald-500" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Historique séances */}
|
||||
<section className="card">
|
||||
<h3 className="mb-3 font-bold">📋 Mes séances suivies</h3>
|
||||
{attendances.length === 0 && (
|
||||
<p className="text-sm text-white/50">
|
||||
Aucune séance enregistrée pour l'instant.
|
||||
</p>
|
||||
)}
|
||||
<div className="max-h-64 space-y-2 overflow-y-auto">
|
||||
{attendances.map((a: any) => (
|
||||
<div
|
||||
key={a.id}
|
||||
className="flex items-center justify-between rounded-xl bg-white/5 px-3 py-2 text-sm"
|
||||
>
|
||||
<span className="text-white/70">
|
||||
{new Date(a.date).toLocaleDateString("fr-FR", {
|
||||
weekday: "long",
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
})}
|
||||
</span>
|
||||
{a.pricePaid !== null && (
|
||||
<span className="tabular-nums text-emerald-300 text-xs">
|
||||
{a.pricePaid} €
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { redirect } from "next/navigation";
|
||||
import YogaDashboard from "./dashboard";
|
||||
|
||||
export default async function YogaPage() {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session?.user) redirect("/login");
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: session.user.id },
|
||||
include: {
|
||||
yoga: {
|
||||
include: {
|
||||
attendances: { orderBy: { date: "desc" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) redirect("/login");
|
||||
|
||||
const messages = await prisma.message.findMany({
|
||||
orderBy: { createdAt: "desc" },
|
||||
where: {
|
||||
OR: [{ category: null }, { category: "general" }, { category: "yoga" }],
|
||||
},
|
||||
include: {
|
||||
readBy: { where: { userId: user.id } },
|
||||
},
|
||||
});
|
||||
|
||||
const unreadCount = messages.filter((m) => m.readBy.length === 0).length;
|
||||
|
||||
return (
|
||||
<YogaDashboard
|
||||
user={user}
|
||||
messages={messages.map((m) => ({ ...m, isRead: m.readBy.length > 0 }))}
|
||||
unreadCount={unreadCount}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user