From e0ba4c2e23a9073c687e58546702d750315f22ac Mon Sep 17 00:00:00 2001 From: AyoubBenziza Date: Mon, 15 Jun 2026 09:17:52 +0200 Subject: [PATCH 1/2] feat(web): "What's new" patch notes modal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - curated, bilingual player-facing notes (lib/patch-notes.ts) — a public subset of CHANGELOG.md (which stays the technical source of truth) - "Nouveautés" button on home with a dot badge when the app version changed since last seen (localStorage); opening marks the version seen - app version injected at build via Vite define (__APP_VERSION__) - add shadcn Dialog component (@workspace/ui, via radix-ui) Verified: notes + version baked into the build; typecheck/lint/build green. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/web/src/components/whats-new.tsx | 79 ++++++++++++++++++++++ apps/web/src/i18n/en.ts | 4 ++ apps/web/src/i18n/fr.ts | 4 ++ apps/web/src/lib/patch-notes.ts | 50 ++++++++++++++ apps/web/src/pages/home.tsx | 2 + apps/web/src/types/globals.d.ts | 2 + apps/web/vite.config.ts | 8 +++ packages/ui/src/components/dialog.tsx | 94 +++++++++++++++++++++++++++ 8 files changed, 243 insertions(+) create mode 100644 apps/web/src/components/whats-new.tsx create mode 100644 apps/web/src/lib/patch-notes.ts create mode 100644 apps/web/src/types/globals.d.ts create mode 100644 packages/ui/src/components/dialog.tsx diff --git a/apps/web/src/components/whats-new.tsx b/apps/web/src/components/whats-new.tsx new file mode 100644 index 0000000..3534b24 --- /dev/null +++ b/apps/web/src/components/whats-new.tsx @@ -0,0 +1,79 @@ +import { useState } from "react" +import { Sparkles } from "lucide-react" +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@workspace/ui/components/dialog" +import { PATCH_NOTES } from "@/lib/patch-notes" +import { useI18n } from "@/i18n/context" + +const SEEN_KEY = "nerdware-seen-version" + +/** Bouton « Nouveautés » + modale des notes de version (pastille si version vue ≠ actuelle). */ +export function WhatsNew({ className = "" }: { className?: string }) { + const { t, lang } = useI18n() + const [seen, setSeen] = useState(() => { + try { + return localStorage.getItem(SEEN_KEY) + } catch { + return null + } + }) + const hasNew = seen !== __APP_VERSION__ + + function markSeen() { + try { + localStorage.setItem(SEEN_KEY, __APP_VERSION__) + } catch { + // ignore + } + setSeen(__APP_VERSION__) + } + + return ( + { + if (open) { + markSeen() + } + }} + > + + + + + + {t.whatsNew.title} + +
+ {PATCH_NOTES.map((note) => ( +
+

+ v{note.version} + + {" "} + · {note.date} + +

+
    + {(lang === "fr" ? note.fr : note.en).map((line, i) => ( +
  • {line}
  • + ))} +
+
+ ))} +
+
+
+ ) +} diff --git a/apps/web/src/i18n/en.ts b/apps/web/src/i18n/en.ts index 8129c18..c9df5c1 100644 --- a/apps/web/src/i18n/en.ts +++ b/apps/web/src/i18n/en.ts @@ -10,6 +10,10 @@ export const en: Dict = { loading: "Loading…", error: "Error", }, + whatsNew: { + button: "What's new", + title: "What's new", + }, home: { tagline: "Geek culture party game", create: "Create a room", diff --git a/apps/web/src/i18n/fr.ts b/apps/web/src/i18n/fr.ts index c6edb91..48b7a56 100644 --- a/apps/web/src/i18n/fr.ts +++ b/apps/web/src/i18n/fr.ts @@ -8,6 +8,10 @@ export const fr = { loading: "Chargement…", error: "Erreur", }, + whatsNew: { + button: "Nouveautés", + title: "Nouveautés", + }, home: { tagline: "Party game culture geek", create: "Créer une room", diff --git a/apps/web/src/lib/patch-notes.ts b/apps/web/src/lib/patch-notes.ts new file mode 100644 index 0000000..f58a05d --- /dev/null +++ b/apps/web/src/lib/patch-notes.ts @@ -0,0 +1,50 @@ +// Notes de version côté joueur (curées, bilingues). Sous-ensemble grand public +// du CHANGELOG.md (qui reste la source de vérité technique). + +export interface PatchNote { + version: string + date: string + fr: string[] + en: string[] +} + +export const PATCH_NOTES: PatchNote[] = [ + { + version: "0.1.2", + date: "2026-06-12", + fr: ["Statistiques d'audience (Umami)."], + en: ["Audience analytics (Umami)."], + }, + { + version: "0.1.1", + date: "2026-06-12", + fr: [ + "Blindtest : bouton « Activer le son » et volume réglable.", + "Blindtest porté à 90 s ; le DJ peut désormais être n'importe qui.", + "« Qui l'a ajouté ? » : l'état de validation est caché (plus de triche).", + "Thème clair / sombre / système.", + "Décompte synchronisé sur tous les appareils.", + ], + en: [ + "Blindtest: “Enable sound” button and volume control.", + "Blindtest is now 90 s; the DJ can be anyone.", + "“Who added it?”: vote status is hidden (no more giveaway).", + "Light / dark / system theme.", + "Countdown synced across all devices.", + ], + }, + { + version: "0.1.0", + date: "2026-06-11", + fr: [ + "Première version : Quiz, Images, Blindtest et mode Mixte.", + "Reconnexion après refresh, bots, historique des parties.", + "Interface FR / EN.", + ], + en: [ + "First release: Quiz, Images, Blindtest and Mixed mode.", + "Reconnect after refresh, bots, game history.", + "FR / EN interface.", + ], + }, +] diff --git a/apps/web/src/pages/home.tsx b/apps/web/src/pages/home.tsx index a862dd9..8760e2f 100644 --- a/apps/web/src/pages/home.tsx +++ b/apps/web/src/pages/home.tsx @@ -8,6 +8,7 @@ import { useI18n } from "@/i18n/context" import { errorMessage } from "@/i18n/error" import { LanguageToggle } from "@/i18n/language-toggle" import { ThemeToggle } from "@/components/theme-toggle" +import { WhatsNew } from "@/components/whats-new" const inputClass = "border-input bg-background ring-offset-background focus-visible:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none disabled:opacity-50" @@ -62,6 +63,7 @@ export function HomePage() { return (
+
diff --git a/apps/web/src/types/globals.d.ts b/apps/web/src/types/globals.d.ts new file mode 100644 index 0000000..e69467a --- /dev/null +++ b/apps/web/src/types/globals.d.ts @@ -0,0 +1,2 @@ +// Version de l'app injectée par Vite (define), depuis apps/web/package.json. +declare const __APP_VERSION__: string diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 79a65e0..fe6ffe0 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -1,11 +1,19 @@ import path from "path" +import { readFileSync } from "node:fs" import tailwindcss from "@tailwindcss/vite" import react from "@vitejs/plugin-react" import { defineConfig } from "vite" +const pkg = JSON.parse( + readFileSync(path.resolve(__dirname, "package.json"), "utf-8") +) as { version: string } + // https://vite.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()], + define: { + __APP_VERSION__: JSON.stringify(pkg.version), + }, resolve: { alias: { "@": path.resolve(__dirname, "./src"), diff --git a/packages/ui/src/components/dialog.tsx b/packages/ui/src/components/dialog.tsx new file mode 100644 index 0000000..247a1f5 --- /dev/null +++ b/packages/ui/src/components/dialog.tsx @@ -0,0 +1,94 @@ +import * as React from "react" +import { Dialog as DialogPrimitive } from "radix-ui" +import { X } from "lucide-react" + +import { cn } from "@workspace/ui/lib/utils" + +function Dialog(props: React.ComponentProps) { + return +} + +function DialogTrigger( + props: React.ComponentProps +) { + return +} + +function DialogClose(props: React.ComponentProps) { + return +} + +function DialogContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + {children} + + + Close + + + + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Dialog, + DialogTrigger, + DialogClose, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} -- 2.45.3 From aae09961fa2c8749655ffb0c8774768b3013381f Mon Sep 17 00:00:00 2001 From: AyoubBenziza Date: Mon, 15 Jun 2026 09:20:44 +0200 Subject: [PATCH 2/2] chore(release): 0.1.3 "What's new" patch notes modal. See CHANGELOG. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 9 +++++++++ apps/server/package.json | 2 +- apps/web/package.json | 2 +- package.json | 2 +- packages/shared/package.json | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1917c63..152a1bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ Toutes les évolutions notables de NerdWare. Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/), versionné en [SemVer](https://semver.org/lang/fr/). +## [0.1.3] — 2026-06-12 + +### Ajouté + +- **Notes de version « Nouveautés »** : modale sur l'accueil listant les nouveautés + par version (curées, FR/EN), avec une pastille tant que la dernière version n'a + pas été vue. Composant `Dialog` réutilisable ajouté à `@workspace/ui`. + ## [0.1.2] — 2026-06-12 ### Ajouté @@ -63,6 +71,7 @@ fin), trois modes, back-office et internationalisation. - **Déploiement** : Dockerfiles (serveur Bun, client nginx) + `docker-compose.prod.yml` prêt pour Dokploy. +[0.1.3]: https://git.ayoubbenziza.dev/ayoub/nerdware/src/tag/v0.1.3 [0.1.2]: https://git.ayoubbenziza.dev/ayoub/nerdware/src/tag/v0.1.2 [0.1.1]: https://git.ayoubbenziza.dev/ayoub/nerdware/src/tag/v0.1.1 [0.1.0]: https://git.ayoubbenziza.dev/ayoub/nerdware/src/tag/v0.1.0 diff --git a/apps/server/package.json b/apps/server/package.json index 48b276a..78125b9 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "@nerdware/server", - "version": "0.1.2", + "version": "0.1.3", "type": "module", "private": true, "scripts": { diff --git a/apps/web/package.json b/apps/web/package.json index ad8ae69..d3c7f4c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "web", - "version": "0.1.2", + "version": "0.1.3", "type": "module", "private": true, "scripts": { diff --git a/package.json b/package.json index caa1f40..df9263a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nerdware", - "version": "0.1.2", + "version": "0.1.3", "private": true, "scripts": { "build": "turbo build", diff --git a/packages/shared/package.json b/packages/shared/package.json index 455310a..46508bc 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@nerdware/shared", - "version": "0.1.2", + "version": "0.1.3", "type": "module", "private": true, "scripts": { -- 2.45.3