create-next-pro-cli 0.1.4 → 0.1.6

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 (77) hide show
  1. package/package.json +5 -1
  2. package/src/index.ts +10 -454
  3. package/src/lib/addComponent.ts +113 -0
  4. package/src/lib/addPage.ts +170 -0
  5. package/src/lib/createProject.ts +18 -0
  6. package/src/lib/createProjectWithPrompt.ts +79 -0
  7. package/src/lib/rmPage.ts +52 -0
  8. package/src/lib/utils.ts +36 -0
  9. package/src/save-index.ts +520 -0
  10. package/templates/Page/loading.tsx +2 -7
  11. package/templates/Page/not-found.tsx +11 -0
  12. package/templates/Page/page-ui.tsx +1 -1
  13. package/templates/Projects/default/messages/en/{dashboard.json → Dashboard.json} +2 -1
  14. package/templates/Projects/default/messages/en/_global_ui.json +4 -0
  15. package/templates/Projects/default/messages/fr/{dashboard.json → Dashboard.json} +2 -1
  16. package/templates/Projects/default/messages/fr/_global_ui.json +4 -0
  17. package/templates/Projects/default/package.json +8 -2
  18. package/templates/Projects/default/src/app/[locale]/(public)/Login/page.tsx +6 -0
  19. package/templates/Projects/default/src/app/[locale]/(public)/Register/page.tsx +6 -0
  20. package/templates/Projects/default/src/app/[locale]/(public)/_home/loading.tsx +5 -1
  21. package/templates/Projects/default/src/app/[locale]/(public)/_home/page.tsx +3 -13
  22. package/templates/Projects/default/src/app/[locale]/(public)/layout.tsx +0 -2
  23. package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/loading.tsx +10 -0
  24. package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/page.tsx +5 -0
  25. package/templates/Projects/default/src/app/[locale]/(user)/Settings/page.tsx +6 -0
  26. package/templates/Projects/default/src/app/[locale]/(user)/UserInfo/page.tsx +6 -0
  27. package/templates/Projects/default/src/app/[locale]/(user)/layout.tsx +0 -3
  28. package/templates/Projects/default/src/app/[locale]/layout.tsx +3 -4
  29. package/templates/Projects/default/src/app/[locale]/loading.tsx +3 -6
  30. package/templates/Projects/default/src/app/[locale]/page.tsx +1 -111
  31. package/templates/Projects/default/src/app/api/auth/[...nextauth]/route.ts +6 -25
  32. package/templates/Projects/default/src/app/api/auth/post-login/route.ts +1 -2
  33. package/templates/Projects/default/src/app/styles/globals.css +295 -16
  34. package/templates/Projects/default/src/lib/auth/disconnect.ts +11 -0
  35. package/templates/Projects/default/src/lib/auth/isConnected.ts +5 -3
  36. package/templates/Projects/default/src/lib/utils.ts +6 -0
  37. package/templates/Projects/default/src/ui/Dashboard/LogoutButton.tsx +27 -0
  38. package/templates/Projects/default/src/ui/{dashboard/page.tsx → Dashboard/page-ui.tsx} +5 -4
  39. package/templates/Projects/default/src/{app/[locale]/(public)/login/page.tsx → ui/Login/page-ui.tsx} +7 -4
  40. package/templates/Projects/default/src/{app/[locale]/(public)/register/page.tsx → ui/Register/page-ui.tsx} +7 -4
  41. package/templates/Projects/default/src/ui/Settings/page-ui.tsx +17 -0
  42. package/templates/Projects/default/src/ui/UserInfo/page-ui.tsx +17 -0
  43. package/templates/Projects/default/src/ui/_global/BackButton.tsx +5 -3
  44. package/templates/Projects/default/src/ui/_global/Button.tsx +75 -0
  45. package/templates/Projects/default/src/ui/_global/GlobalHeader.tsx +42 -21
  46. package/templates/Projects/default/src/ui/_global/GlobalMain.tsx +3 -3
  47. package/templates/Projects/default/src/ui/_global/Loading.tsx +13 -0
  48. package/templates/Projects/default/src/ui/_global/LocaleSwitcher.tsx +1 -1
  49. package/templates/Projects/default/src/ui/_global/PublicNav.tsx +74 -13
  50. package/templates/Projects/default/src/ui/_global/ThemeToggle.tsx +53 -0
  51. package/templates/Projects/default/src/ui/_global/UserNav.tsx +8 -5
  52. package/templates/Projects/default/src/ui/_home/page-ui.tsx +27 -0
  53. package/templates/Projects/default/public/cnp-logo.ico +0 -0
  54. package/templates/Projects/default/public/file.svg +0 -1
  55. package/templates/Projects/default/public/globe.svg +0 -1
  56. package/templates/Projects/default/public/next.svg +0 -1
  57. package/templates/Projects/default/public/vercel.svg +0 -1
  58. package/templates/Projects/default/public/window.svg +0 -1
  59. package/templates/Projects/default/src/app/[locale]/(user)/dashboard/loading.tsx +0 -17
  60. package/templates/Projects/default/src/app/[locale]/(user)/dashboard/page.tsx +0 -12
  61. package/templates/Projects/default/src/app/[locale]/(user)/settings/page.tsx +0 -12
  62. package/templates/Projects/default/src/app/[locale]/(user)/user_info/page.tsx +0 -12
  63. package/templates/Projects/default/src/app/api/auth/[...nextauth].ts +0 -0
  64. package/templates/Projects/default/src/app/api/me/route.ts +0 -15
  65. /package/templates/Projects/default/messages/en/{login.json → Login.json} +0 -0
  66. /package/templates/Projects/default/messages/en/{register.json → Register.json} +0 -0
  67. /package/templates/Projects/default/messages/en/{settings.json → Settings.json} +0 -0
  68. /package/templates/Projects/default/messages/en/{user_info.json → UserInfo.json} +0 -0
  69. /package/templates/Projects/default/messages/fr/{login.json → Login.json} +0 -0
  70. /package/templates/Projects/default/messages/fr/{register.json → Register.json} +0 -0
  71. /package/templates/Projects/default/messages/fr/{settings.json → Settings.json} +0 -0
  72. /package/templates/Projects/default/messages/fr/{user_info.json → UserInfo.json} +0 -0
  73. /package/templates/Projects/default/src/app/[locale]/(user)/{dashboard → Dashboard}/error.tsx +0 -0
  74. /package/templates/Projects/default/src/app/[locale]/(user)/{settings → Settings}/loading.tsx +0 -0
  75. /package/templates/Projects/default/src/app/[locale]/(user)/{user_info → UserInfo}/loading.tsx +0 -0
  76. /package/templates/Projects/default/src/ui/{dashboard → Dashboard}/StatsCard.tsx +0 -0
  77. /package/templates/Projects/default/src/ui/{dashboard → Dashboard}/WelcomeCard.tsx +0 -0
