create-next-pro-cli 0.1.26 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/README.md +281 -291
  2. package/create-next-pro-completion.sh +8 -38
  3. package/create-next-pro-completion.zsh +13 -0
  4. package/dist/bin.bun.js +2265 -794
  5. package/dist/bin.node.js +2425 -805
  6. package/dist/bin.node.js.map +1 -1
  7. package/package.json +50 -27
  8. package/templates/Projects/default/.env.example +17 -0
  9. package/templates/Projects/default/.github/workflows/quality.yml +24 -0
  10. package/templates/Projects/default/.gitignore.template +47 -0
  11. package/templates/Projects/default/.prettierignore +3 -0
  12. package/templates/Projects/default/README.md +52 -108
  13. package/templates/Projects/default/bun.lock +1152 -0
  14. package/templates/Projects/default/eslint.config.mjs +27 -11
  15. package/templates/Projects/default/messages/en/_global_ui.json +23 -10
  16. package/templates/Projects/default/messages/en.ts +17 -2
  17. package/templates/Projects/default/messages/fr/_global_ui.json +23 -10
  18. package/templates/Projects/default/messages/fr.ts +17 -2
  19. package/templates/Projects/default/next.config.ts +43 -3
  20. package/templates/Projects/default/package.json +42 -24
  21. package/templates/Projects/default/playwright.config.ts +26 -0
  22. package/templates/Projects/default/pnpm-workspace.yaml +5 -0
  23. package/templates/Projects/default/public/{cnp-logo.svg → logo.svg} +1 -1
  24. package/templates/Projects/default/src/app/[locale]/(public)/layout.tsx +8 -1
  25. package/templates/Projects/default/src/app/[locale]/(public)/login/page.tsx +8 -0
  26. package/templates/Projects/default/src/app/[locale]/(public)/register/page.tsx +8 -0
  27. package/templates/Projects/default/src/app/[locale]/(user)/dashboard/error.tsx +25 -0
  28. package/templates/Projects/default/src/app/[locale]/(user)/dashboard/loading.tsx +5 -0
  29. package/templates/Projects/default/src/app/[locale]/(user)/{Dashboard → dashboard}/page.tsx +1 -1
  30. package/templates/Projects/default/src/app/[locale]/(user)/layout.tsx +24 -2
  31. package/templates/Projects/default/src/app/[locale]/(user)/settings/loading.tsx +5 -0
  32. package/templates/Projects/default/src/app/[locale]/(user)/{Settings → settings}/page.tsx +1 -2
  33. package/templates/Projects/default/src/app/[locale]/(user)/userInfo/loading.tsx +5 -0
  34. package/templates/Projects/default/src/app/[locale]/(user)/{UserInfo → userInfo}/page.tsx +1 -2
  35. package/templates/Projects/default/src/app/[locale]/layout.tsx +34 -10
  36. package/templates/Projects/default/src/app/[locale]/loading.tsx +0 -9
  37. package/templates/Projects/default/src/app/[locale]/not-found.tsx +6 -15
  38. package/templates/Projects/default/src/app/[locale]/page.tsx +10 -1
  39. package/templates/Projects/default/src/app/api/auth/[...nextauth]/route.ts +8 -60
  40. package/templates/Projects/default/src/app/not-found.tsx +1 -1
  41. package/templates/Projects/default/src/app/sitemap.ts +2 -2
  42. package/templates/Projects/default/src/app/styles/globals.css +166 -113
  43. package/templates/Projects/default/src/auth.ts +20 -0
  44. package/templates/Projects/default/src/config.ts +3 -3
  45. package/templates/Projects/default/src/env.ts +65 -0
  46. package/templates/Projects/default/src/lib/i18n/messages.ts +8 -0
  47. package/templates/Projects/default/src/lib/i18n/request.ts +2 -16
  48. package/templates/Projects/default/src/lib/security/csp.ts +16 -0
  49. package/templates/Projects/default/src/lib/utils.ts +2 -1
  50. package/templates/Projects/default/src/proxy.ts +13 -0
  51. package/templates/Projects/default/src/ui/_global/BackButton.tsx +4 -2
  52. package/templates/Projects/default/src/ui/_global/Button.tsx +3 -8
  53. package/templates/Projects/default/src/ui/_global/GlobalHeader.tsx +10 -28
  54. package/templates/Projects/default/src/ui/_global/GlobalMain.tsx +1 -1
  55. package/templates/Projects/default/src/ui/_global/LocaleSwitcher.tsx +9 -10
  56. package/templates/Projects/default/src/ui/_global/PublicNav.tsx +51 -17
  57. package/templates/Projects/default/src/ui/_global/ThemeToggle.tsx +45 -39
  58. package/templates/Projects/default/src/ui/_global/UserNav.tsx +6 -6
  59. package/templates/Projects/default/src/ui/_home/page-ui.tsx +5 -7
  60. package/templates/Projects/default/src/ui/{Dashboard → dashboard}/LogoutButton.tsx +9 -7
  61. package/templates/Projects/default/src/ui/{Dashboard → dashboard}/StatsCard.tsx +4 -4
  62. package/templates/Projects/default/src/ui/{Dashboard → dashboard}/page-ui.tsx +7 -8
  63. package/templates/Projects/default/src/ui/login/page-ui.tsx +36 -0
  64. package/templates/Projects/default/src/ui/register/page-ui.tsx +38 -0
  65. package/templates/Projects/default/src/ui/settings/page-ui.tsx +15 -0
  66. package/templates/Projects/default/src/ui/userInfo/page-ui.tsx +15 -0
  67. package/templates/Projects/default/tailwind.config.ts +81 -1
  68. package/templates/Projects/default/tests/consumer/validate-template.ts +66 -0
  69. package/templates/Projects/default/tests/e2e/template-remediation.playwright.ts +106 -0
  70. package/templates/Projects/default/tests/rendering/verify-rendering.ts +56 -0
  71. package/templates/Projects/default/tests/unit/csp.test.ts +19 -0
  72. package/templates/Projects/default/tests/unit/env.test.ts +76 -0
  73. package/templates/Projects/default/tsconfig.json +6 -6
  74. package/templates/Projects/default/example.env +0 -8
  75. package/templates/Projects/default/messages/getMergedMessages.ts +0 -31
  76. package/templates/Projects/default/middleware.ts +0 -11
  77. package/templates/Projects/default/src/app/[locale]/(public)/Login/page.tsx +0 -6
  78. package/templates/Projects/default/src/app/[locale]/(public)/Register/page.tsx +0 -6
  79. package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/error.tsx +0 -38
  80. package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/loading.tsx +0 -10
  81. package/templates/Projects/default/src/app/[locale]/(user)/Settings/loading.tsx +0 -17
  82. package/templates/Projects/default/src/app/[locale]/(user)/UserInfo/loading.tsx +0 -17
  83. package/templates/Projects/default/src/app/api/auth/post-login/route.ts +0 -26
  84. package/templates/Projects/default/src/app/api/hello/route.ts +0 -5
  85. package/templates/Projects/default/src/app/layout.tsx +0 -11
  86. package/templates/Projects/default/src/app/page.tsx +0 -6
  87. package/templates/Projects/default/src/auth.config.ts +0 -0
  88. package/templates/Projects/default/src/lib/auth/disconnect.ts +0 -11
  89. package/templates/Projects/default/src/lib/auth/isConnected.ts +0 -18
  90. package/templates/Projects/default/src/lib/sample/example.ts +0 -3
  91. package/templates/Projects/default/src/lib/sample/index.ts +0 -3
  92. package/templates/Projects/default/src/ui/Login/page-ui.tsx +0 -22
  93. package/templates/Projects/default/src/ui/Register/page-ui.tsx +0 -26
  94. package/templates/Projects/default/src/ui/Settings/page-ui.tsx +0 -17
  95. package/templates/Projects/default/src/ui/UserInfo/page-ui.tsx +0 -17
  96. /package/templates/Projects/default/messages/en/{Dashboard.json → dashboard.json} +0 -0
  97. /package/templates/Projects/default/messages/en/{Login.json → login.json} +0 -0
  98. /package/templates/Projects/default/messages/en/{Register.json → register.json} +0 -0
  99. /package/templates/Projects/default/messages/en/{Settings.json → settings.json} +0 -0
  100. /package/templates/Projects/default/messages/en/{UserInfo.json → userInfo.json} +0 -0
  101. /package/templates/Projects/default/messages/fr/{Dashboard.json → dashboard.json} +0 -0
  102. /package/templates/Projects/default/messages/fr/{Login.json → login.json} +0 -0
  103. /package/templates/Projects/default/messages/fr/{Register.json → register.json} +0 -0
  104. /package/templates/Projects/default/messages/fr/{Settings.json → settings.json} +0 -0
  105. /package/templates/Projects/default/messages/fr/{UserInfo.json → userInfo.json} +0 -0
  106. /package/templates/Projects/default/public/{cnp-logo.png → logo.png} +0 -0
  107. /package/templates/Projects/default/src/ui/{Dashboard → dashboard}/WelcomeCard.tsx +0 -0
