frontend/app/routes/stress.tsx
Nathan Lamy fbb9158684
All checks were successful
Deploy to Netlify / Deploy to Netlify (push) Successful in 1m16s
feat: add stress
2025-12-23 15:15:11 +01:00

435 lines
21 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useEffect, useState } from "react";
import { Info } from "lucide-react";
import { Button } from "~/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "~/components/ui/dialog";
import { Label } from "~/components/ui/label";
import { RadioGroup, RadioGroupItem } from "~/components/ui/radio-group";
import { TypingText, TypingTextCursor } from "~/components/ui/text-typing";
const ENCOURAGING_MESSAGES = [
// --- Les Classiques (Gardés) ---
"Tu es plus fort(e) que tu ne le penses. Continue comme ça ! 💪",
"Chaque jour est un pas de plus vers ton objectif. Bravo ! 🌟",
"Ta persévérance est admirable. Tu vas réussir ! 🎯",
"N'oublie pas de te reposer, le sommeil fixe la mémoire ! 😌",
"Les meilleurs résultats viennent avec le temps. Patience ! ⏰",
"Tu fais un travail incroyable. Continue ainsi ! 🚀",
"Crois en toi, tu as tout pour réussir ! ✨",
"Chaque exercice résolu te rapproche de ton rêve ! 📚",
"Ton travail acharné paiera, garde confiance ! 🌈",
"Tu es sur la bonne voie. Reste concentré(e) ! 🎓",
"Prends soin de toi, ta santé mentale est ta meilleure arme ! 💙",
"Ta détermination est inspirante ! 🔥",
"Les concours ne sont qu'une étape, tu es déjà quelqu'un de formidable ! 🌸",
"N'aie pas peur des erreurs, c'est là que tu apprends le plus ! 📝",
"Tu progresses chaque jour, même les jours où tu as l'impression de reculer ! 🌱",
"Ta famille et tes amis sont fiers de toi ! 💖",
"Le meilleur est à venir, l'école de tes rêves t'attend ! 🌅",
"Chaque difficulté surmontée te rend plus fort(e) ! 💎",
"Tu as choisi un chemin difficile, sois fier(e) de le parcourir ! 🏔️",
"N'oublie pas pourquoi tu as commencé. Tu vas y arriver ! 🎪",
// --- Les Nouveaux (Spécial Prépa) ---
"C'est un marathon, pas un sprint. Respire. 🏃",
"Même les major de promo doutent. Toi, tu avances. 🛡️",
"Une mauvaise note en khôlle ne définit pas ta valeur. Relève-toi. 🦋",
"Rappelle-toi la sensation de comprendre enfin une démo complexe. Cherche ça ! 💡",
"Tu es en train de te forger une capacité de travail pour la vie. 🧠",
"L'effort d'aujourd'hui est le sourire de toi-même en juillet. ☀️",
"Fais-le pour le 'toi' qui recevra son affectation SIGEM. 📩",
"La douleur de la discipline pèse moins lourd que celle du regret. ⚖️",
"Tu n'as pas besoin d'être un génie, juste d'être constant. 🐢",
"Visualise ton nom sur la liste des admis. Ça arrive. 👁️",
"Ce chapitre qui te résiste finira par céder. Insiste. 🔨",
"Tu n'es pas seul(e), des milliers d'autres galèrent aussi. Courage ! 🤝",
"Prends 5 minutes pour t'aérer, tu seras plus efficace après. 🌳",
"La prépa, c'est l'école de l'humilité et de la résilience. Tu gères. 🧘",
"Chaque théorème appris est une munition pour le jour J. 🔫",
"Ne te compare pas aux autres, compare-toi à toi hier. 📈",
"Tu construis ton avenir, brique par brique, DM par DM. 🏗️",
"C'est normal d'être fatigué(e), tu accomplis quelque chose de grand. 🛌",
"Garde la tête froide et le cœur chaud. ❤️",
"Il n'y a pas de question bête, il n'y a que des points à gagner. 🙋",
"Le déclic arrive souvent quand on s'y attend le moins. ⚡",
"Sois bienveillant(e) avec toi-même, tu es ton propre moteur. 🚗",
"Regarde tout le chemin parcouru depuis le début de l'année ! 🗺️",
"Tu es capable de comprendre des choses que 99% des gens ignorent. 🌌",
"L'intégration n'est pas une question de chance, c'est une question de ténacité. 🍀",
"Buvard ou Major, l'important c'est de tout donner. 🦁",
"Ton cerveau est un muscle, et là, tu fais de la muscu de haut niveau. 🏋️",
"Rien n'est joué jusqu'à la dernière épreuve. Fonce ! 🏁",
"La réussite, c'est tomber 7 fois et se relever 8. 🩹",
"Fais confiance à ta préparation. Tu as bossé pour ça. 🧱",
"Un jour, tu riras de cette période avec tes potes d'école. 🍻",
"Tu n'étudies pas pour le prof, tu étudies pour ta liberté future. 🕊️",
"Accroche-toi, la vue est superbe depuis le sommet. ⛰️",
"Ta capacité à encaisser la charge de travail est impressionnante. 🔋",
"Ne laisse pas un DS raté gâcher ta semaine. Reset. 🔄",
"Tu es l'architecte de ta propre réussite. 📐",
"Chaque minute investie maintenant te rapportera des années de confort. 🏦",
"Tu as le droit de craquer, mais tu as le devoir de revenir. 🥊",
"Pense à la fierté de porter le tricorne, le képi ou l'uniforme. 🧢",
"La prépa t'apprend à réfléchir, personne ne pourra t'enlever ça. 🗝️",
"C'est dur ? C'est que tu es en train de monter de niveau. 🆙",
"Garde le cap, la tempête finit toujours par passer. ⛵",
"Tu es bien plus intelligent(e) que tu ne le crois sous stress. 🧠",
"Fais-toi un bon thé/café et remets-toi en selle. ☕",
"Le succès est la somme de petits efforts répétés jour après jour. ",
"Ne lâche rien, l'admissibilité se joue parfois à des détails. 🔍",
"Tu as le potentiel pour intégrer l'école qui te correspond. 🧩",
"Respire par le ventre. Tout va bien se passer. 🌬️",
"Ta volonté est ton plus grand talent. ⭐",
"Allez, encore un petit effort. Tu es un(e) guerrier(e). ⚔️",
];
const STRESSFUL_MESSAGES = [
// --- Les Classiques (Gardés) ---
"Pendant que tu scroll, tes concurrents bossent les intégrales. 📉",
"Netflix ou les concours ? Choisis vite. ⏳",
"Les places à l'X ne se gagnent pas sur TikTok. 📱",
"Ton futur toi te regarde avec déception là. 👀",
"Les autres sont déjà au chapitre suivant... 📖",
"Ce DM ne va pas se faire tout seul. Allez hop ! 📝",
"Chaque minute perdue est une place de perdue au classement. ⚠️",
"Tu veux vraiment passer 5/2 ? Bouge-toi ! 🏃",
"Tes notes ne vont pas s'améliorer par magie. 📊",
"La médiocrité est confortable, mais l'X c'est mieux. 🤔",
"Tes concurrents ne prennent pas de pause, eux. ⚡",
"Le niveau monte, et toi tu stagnes. Réagis ! 📈",
"À ce rythme, tu vas finir à la fac (non je rigole... ou pas). 🔄",
"Les colles arrivent, es-tu prêt(e) ? Spoiler : non. 😬",
"Tu sais combien il reste de jours ? C'est terrifiant, non ? ⏰",
"Pendant que tu lis ce message, 3 exos auraient pu être pliés. 🤷",
"La motivation c'est pour les faibles, la discipline c'est pour les forts. 💼",
"Tu te rappelles pourquoi tu es en prépa ? Prouve-le ! 🎯",
"Les profs ont raison : tu gâches ton potentiel là. 📚",
"C'est pas avec des 'demain je m'y mets' que tu vas intégrer Centrale. 🚫",
// --- Les Nouveaux (Spécial "Violence") ---
"Tu vises l'ENS ou l'E3A là ? Ton attitude me met le doute. 🤨",
"Ton major de promo a déjà fini le sujet de 2018, lui. 🏎️",
"C'est pas en regardant le plafond que le théorème va rentrer. 🧱",
"Si tu ne bosses pas maintenant, prépare ton dossier Parcoursup. 📂",
"Dors ou travaille. Tout le reste est une perte de temps. 💤",
"Arrête de te mentir, tu n'es pas en 'pause', tu procrastines. 🤥",
"L'examinateur s'en fiche que tu sois fatigué. Il veut le résultat. 👹",
"Tu crois que Cauchy a procrastiné pour ses suites ? Non. 🔢",
"Ta mention au bac ne te sauvera pas aux concours. 🔥",
"La 5/2, c'est bien, mais intégrer en 3/2 c'est mieux. 🦄",
"Imagine ta tête devant le sujet si tu continues comme ça. 😱",
"Ton téléphone est ton pire ennemi. Éteins-le. 📵",
"Moins de story, plus de théorie. 📸",
"Tu veux finir ingénieur ou touriste ? 👷",
"Les Mines de Paris ne recrutent pas sur Candy Crush. 🍬",
"C'est le moment de souffrir pour ne pas pleurer en juillet. 😭",
"Tu connais tes formules de trigo ? J'en doute. Vérifie. 📐",
"Le jury ne fera pas de cadeau. Pourquoi tu t'en fais ? 🎁",
"Tu as vu le sujet de l'an dernier ? T'es pas prêt. 📉",
"Arrête de rêver ta vie, va étudier ta physique. ⚛️",
"La chimie orga ne s'apprend pas par osmose. 🧪",
"Chaque heure de perdue est une victoire pour ton concurrent direct. 🤺",
"C'est le moment de passer en mode machine. 🤖",
"Tu te reposeras après les écrits. Pour l'instant, au charbon. ⛏️",
"Si c'était facile, tout le monde serait à Polytechnique. 🏰",
"Ton excuse pour ne pas bosser est nulle. Retourne travailler. 🚮",
"L'avenir appartient à ceux qui se lèvent tôt (et qui font des maths). 🌅",
"Tu préfères la douleur de l'effort ou la douleur de l'échec ? 🎭",
"C'est pas le moment de flancher, c'est le money time ! 💰",
"Regarde tes notes de la dernière khôlle. Ça te suffit ? 📉",
"Tu n'es pas fatigué, tu es juste démotivé. Change ça. 🔋",
"La chance ne sourit qu'aux esprits bien préparés. 🍀",
"Tu veux voir tes amis intégrer pendant que tu redoubles ? 👯",
"Fais-le pour humilier ceux qui n'ont pas cru en toi. 😈",
"Pas de bras, pas de chocolat. Pas de travail, pas d'école. 🍫",
"Ton avenir se joue dans les 30 prochaines minutes. ⏳",
"L'excellence est une habitude, pas un acte isolé. 🏆",
"Arrête de chouiner et va faire un DL. 🔢",
"Tu as déjà oublié ton cours d'hier ? C'est grave. 🧠",
"Le concours blanc approche. La panique aussi. 🚨",
"Tu penses que tu as le niveau ? Prouve-le sur une feuille blanche. 📄",
"C'est pas le moment de faire du social. C'est la guerre. ⚔️",
"Tu veux une médaille ? Va la chercher avec tes dents. 🏅",
"La prépa c'est la jungle. Mange ou sois mangé. 🦁",
"Ton lit est confortable, mais le chômage l'est moins. 🛋️",
"Si tu lis ça, c'est que tu ne bosses pas. Allez, oust ! 👉",
"Tu as 5 minutes pour t'y mettre, sinon c'est mort pour aujourd'hui. ⏲️",
"Ne sois pas celui qui dit 'j'aurais dû'. Sois celui qui l'a fait. ✅",
"La physique quantique t'attend. Elle n'est pas patiente. 🌌",
"Arrête de scroller, ça ne va pas augmenter ton admissibilité. 🛑",
];
export default function StressPrepaPage() {
const [timeLeft, setTimeLeft] = useState({
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
});
const [progress, setProgress] = useState(0);
const [messageMode, setMessageMode] = useState<
"encouraging" | "stressful" | "none"
>("none");
const [messages, setMessages] = useState<string[]>([]);
useEffect(() => {
const examDate = new Date("2026-04-13T07:00:00");
const schoolYearStart = new Date("2025-09-01T00:00:00");
const storedMode = localStorage.getItem("stress-prepa-message-mode");
if (
storedMode === "encouraging" ||
storedMode === "stressful" ||
storedMode === "none"
) {
setMessageMode(storedMode);
}
const updateCountdown = () => {
const now = new Date();
const difference = examDate.getTime() - now.getTime();
if (difference > 0) {
const days = Math.floor(difference / (1000 * 60 * 60 * 24));
const hours = Math.floor((difference / (1000 * 60 * 60)) % 24);
const minutes = Math.floor((difference / 1000 / 60) % 60);
const seconds = Math.floor((difference / 1000) % 60);
setTimeLeft({ days, hours, minutes, seconds });
// Calculate progress percentage
const totalDuration = examDate.getTime() - schoolYearStart.getTime();
const elapsed = now.getTime() - schoolYearStart.getTime();
const progressPercent = Math.min(
Math.max((elapsed / totalDuration) * 100, 0),
100
);
setProgress(progressPercent);
} else {
setTimeLeft({ days: 0, hours: 0, minutes: 0, seconds: 0 });
setProgress(100);
}
};
updateCountdown();
const interval = setInterval(updateCountdown, 1000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
if (messageMode === "none") {
setMessages([]);
return;
}
const messages =
messageMode === "encouraging" ? ENCOURAGING_MESSAGES : STRESSFUL_MESSAGES;
setMessages(messages);
}, [messageMode]);
const formatNumber = (num: number) => num.toString().padStart(2, "0");
return (
<div className="relative min-h-full flex flex-col items-center justify-center bg-background px-4 overflow-hidden">
{/* Animated gradient background */}
<div className="absolute inset-0 bg-gradient-to-br from-background via-muted/20 to-background animate-gradient" />
{/* Enhanced grid pattern */}
<div className="absolute inset-0 opacity-[0.03]">
<div
className="absolute inset-0"
style={{
backgroundImage: `
linear-gradient(to right, currentColor 1px, transparent 1px),
linear-gradient(to bottom, currentColor 1px, transparent 1px)
`,
backgroundSize: "64px 64px",
}}
/>
</div>
<Dialog>
<DialogTrigger asChild>
<Button
variant="outline"
size="icon"
className="absolute z-100 top-4 right-4 rounded-full bg-card/80 backdrop-blur-sm hover:bg-card border-border/50 shadow-lg"
>
<Info className="h-4 w-4" />
<span className="sr-only">Informations</span>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="text-2xl font-bold">
Stress Prépa
</DialogTitle>
<DialogContent className="space-y-4 pt-4 text-base">
<div className="space-y-2">
<p className="text-foreground/90">
Compte à rebours jusqu'aux concours
</p>
<div className="h-px bg-border/50" />
</div>
<div className="space-y-3 text-sm">
<div className="flex justify-between items-center">
<span className="text-muted-foreground">
Début des concours
</span>
<span className="font-mono font-semibold text-foreground">
13 Avril 2026
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-muted-foreground">
Début de l'année
</span>
<span className="font-mono font-semibold text-foreground">
1er Sept. 2025
</span>
</div>
</div>
<div className="pt-4 space-y-4">
<div className="h-px bg-border/50" />
<div className="space-y-3">
<Label className="text-sm font-semibold text-foreground">
Mode des messages
</Label>
<RadioGroup
value={messageMode}
onValueChange={(value) => {
localStorage.setItem("stress-prepa-message-mode", value);
setMessageMode(
value as "encouraging" | "stressful" | "none"
);
}}
className="space-y-0"
>
<div className="flex items-center space-x-3 px-3 pt-3 rounded-lg hover:bg-muted/50 transition-colors">
<RadioGroupItem value="none" id="none" />
<Label
htmlFor="none"
className="font-normal cursor-pointer flex-1"
>
Aucun message 🤐
</Label>
</div>
<div className="flex items-center space-x-3 px-3 py-1 rounded-lg hover:bg-muted/50 transition-colors">
<RadioGroupItem value="encouraging" id="encouraging" />
<Label
htmlFor="encouraging"
className="font-normal cursor-pointer flex-1"
>
Encouragement 🙌
</Label>
</div>
<div className="flex items-center space-x-3 px-3 rounded-lg hover:bg-muted/50 transition-colors">
<RadioGroupItem value="stressful" id="stressful" />
<Label
htmlFor="stressful"
className="font-normal cursor-pointer flex-1"
>
Trash talk 🤡
</Label>
</div>
</RadioGroup>
</div>
</div>
<div className="pt-4">
<div className="h-px bg-border/50 mb-4" />
<p className="text-xs text-muted-foreground">
Tous droits réservés Khollisé © {new Date().getFullYear()}
Messages générés avec l'intelligence artificielle.
</p>
</div>
</DialogContent>
</DialogHeader>
</DialogContent>
</Dialog>
<div className="relative w-full max-w-6xl mx-auto text-center space-y-16">
{/* Enhanced title section */}
<div className="space-y-3">
<h1 className="text-5xl md:text-7xl font-bold tracking-tight text-balance leading-tight">
Temps avant les concours
</h1>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 md:gap-4">
<div className="group bg-card border-2 border-border/50 rounded-2xl p-6 md:p-10 transition-all hover:border-border hover:shadow-xl hover:scale-[1.02]">
<div className="text-6xl md:text-8xl font-bold tabular-nums tracking-tight leading-none">
{timeLeft.days}
</div>
<div className="text-xs md:text-sm text-muted-foreground mt-3 font-medium uppercase tracking-wider">
Jour{timeLeft.days > 1 ? "s" : ""}
</div>
</div>
<div className="group bg-card border-2 border-border/50 rounded-2xl p-6 md:p-10 transition-all hover:border-border hover:shadow-xl hover:scale-[1.02]">
<div className="text-6xl md:text-8xl font-bold tabular-nums tracking-tight leading-none">
{formatNumber(timeLeft.hours)}
</div>
<div className="text-xs md:text-sm text-muted-foreground mt-3 font-medium uppercase tracking-wider">
Heure{timeLeft.hours > 1 ? "s" : ""}
</div>
</div>
<div className="group bg-card border-2 border-border/50 rounded-2xl p-6 md:p-10 transition-all hover:border-border hover:shadow-xl hover:scale-[1.02]">
<div className="text-6xl md:text-8xl font-bold tabular-nums tracking-tight leading-none">
{formatNumber(timeLeft.minutes)}
</div>
<div className="text-xs md:text-sm text-muted-foreground mt-3 font-medium uppercase tracking-wider">
Minute{timeLeft.minutes > 1 ? "s" : ""}
</div>
</div>
<div className="group bg-card border-2 border-border/50 rounded-2xl p-6 md:p-10 transition-all hover:border-border hover:shadow-xl hover:scale-[1.02]">
<div className="text-6xl md:text-8xl font-bold tabular-nums tracking-tight leading-none">
{formatNumber(timeLeft.seconds)}
</div>
<div className="text-xs md:text-sm text-muted-foreground mt-3 font-medium uppercase tracking-wider">
Seconde{timeLeft.seconds > 1 ? "s" : ""}
</div>
</div>
</div>
<div className="min-h-10 flex items-center justify-center px-4">
{messages.length > 0 && (
<TypingText
text={messages}
duration={25}
holdDelay={5000}
className="text-lg md:text-xl text-muted-foreground/90 italic max-w-3xl leading-relaxed"
>
<TypingTextCursor className="!h-6 !w-1 rounded-full ml-1 -mb-1" />
</TypingText>
)}
</div>
<div className="space-y-4 max-w-3xl mx-auto">
<div className="relative w-full h-4 bg-muted/50 rounded-full overflow-hidden border border-border/30 shadow-inner">
<div
className="absolute inset-y-0 left-0 bg-primary rounded-full transition-all duration-1000 ease-out shadow-lg"
style={{ width: `${progress}%` }}
/>
</div>
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">
Progression de l'année
</span>
<span className="font-mono font-bold text-lg text-foreground tabular-nums">
{progress.toFixed(2)}%
</span>
</div>
</div>
</div>
</div>
);
}