@@ -0,0 +1,5 @@
1
+ import Dashboard from "@/ui/Dashboard/page-ui";
2
+
3
+ export default function DashboardPage() {
4
+ return <Dashboard />;
5
+ }
@@ -0,0 +1,6 @@
1
+ "use client";
2
+ import SettingsPageUI from "@/ui/Settings/page-ui";
3
+
4
+ export default function UserInfoPage() {
5
+ return <SettingsPageUI />;
6
+ }
@@ -0,0 +1,6 @@
1
+ "use client";
2
+ import UserInfoPageUI from "@/ui/UserInfo/page-ui";
3
+
4
+ export default function UserInfoPage() {
5
+ return <UserInfoPageUI />;
6
+ }
@@ -1,8 +1,5 @@
1
1
  // app/[locale]/(user)/layout.tsx
2
2
 
3
- import UserNav from "@/ui/_global/UserNav";
4
- import LocaleSwitcher from "@/ui/_global/LocaleSwitcher";
5
-
6
3
  export default function UserLayout({
7
4
  children,
8
5
  }: {
@@ -1,6 +1,6 @@
1
1
  // app/[locale]/layout.tsx
2
+ import { NextIntlClientProvider } from "next-intl";
2
3
  import { setRequestLocale } from "next-intl/server";
3
- import { NextIntlClientProvider, hasLocale, Locale } from "next-intl";
4
4
  import React from "react";
5
5
  import "@/app/styles/globals.css"; // Import global styles
6
6
  import GlobalHeader from "@/ui/_global/GlobalHeader";
@@ -16,14 +16,13 @@ export default async function LocaleLayout({
16
16
  }) {
17
17
  const { locale } = await params;
18
18
  setRequestLocale(locale);
19
-
20
- const isConnectedValue = await isConnected();
19
+ const isConnectedInitial = await isConnected();
21
20
 
22
21
  return (
23
22
  <html lang={locale}>
24
23
  <body>
25
24
  <NextIntlClientProvider>
26
- <GlobalHeader isConnected={isConnectedValue} />
25
+ <GlobalHeader isConnectedInitial={isConnectedInitial} />
27
26
  <GlobalMain>{children}</GlobalMain>
28
27
  </NextIntlClientProvider>
29
28
  </body>
@@ -1,12 +1,9 @@
1
1
  // app/[locale]/dashboard/loading.tsx
2
- "use client";
2
+
3
+ import LoadingUI from "@/ui/_global/Loading";
3
4
 
4
5
  export default function Loading() {
5
- return (
6
- <div className="flex flex-col flex-1 items-center justify-center">
7
- <p className="text-gray-500">Loading ROOT...</p>
8
- </div>
9
- );
6
+ return <LoadingUI />;
10
7
  }
11
8
  // This component is used to show a loading state while the dashboard is being prepared
12
9
  // You can customize it with a spinner, skeletons, or any loading UI you prefer
@@ -1,116 +1,6 @@
1
1
  import React from "react";
2
- import Image from "next/image";
3
2
  import HomePage from "@/app/[locale]/(public)/_home/page";
4
- import DashboardPage from "@/app/[locale]/(user)/dashboard/page";
5
- import PublicNav from "@/ui/_global/PublicNav";
6
- import UserNav from "@/ui/_global/UserNav";
7
- import LocaleSwitcher from "@/ui/_global/LocaleSwitcher";
8
- import { cookies } from "next/headers";
9
- import { verify } from "jsonwebtoken";
10
3
 
11
4
  export default async function FallbackPage() {
12
- let isConnected = false;
13
- const cookieStore = await cookies();
14
- const appToken = cookieStore.get("app_token")?.value;
15
- if (appToken) {
16
- try {
17
- verify(appToken, process.env.APP_JWT_SECRET!);
18
- isConnected = true;
19
- } catch {
20
- isConnected = false;
21
- }
22
- }
23
-
24
- return (
25
- <HomePage />
26
- /* <main className="min-h-screen bg-neutral-50 font-sans text-gray-900">
27
- <header className="sticky top-0 z-30 w-full border-b bg-white/80 backdrop-blur supports-[backdrop-filter]:bg-white/60 shadow-sm">
28
- <div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-6">
29
- <div className="flex items-center gap-3">
30
- <Image
31
- src="/cnp-logo.svg"
32
- alt="Logo"
33
- width={63}
34
- height={63}
35
- className="mr-2"
36
- />
37
- <span className="text-lg font-semibold tracking-tight select-none">
38
- Create Next Pro
39
- </span>
40
- </div>
41
- <nav className="flex items-center gap-4">
42
- {isConnected ? <UserNav /> : <PublicNav />}
43
- <LocaleSwitcher />
44
- </nav>
45
- </div>
46
- </header>
47
-
48
- <section className="mx-auto w-full max-w-3xl py-16 px-6">
49
- {isConnected ? <DashboardPage /> : <HomePage />}
50
- </section>
51
- </main> */
52
- );
5
+ return <HomePage />;
53
6
  }
54
-
55
- // Design inspiré de nextjs.org avec Tailwind CSS.
56
-
57
- // import React from "react";
58
- // import HomePage from "@/app/[locale]/(public)/_home/page";
59
- // import DashboardPage from "@/app/[locale]/(user)/dashboard/page";
60
- // import PublicNav from "@/ui/_global/PublicNav";
61
- // import UserNav from "@/ui/_global/UserNav";
62
- // import LocaleSwitcher from "@/ui/_global/LocaleSwitcher";
63
-
64
- // export default async function FallbackPage({
65
- // params,
66
- // }: {
67
- // params: Promise<{ locale: string }>;
68
- // }) {
69
- // // TODO: Remplacer par la logique réelle d'authentification (next-auth)
70
- // const isConnected = false;
71
- // const { locale } = await params;
72
-
73
- // return (
74
- // <main className="min-h-screen bg-neutral-50 font-sans text-gray-900">
75
- // {/* Header sticky façon nextjs.org */}
76
- // <header className="sticky top-0 z-30 w-full border-b bg-white/80 backdrop-blur supports-[backdrop-filter]:bg-white/60 shadow-sm">
77
- // <div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-6">
78
- // <div className="flex items-center gap-3">
79
- // {/* Logo Next.js SVG */}
80
- // <svg
81
- // width="32"
82
- // height="32"
83
- // viewBox="0 0 32 32"
84
- // fill="none"
85
- // xmlns="http://www.w3.org/2000/svg"
86
- // className="mr-2"
87
- // >
88
- // <rect width="32" height="32" rx="16" fill="#000" />
89
- // <path
90
- // d="M16.018 29.917c7.66 0 13.899-6.238 13.899-13.899 0-7.66-6.239-13.899-13.899-13.899-7.66 0-13.899 6.239-13.899 13.899 0 7.661 6.239 13.899 13.899 13.899Z"
91
- // fill="#fff"
92
- // />
93
- // <path
94
- // d="M23.635 22.295 12.7 10.36h-1.68v11.28h1.44v-9.12l10.08 10.08 1.095-1.095ZM19.36 21.64h1.44V10.36h-1.44v11.28Z"
95
- // fill="#000"
96
- // />
97
- // </svg>
98
- // <span className="text-lg font-semibold tracking-tight select-none">
99
- // Create Next Pro
100
- // </span>
101
- // </div>
102
- // <nav className="flex items-center gap-4">
103
- // {isConnected ? <UserNav /> : <PublicNav />}
104
- // <LocaleSwitcher />
105
- // </nav>
106
- // </div>
107
- // </header>
108
- // {/* Main content */}
109
- // <section className="mx-auto w-full max-w-3xl py-16 px-6">
110
- // {isConnected ? <DashboardPage /> : <HomePage />}
111
- // </section>
112
- // </main>
113
- // );
114
- // }
115
-
116
- // // Design inspiré de nextjs.org avec Tailwind CSS.
@@ -1,6 +1,4 @@
1
- import { sign } from "jsonwebtoken";
2
1
  import NextAuth from "next-auth";
3
- //import { authOptions } from "@/auth.config";
4
2
  import type {
5
3
  NextAuthOptions,
6
4
  Session,
@@ -10,8 +8,6 @@ import type {
10
8
  } from "next-auth";
11
9
  import GoogleProvider from "next-auth/providers/google";
12
10
  import type { JWT } from "next-auth/jwt";
13
- import { getToken } from "next-auth/jwt";
14
- import { NextRequest, NextResponse } from "next/server";
15
11
 
16
12
  const authOptions: NextAuthOptions = {
17
13
  providers: [
@@ -28,20 +24,14 @@ const authOptions: NextAuthOptions = {
28
24
  token.name = user.name;
29
25
  }
30
26
  console.log("JWT Callback:", token);
31
- // On ne met rien dans le token NextAuth
32
- // Le token JWT applicatif est géré dans le middleware
33
- // et dans le callback de la route d'authentification
34
- // pour éviter de le stocker dans la session NextAuth
35
- // et de le renvoyer au client.
36
- // Il est uniquement utilisé côté serveur pour vérifier l'authentification.
37
- // On peut ajouter des données supplémentaires si nécessaire
38
- // mais pour l'instant, on garde le token minimaliste.
39
- // Exemple d'ajout de données :
40
- // token.customData = "some data";
27
+
41
28
  return token;
42
29
  },
43
30
  async session({ session, token }: { session: Session; token: JWT }) {
44
- // On ne met rien dans la session NextAuth
31
+ if (session.user) {
32
+ session.user.email = token.email;
33
+ session.user.name = token.name;
34
+ }
45
35
  return session;
46
36
  },
47
37
  async signIn({
@@ -60,22 +50,13 @@ const authOptions: NextAuthOptions = {
60
50
  return true;
61
51
  },
62
52
  async redirect({ url, baseUrl }: { url: string; baseUrl: string }) {
63
- // Après login, redirige vers l'endpoint qui pose le cookie JWT applicatif
64
- // console.log("Before Redirect Callback :", { url: url, baseUrl: baseUrl });
65
53
  const customCallbackRoute = "/api/auth/post-login";
66
- /* if (url.startsWith(baseUrl)) {
67
- console.log("Redirecting to post-login endpoint");
68
- return "/api/auth/post-login";
69
- } */
70
- // console.log("Redirecting to base URL + customCallbackRoute");
54
+
71
55
  return baseUrl + customCallbackRoute;
72
56
  },
73
57
  },
74
- // Ajoute ici d'autres options/callbacks si besoin
75
58
  };
76
59
 
77
- // Suppression de customJwtCookie : la pose du cookie se fait dans /api/auth/post-login
78
-
79
60
  const handler = NextAuth(authOptions);
80
61
 
81
62
  export { handler as GET, handler as POST };
@@ -2,7 +2,6 @@ import { getToken } from "next-auth/jwt";
2
2
  import { sign } from "jsonwebtoken";
3
3
  import { NextResponse, NextRequest } from "next/server";
4
4
  import { redirect } from "@/lib/i18n/navigation";
5
- // import { NextURL } from "next/dist/server/web/next-url";
6
5
 
7
6
  export async function GET(req: NextRequest) {
8
7
  const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
@@ -13,7 +12,7 @@ export async function GET(req: NextRequest) {
13
12
  { expiresIn: "7d" }
14
13
  );
15
14
  const res = NextResponse.redirect(req.nextUrl.origin + "/");
16
- res.cookies.set("app_token", appJwt, {
15
+ res.cookies.set("access_token", appJwt, {
17
16
  httpOnly: true,
18
17
  secure: process.env.NODE_ENV === "production",
19
18
  sameSite: "strict",
@@ -1,26 +1,305 @@
1
1
  @import "tailwindcss";
2
2
 
3
- :root {
4
- --background: #ffffff;
5
- --foreground: #171717;
3
+ @tailwind base;
4
+ @tailwind components;
5
+ @tailwind utilities;
6
+
7
+ /* @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap"); */
8
+
9
+ /* Dark mode : fond noir, texte blanc */
10
+ .dark {
11
+ --background: 0 0% 0%; /* noir */
12
+ --foreground: 0 0% 100%; /* blanc */
13
+ --card: 0 0% 0%;
14
+ --card-foreground: 0 0% 100%;
15
+ --popover: 0 0% 0%;
16
+ --popover-foreground: 0 0% 100%;
17
+ --primary: 0 0% 100%; /* blanc */
18
+ --primary-foreground: 0 0% 0%; /* noir */
19
+ --secondary: 0 0% 20%; /* gris foncé */
20
+ --secondary-foreground: 0 0% 100%;
21
+ --muted: 0 0% 20%;
22
+ --muted-foreground: 0 0% 80%;
23
+ --accent: 0 0% 20%;
24
+ --accent-foreground: 0 0% 100%;
25
+ --destructive: 0 62.8% 30.6%;
26
+ --destructive-foreground: 0 0% 100%;
27
+ --border: 0 0% 20%;
28
+ --input: 0 0% 20%;
29
+ --ring: 0 0% 100%;
30
+ --radius: 0.75rem;
6
31
  }
7
32
 
8
- @theme inline {
9
- --color-background: var(--background);
10
- --color-foreground: var(--foreground);
11
- --font-sans: var(--font-geist-sans);
12
- --font-mono: var(--font-geist-mono);
33
+ /* Light mode : fond blanc, texte noir */
34
+ .light {
35
+ --background: 0 0% 100%; /* blanc */
36
+ --foreground: 0 0% 0%; /* noir */
37
+ --card: 0 0% 100%;
38
+ --card-foreground: 0 0% 0%;
39
+ --popover: 0 0% 100%;
40
+ --popover-foreground: 0 0% 0%;
41
+ --primary: 0 0% 0%; /* noir */
42
+ --primary-foreground: 0 0% 100%; /* blanc */
43
+ --secondary: 0 0% 95%; /* gris très clair */
44
+ --secondary-foreground: 0 0% 0%;
45
+ --muted: 0 0% 95%;
46
+ --muted-foreground: 0 0% 20%;
47
+ --accent: 0 0% 95%;
48
+ --accent-foreground: 0 0% 0%;
49
+ --destructive: 0 84.2% 60.2%;
50
+ --destructive-foreground: 0 0% 100%;
51
+ --border: 0 0% 80%;
52
+ --input: 0 0% 80%;
53
+ --ring: 0 0% 0%;
13
54
  }
14
55
 
15
- @media (prefers-color-scheme: dark) {
16
- :root {
17
- --background: #0a0a0a;
18
- --foreground: #ededed;
19
- }
56
+ * {
57
+ border-color: hsl(var(--border));
20
58
  }
21
59
 
22
60
  body {
23
- background: var(--background);
24
- color: var(--foreground);
25
- font-family: Arial, Helvetica, sans-serif;
61
+ font-family: "Inter", sans-serif;
62
+ background: hsl(var(--background));
63
+ color: hsl(var(--foreground));
64
+ line-height: 1.6;
65
+ transition: background 0.3s, color 0.3s;
66
+ }
67
+
68
+ .gradient-text {
69
+ background: linear-gradient(135deg, #fff 0%, #bbb 100%);
70
+ -webkit-background-clip: text;
71
+ -webkit-text-fill-color: transparent;
72
+ background-clip: text;
73
+ }
74
+ .light .gradient-text {
75
+ background: linear-gradient(135deg, #000 0%, #888 100%);
76
+ -webkit-background-clip: text;
77
+ -webkit-text-fill-color: transparent;
78
+ background-clip: text;
79
+ }
80
+
81
+ .dark .glass-effect {
82
+ background: rgba(0, 0, 0, 0.8);
83
+ backdrop-filter: blur(10px);
84
+ border: 1px solid #444;
85
+ }
86
+ .light .glass-effect {
87
+ background: rgba(0, 0, 0, 0.08);
88
+ border: 1px solid #ccc;
89
+ }
90
+
91
+ .chat-bubble-user {
92
+ background: linear-gradient(135deg, #fff 0%, #bbb 100%);
93
+ border-radius: 18px 18px 4px 18px;
94
+ color: #000;
95
+ }
96
+ .light .chat-bubble-user {
97
+ background: linear-gradient(135deg, #000 0%, #888 100%);
98
+ color: #fff;
99
+ }
100
+
101
+ .chat-bubble-ai {
102
+ background: rgba(255, 255, 255, 0.12);
103
+ border-radius: 18px 18px 18px 4px;
104
+ border: 1px solid #444;
105
+ color: #000;
106
+ }
107
+ .light .chat-bubble-ai {
108
+ background: rgba(0, 0, 0, 0.12);
109
+ border: 1px solid #ccc;
110
+ color: #fff;
111
+ }
112
+
113
+ .typing-indicator {
114
+ display: inline-block;
115
+ width: 8px;
116
+ height: 8px;
117
+ border-radius: 50%;
118
+ background-color: #fff;
119
+ animation: typing 1.4s infinite ease-in-out;
120
+ }
121
+ .light .typing-indicator {
122
+ background-color: #000;
123
+ }
124
+
125
+ .typing-indicator:nth-child(1) {
126
+ animation-delay: -0.32s;
127
+ }
128
+ .typing-indicator:nth-child(2) {
129
+ animation-delay: -0.16s;
130
+ }
131
+
132
+ @keyframes typing {
133
+ 0%,
134
+ 80%,
135
+ 100% {
136
+ transform: scale(0);
137
+ opacity: 0.5;
138
+ }
139
+ 40% {
140
+ transform: scale(1);
141
+ opacity: 1;
142
+ }
143
+ }
144
+
145
+ .hero-pattern {
146
+ background-image: radial-gradient(
147
+ circle at 25% 25%,
148
+ rgba(255, 255, 255, 0.18) 0%,
149
+ transparent 50%
150
+ ),
151
+ radial-gradient(
152
+ circle at 75% 75%,
153
+ rgba(180, 180, 180, 0.18) 0%,
154
+ transparent 50%
155
+ );
156
+ }
157
+ .light .hero-pattern {
158
+ background-image: radial-gradient(
159
+ circle at 25% 25%,
160
+ rgba(0, 0, 0, 0.08) 0%,
161
+ transparent 50%
162
+ ),
163
+ radial-gradient(
164
+ circle at 75% 75%,
165
+ rgba(120, 120, 120, 0.08) 0%,
166
+ transparent 50%
167
+ );
168
+ }
169
+
170
+ .light .mobil-menu {
171
+ background: rgba(255, 255, 255, 0.9);
172
+ }
173
+
174
+ .dark .mobil-menu {
175
+ background: rgba(0, 0, 0, 0.9);
176
+ }
177
+
178
+ .light .text-white,
179
+ .light .text-gray-300,
180
+ .light .text-gray-400 {
181
+ color: black;
182
+ }
183
+
184
+ .light button.text-white:hover,
185
+ .light a:hover {
186
+ color: rgb(142, 197, 255);
187
+ }
188
+ .gsi-material-button {
189
+ user-select: none;
190
+ -webkit-user-select: none;
191
+ -ms-user-select: none;
192
+ appearance: none;
193
+ -webkit-appearance: none;
194
+ /* background-color: linear-gradient(135deg, #fff 0%, #a78bfa 100%);
195
+ backdrop-filter: blur(8px); */
196
+ /* background-color: #131314; */
197
+ /* background-image: none; */
198
+ /* background: linear-gradient(135deg, #ffffff 0%, #a78bfa 100%); */
199
+ backdrop-filter: blur(8px);
200
+ border: 1px solid #747775;
201
+ border-radius: 4px;
202
+ box-sizing: border-box;
203
+ color: #e3e3e3;
204
+ /* color: #000000; */
205
+ cursor: pointer;
206
+ font-family: "Roboto", arial, sans-serif;
207
+ font-size: 14px;
208
+ height: 40px;
209
+ letter-spacing: 0.25px;
210
+ outline: none;
211
+ overflow: hidden;
212
+ padding: 0 12px;
213
+ position: relative;
214
+ text-align: center;
215
+ transition: background-color 0.218s, border-color 0.218s, box-shadow 0.218s;
216
+ vertical-align: middle;
217
+ white-space: nowrap;
218
+ width: auto;
219
+ max-width: 400px;
220
+ min-width: min-content;
221
+ border-color: #8e918f;
222
+ }
223
+ .light .gsi-material-button {
224
+ color: #222;
225
+ background: #fff;
226
+ border-color: #d1d5db;
227
+ }
228
+
229
+ .gsi-material-button .gsi-material-button-icon {
230
+ height: 20px;
231
+ margin-right: 12px;
232
+ min-width: 20px;
233
+ width: 20px;
234
+ }
235
+
236
+ .gsi-material-button .gsi-material-button-content-wrapper {
237
+ -webkit-align-items: center;
238
+ align-items: center;
239
+ display: flex;
240
+ -webkit-flex-direction: row;
241
+ flex-direction: row;
242
+ -webkit-flex-wrap: nowrap;
243
+ flex-wrap: nowrap;
244
+ height: 100%;
245
+ justify-content: space-between;
246
+ position: relative;
247
+ width: 100%;
248
+ }
249
+
250
+ .gsi-material-button .gsi-material-button-contents {
251
+ -webkit-flex-grow: 1;
252
+ flex-grow: 1;
253
+ font-family: "Roboto", arial, sans-serif;
254
+ font-weight: 500;
255
+ overflow: hidden;
256
+ text-overflow: ellipsis;
257
+ vertical-align: top;
258
+ }
259
+
260
+ .gsi-material-button .gsi-material-button-state {
261
+ -webkit-transition: opacity 0.218s;
262
+ transition: opacity 0.218s;
263
+ bottom: 0;
264
+ left: 0;
265
+ opacity: 0;
266
+ position: absolute;
267
+ right: 0;
268
+ top: 0;
269
+ }
270
+
271
+ .gsi-material-button:disabled {
272
+ cursor: default;
273
+ background-color: #13131461;
274
+ border-color: #8e918f1f;
275
+ }
276
+
277
+ .gsi-material-button:disabled .gsi-material-button-state {
278
+ background-color: #e3e3e31f;
279
+ }
280
+
281
+ .gsi-material-button:disabled .gsi-material-button-contents {
282
+ opacity: 38%;
283
+ }
284
+
285
+ .gsi-material-button:disabled .gsi-material-button-icon {
286
+ opacity: 38%;
287
+ }
288
+
289
+ .gsi-material-button:not(:disabled):active .gsi-material-button-state,
290
+ .gsi-material-button:not(:disabled):focus .gsi-material-button-state {
291
+ background-color: white;
292
+ opacity: 12%;
293
+ }
294
+
295
+ .gsi-material-button:not(:disabled):hover {
296
+ -webkit-box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3),
297
+ 0 1px 3px 1px rgba(60, 64, 67, 0.15);
298
+ box-shadow: 0 1px 2px 0 rgba(60, 64, 67, 0.3),
299
+ 0 1px 3px 1px rgba(60, 64, 67, 0.15);
300
+ }
301
+
302
+ .gsi-material-button:not(:disabled):hover .gsi-material-button-state {
303
+ background-color: white;
304
+ opacity: 8%;
26
305
  }
@@ -0,0 +1,11 @@
1
+ // src/lib/auth/disconnect.ts
2
+ "use server";
3
+ import { cookies } from "next/headers";
4
+
5
+ /**
6
+ * Supprime le cookie access_token pour déconnecter l'utilisateur côté serveur.
7
+ */
8
+ export async function disconnect() {
9
+ const cookieStore = await cookies();
10
+ cookieStore.delete("access_token");
11
+ }
@@ -1,12 +1,14 @@
1
+ "use server";
2
+ // src/lib/auth/isConnected.ts
1
3
  import { cookies } from "next/headers";
2
4
  import { verify } from "jsonwebtoken";
3
5
 
4
6
  export async function isConnected(): Promise<boolean> {
5
7
  const cookieStore = await cookies();
6
- const appToken = cookieStore.get("app_token")?.value;
7
- if (appToken) {
8
+ const accessToken = cookieStore.get("access_token")?.value;
9
+ if (accessToken) {
8
10
  try {
9
- verify(appToken, process.env.APP_JWT_SECRET!);
11
+ verify(accessToken, process.env.APP_JWT_SECRET!);
10
12
  return true;
11
13
  } catch {
12
14
  return false;
@@ -0,0 +1,6 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: unknown[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,27 @@
1
+ // src/ui/Dashboard/LogoutButton.tsx
2
+ "use client"; // DO NOT FORGET , this is a client component.
3
+ import { useTranslations } from "next-intl";
4
+ import { Button } from "@/ui/_global/Button";
5
+ import { LogOut } from "lucide-react";
6
+ import { disconnect } from "@/lib/auth/disconnect";
7
+
8
+ const LogoutButton = () => {
9
+ const t = useTranslations("Dashboard");
10
+ const handleLogout = () => {
11
+ disconnect().then(() => {
12
+ window.location.href = "/";
13
+ });
14
+ };
15
+ return (
16
+ <Button
17
+ variant="ghost"
18
+ size="sm"
19
+ onClick={handleLogout}
20
+ className="text-gray-400 hover:text-black glass-effect"
21
+ >
22
+ <LogOut className="h-4 w-4 sm:mr-2" />
23
+ <span className="hidden sm:inline">{t("logout")}</span>
24
+ </Button>
25
+ );
26
+ };
27
+ export default LogoutButton;
@@ -1,11 +1,12 @@
1
- ("");
1
+ // src/ui/Dashboard/page-ui.tsx
2
+ "use client";
2
3
  import { useTranslations } from "next-intl";
3
4
  import BackButton from "@/ui/_global/BackButton";
4
- import WelcomeCard from "@/ui/dashboard/WelcomeCard";
5
- import StatsCard from "@/ui/dashboard/StatsCard";
5
+ import WelcomeCard from "@/ui/Dashboard/WelcomeCard";
6
+ import StatsCard from "@/ui/Dashboard/StatsCard";
6
7
 
7
8
  export default function Dashboard() {
8
- const t = useTranslations("dashboard");
9
+ const t = useTranslations("Dashboard");
9
10
  return (
10
11
  <>
11
12
  <div className="flex items-center justify-between mb-6">