TypeScript 98.3%
CSS 0.9%
Shell 0.7%
1// Page d'accueil — plateforme pédagogique de cours (IMM1003 · IMM1033, UQO).2// Ton institutionnel et transparent : ce n'est pas un produit commercial.3import Image from "next/image";4import Link from "next/link";5import { redirect } from "next/navigation";6import { DemoButton } from "@/components/demo-button";7import {8 ArrowRight, BadgeCheck, BookOpen, BookOpenCheck, BrainCircuit, FileSearch,9 GraduationCap, Layers, LineChart, Lock, MessageSquareText, Network,10 Presentation, ShieldCheck, Timer,11} from "lucide-react";12import { ImmbotLogo, ImmbotMark, UqoLogo } from "@/components/logo";13import { ThemeToggle } from "@/components/theme-toggle";14import { currentUser } from "@/lib/auth/session.ts";15import { get } from "@/lib/db/index.ts";1617export default async function HomePage() {18 const user = await currentUser();19 if (user) redirect("/chat");2021 const chunks = get<{ n: number }>("SELECT COUNT(*) as n FROM chunks WHERE space LIKE 'official-%'")?.n ?? 2000;22 const questions = get<{ n: number }>("SELECT COUNT(*) as n FROM quiz_questions")?.n ?? 129;23 const cards = get<{ n: number }>("SELECT COUNT(*) as n FROM flashcards")?.n ?? 140;24 const exams = get<{ n: number }>("SELECT COUNT(*) as n FROM mock_exams")?.n ?? 6;2526 return (27 <div className="min-h-dvh bg-app">28 {/* Filet institutionnel */}29 <div className="h-1 bg-brand-700" aria-hidden>30 <div className="h-full w-24 bg-gold-500 ml-4 sm:ml-8" />31 </div>3233 <header className="sticky top-0 z-40 bg-card/95 backdrop-blur border-b border-app">34 <div className="max-w-6xl mx-auto px-4 sm:px-6 h-15 sm:h-16 flex items-center justify-between gap-3">35 <div className="flex items-center gap-3 sm:gap-4 min-w-0">36 <ImmbotLogo size={28} />37 <span className="hidden sm:block w-px h-7" style={{ background: "var(--border)" }} aria-hidden />38 <UqoLogo height={30} className="hidden sm:block dark:invert" />39 </div>40 <nav className="flex items-center gap-1.5 sm:gap-2 shrink-0">41 <ThemeToggle className="hidden sm:block" />42 <Link href="/connexion" className="px-3 sm:px-4 py-2 text-[13.5px] sm:text-sm font-semibold text-fg hover:bg-surface-2 dark:hover:bg-brand-900/40 rounded-lg transition-colors">43 Connexion44 </Link>45 <Link href="/inscription" className="px-3 sm:px-4 py-2 text-[13.5px] sm:text-sm font-semibold bg-brand-700 hover:bg-brand-800 text-white rounded-lg transition-colors">46 Créer un compte47 </Link>48 </nav>49 </div>50 {/* Bandeau de divulgation */}51 <div className="bg-brand-700 text-white">52 <p className="max-w-6xl mx-auto px-4 sm:px-6 py-1.5 text-[11.5px] sm:text-[12.5px] font-medium text-center sm:text-left">53 Plateforme pédagogique réservée aux personnes inscrites à <strong>IMM1003</strong> et{" "}54 <strong>IMM1033</strong> — Automne 2026 · Fournie par le professeur Simon-Pierre Boucher, UQO55 </p>56 </div>57 </header>5859 <main>60 {/* ================= Héro ================= */}61 <section className="relative overflow-hidden">62 {/* Quadrillage discret */}63 <div64 aria-hidden65 className="absolute inset-0 opacity-[0.5] dark:opacity-[0.12]"66 style={{67 backgroundImage:68 "linear-gradient(var(--border) 1px, transparent 1px), linear-gradient(90deg, var(--border) 1px, transparent 1px)",69 backgroundSize: "44px 44px",70 maskImage: "radial-gradient(ellipse 90% 65% at 50% 0%, black 35%, transparent 78%)",71 WebkitMaskImage: "radial-gradient(ellipse 90% 65% at 50% 0%, black 35%, transparent 78%)",72 }}73 />74 <div className="relative max-w-6xl mx-auto px-4 sm:px-6 pt-10 sm:pt-16 pb-12 sm:pb-16 grid lg:grid-cols-2 gap-10 lg:gap-12 items-center">75 <div>76 <p className="inline-flex items-center gap-2 text-[12px] sm:text-[13px] font-semibold text-brand-700 dark:text-brand-300 bg-brand-100 dark:bg-brand-900/50 border border-brand-200 dark:border-brand-800 px-3.5 py-1.5 rounded-full animate-fade-up">77 <GraduationCap size={14} /> L'outil d'étude officiel de vos cours78 </p>79 <h1 className="mt-5 text-[2.1rem] leading-[1.1] sm:text-[3.4rem] font-bold tracking-tight text-fg animate-fade-up [animation-delay:60ms] text-balance">80 Étudiez IMM1003 et IMM1033 avec un assistant qui connaît{" "}81 <span className="relative whitespace-nowrap">82 <span className="text-brand-600 dark:text-brand-300">vos diapositives</span>83 <svg className="absolute -bottom-1.5 left-0 w-full" height="6" viewBox="0 0 200 6" preserveAspectRatio="none" aria-hidden>84 <path d="M0 4 Q 50 0.5 100 3 T 200 2.5" stroke="#C6A300" strokeWidth="2.5" fill="none" strokeLinecap="round" />85 </svg>86 </span>87 .88 </h1>89 <p className="mt-5 text-[15px] sm:text-[17px] text-muted leading-relaxed animate-fade-up [animation-delay:120ms] text-pretty max-w-xl">90 Mise en place par votre professeur, cette plateforme est construite sur le{" "}91 <strong className="text-fg font-semibold">matériel officiel des deux cours</strong> — chaque92 réponse cite la séance et la diapositive exactes, que vous pouvez vérifier d'un clic.93 </p>94 <ul className="mt-5 space-y-2 animate-fade-up [animation-delay:160ms]">95 {[96 "Fondé uniquement sur le contenu du cours (mode par défaut)",97 "Vos conversations et votre progression restent privées",98 "Gratuit pour les personnes inscrites — aucun objectif commercial",99 ].map((t) => (100 <li key={t} className="flex items-start gap-2.5 text-[13.5px] sm:text-[14.5px] text-fg">101 <BadgeCheck size={17} className="text-brand-600 dark:text-brand-300 shrink-0 mt-0.5" />102 {t}103 </li>104 ))}105 </ul>106 <div className="mt-7 flex flex-col sm:flex-row sm:items-center gap-2.5 animate-fade-up [animation-delay:200ms]">107 <Link href="/inscription" className="inline-flex items-center justify-center gap-2 px-6 py-3 bg-brand-700 hover:bg-brand-800 text-white font-semibold rounded-xl shadow-md shadow-brand-700/20 transition-colors">108 Créer mon compte étudiant <ArrowRight size={16} />109 </Link>110 <Link href="/connexion" className="inline-flex items-center justify-center px-6 py-3 bg-card border border-app hover:border-brand-400 text-fg font-semibold rounded-xl transition-colors">111 J'ai déjà un compte112 </Link>113 </div>114 <div className="mt-3 animate-fade-up [animation-delay:240ms]">115 <DemoButton />116 <p className="text-[11.5px] text-muted mt-1.5">117 Compte d'essai partagé avec accès aux deux cours — vos vraies données commencent avec votre compte personnel.118 </p>119 </div>120 </div>121122 {/* Aperçu du chat */}123 <div className="animate-fade-up [animation-delay:240ms]">124 <div className="bg-card border border-app rounded-xl shadow-xl overflow-hidden">125 <div className="px-4 py-2.5 border-b border-app flex items-center gap-2.5 bg-surface-1 dark:bg-brand-950/60">126 <ImmbotMark size={20} />127 <span className="text-[12.5px] font-semibold text-fg">IMM1033 · Cours interactif</span>128 <span className="ml-auto text-[11px] text-muted font-medium">Mode par défaut : matériel officiel</span>129 </div>130 <div className="p-4 sm:p-5 space-y-3.5 text-left">131 <div className="flex justify-end">132 <div className="bg-brand-700 text-white rounded-2xl rounded-br-md px-4 py-2.5 max-w-[88%] text-[13px] sm:text-[13.5px]">133 Que dit la séance 10 sur la dépréciation incurable de longue durée ?134 </div>135 </div>136 <div className="flex flex-wrap gap-1.5">137 <span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg border border-app bg-surface-1 dark:bg-brand-950/40 text-[11.5px] font-medium text-muted">138 <BookOpen size={11} /> Parcourt le plan de la séance 10139 </span>140 <span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg border border-app bg-surface-1 dark:bg-brand-950/40 text-[11.5px] font-medium text-muted">141 <Presentation size={11} /> Lit les diapositives 23–27142 </span>143 </div>144 <div className="flex gap-2.5">145 <ImmbotMark size={22} className="shrink-0 mt-1" />146 <div className="bg-surface-1 dark:bg-brand-900/40 border border-app rounded-2xl rounded-bl-md px-4 py-3 text-[13px] sm:text-[13.5px] text-fg leading-relaxed">147 La dépréciation physique incurable de longue durée touche les composantes qui durent148 aussi longtemps que le bâtiment <span className="citation-chip">S1</span>. On l'estime149 par la méthode <strong>âge-vie</strong> appliquée à la base résiduelle — après avoir150 retiré le curable et le court terme pour éviter le{" "}151 <strong>double comptage</strong> <span className="citation-chip">S3</span>.152 <span className="block mt-2.5 pt-2 border-t border-app text-[11px] text-muted">153 Sources : Séance 10 — Diapositives 23 et 25 · vérifiables d'un clic154 </span>155 </div>156 </div>157 </div>158 </div>159 <p className="text-center text-[11.5px] text-muted mt-2">160 Réponse réelle du mode « Cours interactif » : l'assistant consulte les diapositives sous vos yeux.161 </p>162 </div>163 </div>164 </section>165166 {/* ================= Matériel indexé ================= */}167 <section className="border-y border-app bg-card">168 <div className="max-w-6xl mx-auto px-4 sm:px-6 py-8 sm:py-10">169 <p className="text-center text-[12px] font-bold uppercase tracking-widest text-muted">170 Le matériel de vos deux cours, indexé à la diapositive près171 </p>172 <div className="mt-5 grid grid-cols-2 sm:grid-cols-4 gap-px rounded-xl overflow-hidden border border-app" style={{ background: "var(--border)" }}>173 {[174 [chunks.toLocaleString("fr-CA"), "extraits du matériel officiel"],175 ["28", "séances consultables en ligne"],176 [`${questions} + ${cards}`, "questions et cartes mémoire"],177 [String(exams), "examens blancs corrigés"],178 ].map(([n, label]) => (179 <div key={label} className="bg-card px-4 py-4 sm:py-5 text-center">180 <p className="text-xl sm:text-2xl font-bold text-brand-700 dark:text-brand-300 tracking-tight">{n}</p>181 <p className="text-[11.5px] sm:text-[12.5px] text-muted mt-0.5">{label}</p>182 </div>183 ))}184 </div>185 </div>186 </section>187188 {/* ================= Trois façons d'étudier ================= */}189 <section className="max-w-6xl mx-auto px-4 sm:px-6 py-14 sm:py-20">190 <h2 className="text-xl sm:text-3xl font-bold tracking-tight text-center text-fg">Trois façons d'étudier</h2>191 <p className="text-center text-[13.5px] sm:text-base text-muted mt-2.5 max-w-xl mx-auto">192 Chaque module applique un principe validé par la science de l'apprentissage — pas de gadgets.193 </p>194 <div className="mt-9 sm:mt-12 grid md:grid-cols-3 gap-4 sm:gap-5">195 {PILLARS.map((p, i) => (196 <div key={p.title} className="relative bg-card border border-app rounded-xl p-5 sm:p-6 overflow-hidden">197 <div className="absolute inset-x-0 top-0 h-1" style={{ background: i === 1 ? "#C6A300" : "var(--color-brand-700, #003E7E)" }} />198 <div className="flex items-center gap-3">199 <div className="w-10 h-10 rounded-lg bg-brand-700 text-white flex items-center justify-center shrink-0">200 <p.icon size={19} />201 </div>202 <div>203 <p className="text-[11px] font-bold uppercase tracking-widest text-muted">{p.eyebrow}</p>204 <h3 className="font-bold text-[16px] text-fg tracking-tight">{p.title}</h3>205 </div>206 </div>207 <ul className="mt-4 space-y-2.5">208 {p.items.map((it) => (209 <li key={it.label} className="flex items-start gap-2.5">210 <it.icon size={15} className="text-brand-600 dark:text-brand-300 shrink-0 mt-0.5" />211 <span className="text-[13px] text-fg leading-snug">212 <strong className="font-semibold">{it.label}</strong>213 <span className="text-muted"> — {it.text}</span>214 </span>215 </li>216 ))}217 </ul>218 </div>219 ))}220 </div>221 </section>222223 {/* ================= La plateforme en images ================= */}224 <section className="border-t border-app bg-card">225 <div className="max-w-6xl mx-auto px-4 sm:px-6 py-14 sm:py-20">226 <h2 className="text-xl sm:text-3xl font-bold tracking-tight text-center text-fg">La plateforme en images</h2>227 <p className="text-center text-[13.5px] sm:text-base text-muted mt-2.5 max-w-xl mx-auto">228 Captures réelles de l'application — exactement ce que vous verrez une fois connecté·e.229 </p>230 <div className="mt-9 grid md:grid-cols-2 gap-5 sm:gap-7">231 {SCREENS.map((s) => (232 <figure key={s.src} className="group">233 <div className="rounded-xl border border-app bg-app shadow-lg overflow-hidden transition-transform duration-300 group-hover:-translate-y-0.5 group-hover:shadow-xl">234 <div className="flex items-center gap-1.5 px-3.5 py-2 bg-surface-2 dark:bg-brand-950/60 border-b border-app">235 <span className="w-2.5 h-2.5 rounded-full bg-red-400/80" aria-hidden />236 <span className="w-2.5 h-2.5 rounded-full bg-amber-400/80" aria-hidden />237 <span className="w-2.5 h-2.5 rounded-full bg-emerald-400/80" aria-hidden />238 <span className="ml-2 text-[11px] font-medium text-muted truncate">{s.url}</span>239 </div>240 <Image241 src={s.src}242 alt={s.alt}243 width={1280}244 height={800}245 className="w-full h-auto"246 sizes="(max-width: 768px) 100vw, 560px"247 />248 </div>249 <figcaption className="mt-2.5 px-1">250 <p className="text-[13.5px] font-semibold text-fg">{s.title}</p>251 <p className="text-[12.5px] text-muted mt-0.5">{s.caption}</p>252 </figcaption>253 </figure>254 ))}255 </div>256 </div>257 </section>258259 {/* ================= Les deux cours ================= */}260 <section className="border-t border-app bg-card">261 <div className="max-w-6xl mx-auto px-4 sm:px-6 py-14 sm:py-18">262 <div className="grid md:grid-cols-2 gap-4">263 <div className="relative border border-app rounded-xl p-5 sm:p-7 bg-app overflow-hidden">264 <div className="absolute inset-x-0 top-0 h-1 bg-brand-700" />265 <p className="text-[12px] font-bold text-brand-700 dark:text-brand-300 tracking-widest">IMM1003 · AUTOMNE 2026</p>266 <h3 className="text-lg sm:text-xl font-bold tracking-tight text-fg mt-1.5">Éléments d'évaluation immobilière</h3>267 <p className="text-[13px] sm:text-sm text-muted mt-2.5 leading-relaxed">268 Cadre professionnel OEAQ, principes économiques, types de valeur, marché québécois et les269 trois méthodes reconnues — jusqu'au rapport d'évaluation conforme.270 </p>271 </div>272 <div className="relative border border-app rounded-xl p-5 sm:p-7 bg-app overflow-hidden">273 <div className="absolute inset-x-0 top-0 h-1 bg-gold-500" />274 <p className="text-[12px] font-bold text-gold-600 dark:text-gold-400 tracking-widest">IMM1033 · AUTOMNE 2026</p>275 <h3 className="text-lg sm:text-xl font-bold tracking-tight text-fg mt-1.5">Méthodes du coût en évaluation immobilière</h3>276 <p className="text-[13px] sm:text-sm text-muted mt-2.5 leading-relaxed">277 Évaluation du terrain, coûts directs et indirects, profit de l'entrepreneur et la mesure278 rigoureuse des trois formes de dépréciation — jusqu'au cas intégrateur complet.279 </p>280 </div>281 </div>282 <p className="text-center text-[12.5px] text-muted mt-5">283 Professeur : <strong className="text-fg font-semibold">Simon-Pierre Boucher</strong> — Département284 des sciences administratives, Université du Québec en Outaouais285 </p>286 </div>287 </section>288289 {/* ================= Un outil pédagogique, pas un raccourci ================= */}290 <section className="max-w-4xl mx-auto px-4 sm:px-6 py-14 sm:py-20">291 <h2 className="text-xl sm:text-3xl font-bold tracking-tight text-center text-fg">292 Un outil pédagogique — pas un raccourci293 </h2>294 <div className="mt-8 grid sm:grid-cols-3 gap-4">295 {[296 {297 icon: ShieldCheck,298 title: "Transparent",299 text: "L'origine de chaque information est identifiée : matériel officiel (cité par diapositive), vos documents, Web ou connaissances du modèle. Quand le cours ne couvre pas une question, l'assistant le dit.",300 },301 {302 icon: BookOpenCheck,303 title: "Honnête avec vos travaux",304 text: "Pour un atelier noté, l'assistant vous demande d'abord votre tentative et guide par indices — il est là pour vous préparer à l'examen, pas pour faire le travail à votre place.",305 },306 {307 icon: Lock,308 title: "Privé",309 text: "Vos conversations et votre progression individuelle ne sont jamais montrées au professeur — il ne voit que des statistiques agrégées et anonymisées. Aucune donnée n'est vendue ni utilisée à d'autres fins.",310 },311 ].map((c) => (312 <div key={c.title} className="text-center px-2">313 <c.icon className="mx-auto text-brand-600 dark:text-brand-300" size={26} />314 <h3 className="font-bold text-[15px] text-fg mt-3 tracking-tight">{c.title}</h3>315 <p className="text-[12.5px] text-muted mt-2 leading-relaxed">{c.text}</p>316 </div>317 ))}318 </div>319 <div className="mt-10 text-center">320 <Link href="/inscription" className="inline-flex items-center gap-2 px-6 py-3 bg-brand-700 hover:bg-brand-800 text-white font-semibold rounded-xl transition-colors">321 Accéder à la plateforme <ArrowRight size={16} />322 </Link>323 <p className="text-[12px] text-muted mt-3">324 Réservé aux personnes inscrites aux cours. L'assistant peut se tromper : vérifiez les sources citées.325 </p>326 </div>327 </section>328 </main>329330 <footer className="border-t border-app bg-card">331 <div className="max-w-6xl mx-auto px-4 sm:px-6 py-8 flex flex-col sm:flex-row items-center sm:items-start justify-between gap-5">332 <div className="flex flex-col items-center sm:items-start gap-3 max-w-md text-center sm:text-left">333 <div className="flex items-center gap-4">334 <ImmbotLogo size={24} />335 <span className="w-px h-6" style={{ background: "var(--border)" }} aria-hidden />336 <UqoLogo height={26} className="dark:invert" />337 </div>338 <p className="text-[11.5px] text-muted leading-relaxed">339 Immbot AI est un outil pédagogique fourni gratuitement aux étudiantes et étudiants340 d'IMM1003 et IMM1033 par le professeur Simon-Pierre Boucher (UQO). Il ne remplace ni les341 séances, ni le matériel officiel, ni le plan de cours.342 </p>343 </div>344 <div className="text-[11.5px] text-muted text-center sm:text-right space-y-1 shrink-0">345 <p>IMM1003 · IMM1033 — Automne 2026</p>346 <p>Université du Québec en Outaouais</p>347 <p>simon-pierre.boucher@uqo.ca</p>348 <p className="pt-1.5 space-x-3">349 <Link href="/conditions" className="font-semibold text-fg hover:underline">Conditions d'utilisation</Link>350 <Link href="/confidentialite" className="font-semibold text-fg hover:underline">Confidentialité</Link>351 </p>352 </div>353 </div>354 </footer>355 </div>356 );357}358359const SCREENS = [360 {361 src: "/screens/chat.png",362 url: "immbot.ai/chat",363 alt: "Le chat d'Immbot AI répond avec des citations vérifiables et montre ses consultations du cours",364 title: "Le chat qui montre ses sources",365 caption: "L'assistant consulte le plan et lit les diapositives sous vos yeux, puis cite chaque affirmation — cliquez S1 pour voir l'extrait original.",366 },367 {368 src: "/screens/diapositives.png",369 url: "immbot.ai/apprendre/imm1033/diapositives",370 alt: "La visionneuse de diapositives avec sommaire et encadrés colorés",371 title: "Les 28 séances consultables en ligne",372 caption: "Navigation au clavier, recherche dans la séance, définitions et formules en encadrés, PDF original téléchargeable.",373 },374 {375 src: "/screens/progression.png",376 url: "immbot.ai/apprendre/imm1003",377 alt: "Le tableau de progression avec maîtrise estimée, série de jours et statistiques",378 title: "Votre progression, honnêtement",379 caption: "Maîtrise estimée par notion, série de jours d'étude, cartes dues et recommandations justifiées — visible par vous seul·e.",380 },381 {382 src: "/screens/concepts.png",383 url: "immbot.ai/apprendre/imm1003/concepts",384 alt: "La carte interactive des concepts du cours, colorée selon la maîtrise",385 title: "La carte des concepts du cours",386 caption: "Les notions semaine par semaine avec leurs préalables, colorées selon votre maîtrise — un clic pour réviser, quizzer ou en discuter.",387 },388] as const;389390const PILLARS = [391 {392 eyebrow: "Comprendre",393 title: "Poser des questions au cours",394 icon: MessageSquareText,395 items: [396 { icon: FileSearch, label: "Citations exactes", text: "chaque réponse renvoie à la séance et à la diapositive, vérifiables d'un clic" },397 { icon: BrainCircuit, label: "Cours interactif", text: "l'assistant consulte le plan et lit les diapositives sous vos yeux" },398 { icon: Presentation, label: "Diapositives en ligne", text: "les 28 séances consultables avec recherche, sommaire et PDF original" },399 ],400 },401 {402 eyebrow: "Pratiquer",403 title: "Se tester régulièrement",404 icon: BookOpenCheck,405 items: [406 { icon: Layers, label: "Cartes mémoire", text: "répétition espacée pour retenir formules et taxonomies durablement" },407 { icon: BookOpenCheck, label: "Quiz adaptatifs", text: "la difficulté suit vos réponses et cible vos faiblesses" },408 { icon: Timer, label: "Examens blancs", text: "intra et final simulés, chronométrés, corrigés par compétence" },409 ],410 },411 {412 eyebrow: "Progresser",413 title: "Savoir où vous en êtes",414 icon: LineChart,415 items: [416 { icon: Network, label: "Carte des concepts", text: "les notions du cours et leurs préalables, colorées selon votre maîtrise" },417 { icon: LineChart, label: "Progression honnête", text: "une estimation par notion, fondée sur la difficulté et la récence" },418 { icon: GraduationCap, label: "Plan d'étude", text: "généré depuis votre date d'examen et vos disponibilités, il s'adapte" },419 ],420 },421] as const;422