@@ -1,51 +1,33 @@
1
- "use client";
2
1
  import Image from "next/image";
3
2
  import UserNav from "@/ui/_global/UserNav";
4
3
  import LocaleSwitcher from "@/ui/_global/LocaleSwitcher";
5
4
  import PublicNav from "@/ui/_global/PublicNav";
6
- import { useEffect, useState } from "react";
7
5
  import { Link } from "@/lib/i18n/navigation";
8
- import { isConnected } from "@/lib/auth/isConnected";
9
6
 
10
7
  type GlobalHeaderProps = {
11
- isConnectedInitial: boolean;
8
+ hasSessionInitial: boolean;
12
9
  };
13
10
 
14
- export default function GlobalHeader({
15
- isConnectedInitial,
16
- }: GlobalHeaderProps) {
17
- const [isConnectedState, setIsConnectedState] = useState(isConnectedInitial);
18
-
19
- // Vérification dynamique côté client (exemple via /api/me)
20
- useEffect(() => {
21
- async function checkConnection() {
22
- setIsConnectedState(await isConnected());
23
- }
24
- checkConnection();
25
- // Optionnel : timer pour rafraîchir périodiquement
26
- // const interval = setInterval(checkConnection, 60000);
27
- // return () => clearInterval(interval);
28
- }, []);
29
-
11
+ export default function GlobalHeader({ hasSessionInitial }: GlobalHeaderProps) {
30
12
  return (
31
- <header className="fixed top-0 w-full z-50 glass-effect border-b">
32
- <div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-6">
33
- <Link href="/" className="">
34
- <div className="flex items-center h-16">
13
+ <header className="fixed top-0 z-50 w-full border-b bg-background/80 glass-effect">
14
+ <div className="mx-auto grid h-16 max-w-6xl grid-cols-[auto_1fr_auto] items-center gap-2 px-3 sm:px-4 md:px-6">
15
+ <Link href="/" aria-label="Create Next Pro home" className="min-w-0">
16
+ <div className="flex h-16 min-w-0 items-center">
35
17
  <Image
36
- src="/cnp-logo.svg"
18
+ src="/logo.svg"
37
19
  alt="Logo"
38
20
  width={63}
39
21
  height={63}
40
- className="mr-2"
22
+ className="mr-2 h-11 w-11 sm:h-[52px] sm:w-[52px] md:h-[63px] md:w-[63px]"
41
23
  />
42
- <h1 className="text-lg font-semibold tracking-tight select-none">
24
+ <h1 className="hidden whitespace-nowrap text-base font-semibold tracking-tight select-none sm:block md:text-lg">
43
25
  Create Next Pro
44
26
  </h1>
45
27
  </div>
46
28
  </Link>
47
29
 
48
- {isConnectedState ? <UserNav /> : <PublicNav />}
30
+ {hasSessionInitial ? <UserNav /> : <PublicNav />}
49
31
  <div className="flex items-center h-16">
50
32
  <LocaleSwitcher />
51
33
  </div>
@@ -6,7 +6,7 @@ export default function GlobalMain({
6
6
  children: React.ReactNode;
7
7
  }) {
8
8
  return (
9
- <main className="mt-0 min-h-screen bg-neutral-50 font-sans text-gray-900 flex flex-col">
9
+ <main className="mt-0 flex min-h-screen flex-col bg-neutral-50 text-foreground font-sans hero-pattern">
10
10
  <div className="flex-1 flex flex-col justify-center items-stretch mx-auto w-full px-6 self-stretch">
11
11
  {children}
12
12
  </div>
@@ -1,6 +1,6 @@
1
- "use client"; // DO NOT FORGET , this is a client component.
2
- import { useRouter, usePathname } from "next/navigation";
3
- import { useParams } from "next/navigation";
1
+ "use client";
2
+ import { useLocale, useTranslations } from "next-intl";
3
+ import { usePathname, useRouter } from "@/lib/i18n/navigation";
4
4
 
5
5
  const locales = [
6
6
  { code: "fr", label: "Français" },
@@ -10,23 +10,22 @@ const locales = [
10
10
  export default function LocaleSwitcher() {
11
11
  const router = useRouter();
12
12
  const pathname = usePathname();
13
- const params = useParams();
14
- const currentLocale = params?.locale || "fr";
13
+ const currentLocale = useLocale();
14
+ const t = useTranslations("_global_ui");
15
15
 
16
16
  const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
17
17
  const newLocale = e.target.value;
18
18
  if (newLocale === currentLocale) return;
19
- // Replace locale in pathname
20
- const newPath = pathname.replace(`/${currentLocale}`, `/${newLocale}`);
21
- router.push(newPath);
19
+
20
+ router.replace(pathname, { locale: newLocale });
22
21
  };
23
22
 
24
23
  return (
25
24
  <select
26
- className="ml-2 ring rounded px-2 py-1 text-sm dark:bg-gray-800 dark:text-gray-200"
25
+ className="w-[7.5rem] rounded px-2 py-1 text-sm ring dark:bg-stone-800 dark:text-gray-200 light:bg-stone-200 light:text-stone-800 sm:w-auto"
27
26
  value={currentLocale}
28
27
  onChange={handleChange}
29
- aria-label="Select language"
28
+ aria-label={t("select_language")}
30
29
  >
31
30
  {locales.map((l) => (
32
31
  <option key={l.code} value={l.code}>
@@ -1,4 +1,4 @@
1
- "use client"; // DO NOT FORGET , this is a client component.
1
+ "use client";
2
2
  import { Link } from "@/lib/i18n/navigation";
3
3
  import { useTranslations } from "next-intl";
4
4
  import { useEffect, useState, useRef } from "react";
@@ -8,14 +8,17 @@ import { Menu, X } from "lucide-react";
8
8
 
9
9
  const navLinks = [
10
10
  { href: "/", labelKey: "public_nav.links.home" },
11
- { href: "/Login", labelKey: "public_nav.links.login" },
12
- { href: "/Register", labelKey: "public_nav.links.register" },
11
+ { href: "/login", labelKey: "public_nav.links.login" },
12
+ { href: "/register", labelKey: "public_nav.links.register" },
13
13
  ];
14
14
 
15
15
  const PublicNav = () => {
16
16
  const t = useTranslations("_global_ui");
17
17
  const [open, setOpen] = useState(false);
18
18
  const menuRef = useRef<HTMLDivElement>(null);
19
+ const menuButtonRef = useRef<HTMLButtonElement>(null);
20
+ const firstLinkRef = useRef<HTMLAnchorElement>(null);
21
+
19
22
  useEffect(() => {
20
23
  const handleClickOutside = (event: MouseEvent) => {
21
24
  if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
@@ -25,6 +28,7 @@ const PublicNav = () => {
25
28
 
26
29
  if (open) {
27
30
  document.addEventListener("mousedown", handleClickOutside);
31
+ firstLinkRef.current?.focus();
28
32
  }
29
33
 
30
34
  return () => {
@@ -32,32 +36,47 @@ const PublicNav = () => {
32
36
  };
33
37
  }, [open]);
34
38
 
39
+ useEffect(() => {
40
+ const handleEscape = (event: KeyboardEvent) => {
41
+ if (event.key === "Escape" && open) {
42
+ setOpen(false);
43
+ menuButtonRef.current?.focus();
44
+ }
45
+ };
46
+ document.addEventListener("keydown", handleEscape);
47
+ return () => document.removeEventListener("keydown", handleEscape);
48
+ }, [open]);
49
+
35
50
  return (
36
- <nav aria-label="Public navigation">
37
- <div className="flex justify-between items-center h-16">
38
- <ul className="flex gap-2 md:gap-4 hidden md:flex items-center space-x-4">
51
+ <nav aria-label="Public navigation" className="justify-self-end">
52
+ <div className="flex h-16 items-center gap-1 sm:gap-2">
53
+ <ul className="hidden items-center gap-2 md:flex lg:gap-4">
39
54
  {navLinks.map((link) => (
40
55
  <li key={link.href}>
41
56
  <Link
42
57
  href={link.href}
43
- className="inline-block rounded px-3 py-1.5 text-sm font-medium dark:text-gray-400 light:text-gray-700 hover:text-blue-600 hover:bg-blue-50 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
58
+ className="inline-block rounded px-3 py-1.5 text-sm font-medium dark:text-stone-100 light:text-stone-900 dark:hover:bg-white/10 light:hover:bg-black/10 transition-colors focus:outline-none focus-visible:ring-2"
44
59
  >
45
60
  {t(link.labelKey)}
46
61
  </Link>
47
62
  </li>
48
63
  ))}
49
64
  </ul>
50
- <div className="flex items-center space-x-4">
65
+ <div className="flex items-center">
51
66
  <ThemeToggle />
52
67
  </div>
53
- <div className="md:hidden flex items-center">
68
+ <div className="flex items-center md:hidden">
54
69
  <Button
55
- variant="default"
56
- onClick={() => setOpen((o) => !o)}
70
+ ref={menuButtonRef}
71
+ variant="ghost"
72
+ size="icon"
57
73
  className="focus:outline-none"
74
+ onClick={() => setOpen((o) => !o)}
58
75
  aria-label="Menu"
76
+ aria-expanded={open}
77
+ aria-controls="public-mobile-menu"
59
78
  >
60
- {open ? <X className="h-7 w-7" /> : <Menu className="h-7 w-7" />}
79
+ {open ? <X className="h-6 w-6" /> : <Menu className="h-6 w-6" />}
61
80
  </Button>
62
81
  </div>
63
82
  </div>
@@ -66,19 +85,34 @@ const PublicNav = () => {
66
85
  <div className="fixed inset-0 z-40 md:hidden">
67
86
  <div className="absolute inset-0 bg-black/40" aria-hidden="true" />
68
87
  <div
88
+ id="public-mobile-menu"
69
89
  ref={menuRef}
70
- className="mobil-menu absolute top-4 left-1/2 -translate-x-1/2 flex flex-col gap-2 rounded-xl p-4 shadow-lg animate-fade-in z-50 w-11/12 max-w-xs"
90
+ role="dialog"
91
+ aria-modal="true"
92
+ aria-label={t("public_nav.menu")}
93
+ className="mobil-menu absolute top-20 left-1/2 z-50 flex w-11/12 max-w-xs -translate-x-1/2 flex-col gap-2 rounded-xl p-4 shadow-lg animate-fade-in"
71
94
  >
72
- <Link href="/Login" onClick={() => setOpen(false)}>
95
+ <Link ref={firstLinkRef} href="/" onClick={() => setOpen(false)}>
73
96
  <Button
74
97
  variant="ghost"
75
- className="w-full text-white justify-center hover:text-blue-300 cursor-pointer glass-effect"
98
+ className="w-full justify-center cursor-pointer glass-effect"
99
+ >
100
+ {t("public_nav.links.home")}
101
+ </Button>
102
+ </Link>
103
+ <Link href="/login" onClick={() => setOpen(false)}>
104
+ <Button
105
+ variant="ghost"
106
+ className="w-full justify-center cursor-pointer glass-effect"
76
107
  >
77
108
  {t("public_nav.links.login")}
78
109
  </Button>
79
110
  </Link>
80
- <Link href="/Register" onClick={() => setOpen(false)}>
81
- <Button className="w-full bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 justify-center cursor-pointer">
111
+ <Link href="/register" onClick={() => setOpen(false)}>
112
+ <Button
113
+ className="w-full justify-center cursor-pointer"
114
+ variant="ghost"
115
+ >
82
116
  {t("public_nav.links.register")}
83
117
  </Button>
84
118
  </Link>
@@ -1,53 +1,59 @@
1
1
  "use client";
2
- import { useEffect, useState } from "react";
2
+ import { useSyncExternalStore } from "react";
3
3
  import { Moon, Sun } from "lucide-react";
4
4
  import { Button } from "./Button";
5
+ import { useTranslations } from "next-intl";
5
6
 
6
- export default function ThemeToggle() {
7
- const [isDark, setIsDark] = useState(false);
8
-
9
- useEffect(() => {
10
- // Initial state from html class
11
- setIsDark(document.documentElement.classList.contains("dark"));
12
- }, []);
13
-
14
- const toggleTheme = () => {
15
- const html = document.documentElement;
16
- if (html.classList.contains("dark")) {
17
- html.classList.add("light");
18
- html.classList.remove("dark");
19
- setIsDark(false);
20
- localStorage.setItem("theme", "light");
21
- } else {
22
- html.classList.add("dark");
23
- html.classList.remove("light");
24
- setIsDark(true);
25
- localStorage.setItem("theme", "dark");
26
- }
7
+ const themeChangeEvent = "template-theme-change";
8
+
9
+ function getThemeSnapshot() {
10
+ return document.documentElement.classList.contains("dark") ? "dark" : "light";
11
+ }
12
+
13
+ function getServerThemeSnapshot() {
14
+ return "light";
15
+ }
16
+
17
+ function subscribeToThemeChange(onStoreChange: () => void) {
18
+ window.addEventListener(themeChangeEvent, onStoreChange);
19
+ window.addEventListener("storage", onStoreChange);
20
+
21
+ return () => {
22
+ window.removeEventListener(themeChangeEvent, onStoreChange);
23
+ window.removeEventListener("storage", onStoreChange);
27
24
  };
25
+ }
26
+
27
+ export default function ThemeToggle() {
28
+ const t = useTranslations("_global_ui");
29
+ const theme = useSyncExternalStore(
30
+ subscribeToThemeChange,
31
+ getThemeSnapshot,
32
+ getServerThemeSnapshot,
33
+ );
34
+ const isDark = theme === "dark";
28
35
 
29
- useEffect(() => {
30
- // On mount, set theme from localStorage
31
- const theme = localStorage.getItem("theme");
32
- if (theme === "dark") {
33
- document.documentElement.classList.add("dark");
34
- document.documentElement.classList.remove("light");
35
- setIsDark(true);
36
- } else if (theme === "light") {
37
- document.documentElement.classList.add("light");
38
- document.documentElement.classList.remove("dark");
39
- setIsDark(false);
40
- }
41
- }, []);
36
+ const applyTheme = (dark: boolean) => {
37
+ document.documentElement.classList.toggle("dark", dark);
38
+ document.documentElement.classList.toggle("light", !dark);
39
+ localStorage.setItem("theme", dark ? "dark" : "light");
40
+ window.dispatchEvent(new Event(themeChangeEvent));
41
+ };
42
42
 
43
43
  return (
44
44
  <Button
45
- onClick={toggleTheme}
46
- className="ml-2 p-2 rounded-full hover:bg-white/10 transition-colors "
47
- aria-label="Toggle theme"
45
+ onClick={() => applyTheme(!isDark)}
46
+ className="rounded-full p-0 dark:hover:bg-white/10 light:hover:bg-black/10 transition-colors"
47
+ aria-label={t("toggle_theme")}
48
+ variant="ghost"
49
+ size="icon"
48
50
  type="button"
49
51
  >
50
- {isDark ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />}
52
+ {isDark ? (
53
+ <Sun className="h-5 w-5 text-white" />
54
+ ) : (
55
+ <Moon className="h-5 w-5 text-black" />
56
+ )}
51
57
  </Button>
52
58
  );
53
59
  }
@@ -1,12 +1,12 @@
1
- "use client"; // DO NOT FORGET , this is a client component.
1
+ "use client";
2
2
  import { Link } from "@/lib/i18n/navigation";
3
3
  import { useTranslations } from "next-intl";
4
- import LogoutButton from "../Dashboard/LogoutButton";
4
+ import LogoutButton from "../dashboard/LogoutButton";
5
5
 
6
6
  const navLinks = [
7
- { href: "/Dashboard", labelKey: "user_nav.links.dashboard" },
8
- { href: "/Settings", labelKey: "user_nav.links.settings" },
9
- { href: "/UserInfo", labelKey: "user_nav.links.user_info" },
7
+ { href: "/dashboard", labelKey: "user_nav.links.dashboard" },
8
+ { href: "/settings", labelKey: "user_nav.links.settings" },
9
+ { href: "/userInfo", labelKey: "user_nav.links.user_info" },
10
10
  ];
11
11
 
12
12
  const UserNav = () => {
@@ -18,7 +18,7 @@ const UserNav = () => {
18
18
  <li key={link.href}>
19
19
  <Link
20
20
  href={link.href}
21
- className="inline-block rounded px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
21
+ className="inline-block rounded px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-stone-600 hover:bg-stone-50 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-stone-500"
22
22
  >
23
23
  {t(link.labelKey)}
24
24
  </Link>
@@ -1,14 +1,12 @@
1
1
  // src/ui/_home/page-ui.tsx
2
- "use client";
2
+ import { getTranslations } from "next-intl/server";
3
3
 
4
- import { useTranslations } from "next-intl";
5
-
6
- export default function HomePageUI() {
7
- const t = useTranslations("_home");
4
+ export default async function HomePageUI() {
5
+ const t = await getTranslations("_home");
8
6
 
9
7
  return (
10
8
  <>
11
- <section className="pt-32 pb-20 px-4 sm:px-6 lg:px-8 hero-pattern">
9
+ <section className="pt-32 pb-20 px-4 sm:px-6 lg:px-8">
12
10
  <div className="max-w-7xl mx-auto">
13
11
  <div className="grid lg:grid-cols-2 gap-12 items-center">
14
12
  <h1 className="text-5xl lg:text-6xl font-bold mb-6 leading-tight">
@@ -18,7 +16,7 @@ export default function HomePageUI() {
18
16
  ),
19
17
  })}
20
18
  </h1>
21
- <p className="text-lg text-gray-400">{t("description")}</p>
19
+ <p className="text-lg">{t("description")}</p>
22
20
  </div>
23
21
  </div>
24
22
  </section>
@@ -1,17 +1,19 @@
1
- // src/ui/Dashboard/LogoutButton.tsx
2
- "use client"; // DO NOT FORGET , this is a client component.
1
+ // src/ui/dashboard/LogoutButton.tsx
2
+ "use client";
3
3
  import { useTranslations } from "next-intl";
4
+ import { useLocale } from "next-intl";
5
+ import { signOut } from "next-auth/react";
4
6
  import { Button } from "@/ui/_global/Button";
5
7
  import { LogOut } from "lucide-react";
6
- import { disconnect } from "@/lib/auth/disconnect";
7
8
 
8
9
  const LogoutButton = () => {
9
- const t = useTranslations("Dashboard");
10
+ const t = useTranslations("dashboard");
11
+ const locale = useLocale();
12
+
10
13
  const handleLogout = () => {
11
- disconnect().then(() => {
12
- window.location.href = "/";
13
- });
14
+ void signOut({ redirectTo: `/${locale}` });
14
15
  };
16
+
15
17
  return (
16
18
  <Button
17
19
  variant="ghost"
@@ -3,11 +3,11 @@ import React from "react";
3
3
  export default function StatsCard({ t }: { t: (key: string) => string }) {
4
4
  return (
5
5
  <div className="rounded border bg-white p-6 shadow-sm">
6
- <h2 className="text-lg font-semibold mb-2">Statistiques</h2>
6
+ <h2 className="text-lg font-semibold mb-2">{t("widgets.stats.title")}</h2>
7
7
  <ul className="text-sm text-gray-600 space-y-1">
8
- <li>• {t("widgets.stats.views")}: 123</li>
9
- <li>• {t("widgets.stats.likes")}: 45</li>
10
- <li>• {t("widgets.stats.comments")}: 7</li>
8
+ <li>{t("widgets.stats.views")}: 123</li>
9
+ <li>{t("widgets.stats.likes")}: 45</li>
10
+ <li>{t("widgets.stats.comments")}: 7</li>
11
11
  </ul>
12
12
  </div>
13
13
  );
@@ -1,19 +1,18 @@
1
- // src/ui/Dashboard/page-ui.tsx
2
- "use client";
3
- import { useTranslations } from "next-intl";
1
+ // src/ui/dashboard/page-ui.tsx
2
+ import { getTranslations } from "next-intl/server";
4
3
  import BackButton from "@/ui/_global/BackButton";
5
- import WelcomeCard from "@/ui/Dashboard/WelcomeCard";
6
- import StatsCard from "@/ui/Dashboard/StatsCard";
4
+ import WelcomeCard from "@/ui/dashboard/WelcomeCard";
5
+ import StatsCard from "@/ui/dashboard/StatsCard";
7
6
 
8
- export default function Dashboard() {
9
- const t = useTranslations("Dashboard");
7
+ export default async function Dashboard() {
8
+ const t = await getTranslations("dashboard");
10
9
  return (
11
10
  <>
12
11
  <div className="flex items-center justify-between mb-6">
13
12
  <h1 className="text-2xl font-bold">{t("title")}</h1>
14
13
  <BackButton />
15
14
  </div>
16
- <p className="text-muted mb-8">{t("description")}</p>
15
+ <p className="mb-8">{t("description")}</p>
17
16
  <section className="grid grid-cols-1 md:grid-cols-2 gap-6">
18
17
  <WelcomeCard t={t} />
19
18
  <StatsCard t={t} />
@@ -0,0 +1,36 @@
1
+ // src/ui/login/page-ui.tsx
2
+ "use client";
3
+
4
+ import { useTranslations } from "next-intl";
5
+ import { useLocale } from "next-intl";
6
+ import { signIn } from "next-auth/react";
7
+ import { siGoogle } from "simple-icons";
8
+
9
+ export default function LoginPageUI() {
10
+ const t = useTranslations("login");
11
+ const locale = useLocale();
12
+
13
+ return (
14
+ <section className="py-8 px-4 max-w-3xl mx-auto">
15
+ <h1 className="text-2xl font-bold">{t("title")}</h1>
16
+ <p className="mt-2">{t("description")}</p>
17
+ <button
18
+ onClick={() => signIn("google", { redirectTo: `/${locale}/dashboard` })}
19
+ className="mt-6 w-full py-2 px-4 rounded bg-[rgb(var(--background))] font-semibold hover:bg-stone-700 transition"
20
+ >
21
+ <svg
22
+ role="img"
23
+ viewBox="0 0 24 24"
24
+ width="25"
25
+ height="25"
26
+ aria-label={siGoogle.title}
27
+ fill={`#${siGoogle.hex}`}
28
+ className="inline-block mr-4"
29
+ >
30
+ <path d={siGoogle.path} />
31
+ </svg>
32
+ {t("login_with_google")}
33
+ </button>
34
+ </section>
35
+ );
36
+ }
@@ -0,0 +1,38 @@
1
+ // src/ui/register/page-ui.tsx
2
+ "use client";
3
+
4
+ import { useTranslations } from "next-intl";
5
+ import { useLocale } from "next-intl";
6
+ import { signIn } from "next-auth/react";
7
+ import { siGoogle } from "simple-icons";
8
+
9
+ export default function RegisterPageUI() {
10
+ const t = useTranslations("register");
11
+ const locale = useLocale();
12
+
13
+ return (
14
+ <section className="py-8 px-4 max-w-md mx-auto">
15
+ <div className="flex items-center justify-between mb-6">
16
+ <h1 className="text-2xl font-bold">{t("title")}</h1>
17
+ </div>
18
+ <p className="mb-6">{t("description")}</p>
19
+ <button
20
+ onClick={() => signIn("google", { redirectTo: `/${locale}/dashboard` })}
21
+ className="w-full py-2 px-4 rounded font-semibold bg-[rgb(var(--background))] hover:bg-[rgb(var(--secondary))] transition"
22
+ >
23
+ <svg
24
+ role="img"
25
+ viewBox="0 0 24 24"
26
+ width="25"
27
+ height="25"
28
+ aria-label={siGoogle.title}
29
+ fill={`#${siGoogle.hex}`}
30
+ className="inline-block mr-4"
31
+ >
32
+ <path d={siGoogle.path} />
33
+ </svg>
34
+ {t("register_with_google")}
35
+ </button>
36
+ </section>
37
+ );
38
+ }
@@ -0,0 +1,15 @@
1
+ // src/ui/settings/page-ui.tsx
2
+ import { getTranslations } from "next-intl/server";
3
+
4
+ export default async function SettingsPageUI() {
5
+ const t = await getTranslations("settings");
6
+
7
+ return (
8
+ <>
9
+ <section className="py-8 px-4 max-w-3xl mx-auto">
10
+ <h1 className="text-2xl font-bold">{t("title")}</h1>
11
+ <p className="mt-2">{t("description")}</p>
12
+ </section>
13
+ </>
14
+ );
15
+ }
@@ -0,0 +1,15 @@
1
+ // src/ui/userInfo/page-ui.tsx
2
+ import { getTranslations } from "next-intl/server";
3
+
4
+ export default async function UserInfoPageUI() {
5
+ const t = await getTranslations("userInfo");
6
+
7
+ return (
8
+ <>
9
+ <section className="py-8 px-4 max-w-3xl mx-auto">
10
+ <h1 className="text-2xl font-bold">{t("title")}</h1>
11
+ <p className="mt-2">{t("description")}</p>
12
+ </section>
13
+ </>
14
+ );
15
+ }