create-next-pro-cli 0.1.3 → 0.1.5

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 (71) hide show
  1. package/package.json +1 -1
  2. package/templates/Page/loading.tsx +2 -6
  3. package/templates/Page/not-found.tsx +11 -0
  4. package/templates/Page/page-ui.tsx +1 -1
  5. package/templates/Page/page.tsx +0 -20
  6. package/templates/Projects/default/messages/en/{dashboard.json → Dashboard.json} +2 -1
  7. package/templates/Projects/default/messages/en/_global_ui.json +4 -0
  8. package/templates/Projects/default/messages/fr/{dashboard.json → Dashboard.json} +2 -1
  9. package/templates/Projects/default/messages/fr/_global_ui.json +4 -0
  10. package/templates/Projects/default/package.json +8 -2
  11. package/templates/Projects/default/src/app/[locale]/(public)/Login/page.tsx +6 -0
  12. package/templates/Projects/default/src/app/[locale]/(public)/Register/page.tsx +6 -0
  13. package/templates/Projects/default/src/app/[locale]/(public)/_home/loading.tsx +5 -1
  14. package/templates/Projects/default/src/app/[locale]/(public)/_home/page.tsx +3 -13
  15. package/templates/Projects/default/src/app/[locale]/(public)/layout.tsx +0 -2
  16. package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/loading.tsx +10 -0
  17. package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/page.tsx +5 -0
  18. package/templates/Projects/default/src/app/[locale]/(user)/Settings/page.tsx +6 -0
  19. package/templates/Projects/default/src/app/[locale]/(user)/UserInfo/page.tsx +6 -0
  20. package/templates/Projects/default/src/app/[locale]/(user)/layout.tsx +0 -3
  21. package/templates/Projects/default/src/app/[locale]/layout.tsx +3 -4
  22. package/templates/Projects/default/src/app/[locale]/loading.tsx +3 -6
  23. package/templates/Projects/default/src/app/[locale]/page.tsx +1 -111
  24. package/templates/Projects/default/src/app/api/auth/[...nextauth]/route.ts +6 -25
  25. package/templates/Projects/default/src/app/api/auth/post-login/route.ts +1 -2
  26. package/templates/Projects/default/src/app/styles/globals.css +295 -16
  27. package/templates/Projects/default/src/lib/auth/disconnect.ts +11 -0
  28. package/templates/Projects/default/src/lib/auth/isConnected.ts +5 -3
  29. package/templates/Projects/default/src/lib/utils.ts +6 -0
  30. package/templates/Projects/default/src/ui/Dashboard/LogoutButton.tsx +27 -0
  31. package/templates/Projects/default/src/ui/{dashboard/page.tsx → Dashboard/page-ui.tsx} +5 -4
  32. package/templates/Projects/default/src/{app/[locale]/(public)/login/page.tsx → ui/Login/page-ui.tsx} +7 -4
  33. package/templates/Projects/default/src/{app/[locale]/(public)/register/page.tsx → ui/Register/page-ui.tsx} +7 -4
  34. package/templates/Projects/default/src/ui/Settings/page-ui.tsx +17 -0
  35. package/templates/Projects/default/src/ui/UserInfo/page-ui.tsx +17 -0
  36. package/templates/Projects/default/src/ui/_global/BackButton.tsx +5 -3
  37. package/templates/Projects/default/src/ui/_global/Button.tsx +75 -0
  38. package/templates/Projects/default/src/ui/_global/GlobalHeader.tsx +42 -21
  39. package/templates/Projects/default/src/ui/_global/GlobalMain.tsx +3 -3
  40. package/templates/Projects/default/src/ui/_global/Loading.tsx +13 -0
  41. package/templates/Projects/default/src/ui/_global/LocaleSwitcher.tsx +1 -1
  42. package/templates/Projects/default/src/ui/_global/PublicNav.tsx +74 -13
  43. package/templates/Projects/default/src/ui/_global/ThemeToggle.tsx +53 -0
  44. package/templates/Projects/default/src/ui/_global/UserNav.tsx +8 -5
  45. package/templates/Projects/default/src/ui/_home/page-ui.tsx +27 -0
  46. package/bun.lock +0 -27
  47. package/templates/Projects/default/public/cnp-logo.ico +0 -0
  48. package/templates/Projects/default/public/file.svg +0 -1
  49. package/templates/Projects/default/public/globe.svg +0 -1
  50. package/templates/Projects/default/public/next.svg +0 -1
  51. package/templates/Projects/default/public/vercel.svg +0 -1
  52. package/templates/Projects/default/public/window.svg +0 -1
  53. package/templates/Projects/default/src/app/[locale]/(user)/dashboard/loading.tsx +0 -17
  54. package/templates/Projects/default/src/app/[locale]/(user)/dashboard/page.tsx +0 -12
  55. package/templates/Projects/default/src/app/[locale]/(user)/settings/page.tsx +0 -12
  56. package/templates/Projects/default/src/app/[locale]/(user)/user_info/page.tsx +0 -12
  57. package/templates/Projects/default/src/app/api/auth/[...nextauth].ts +0 -0
  58. package/templates/Projects/default/src/app/api/me/route.ts +0 -15
  59. /package/templates/Projects/default/messages/en/{login.json → Login.json} +0 -0
  60. /package/templates/Projects/default/messages/en/{register.json → Register.json} +0 -0
  61. /package/templates/Projects/default/messages/en/{settings.json → Settings.json} +0 -0
  62. /package/templates/Projects/default/messages/en/{user_info.json → UserInfo.json} +0 -0
  63. /package/templates/Projects/default/messages/fr/{login.json → Login.json} +0 -0
  64. /package/templates/Projects/default/messages/fr/{register.json → Register.json} +0 -0
  65. /package/templates/Projects/default/messages/fr/{settings.json → Settings.json} +0 -0
  66. /package/templates/Projects/default/messages/fr/{user_info.json → UserInfo.json} +0 -0
  67. /package/templates/Projects/default/src/app/[locale]/(user)/{dashboard → Dashboard}/error.tsx +0 -0
  68. /package/templates/Projects/default/src/app/[locale]/(user)/{settings → Settings}/loading.tsx +0 -0
  69. /package/templates/Projects/default/src/app/[locale]/(user)/{user_info → UserInfo}/loading.tsx +0 -0
  70. /package/templates/Projects/default/src/ui/{dashboard → Dashboard}/StatsCard.tsx +0 -0
  71. /package/templates/Projects/default/src/ui/{dashboard → Dashboard}/WelcomeCard.tsx +0 -0
@@ -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">
@@ -1,11 +1,14 @@
1
+ // src/ui/Login/page-ui.tsx
1
2
  "use client";
3
+
2
4
  import { useTranslations } from "next-intl";
3
5
  import { signIn } from "next-auth/react";
4
6
 
5
- export default function LoginPage() {
6
- const t = useTranslations("login");
7
+ export default function LoginPageUI() {
8
+ const t = useTranslations("Login");
9
+
7
10
  return (
8
- <main className="py-8 px-4 max-w-3xl mx-auto">
11
+ <section className="py-8 px-4 max-w-3xl mx-auto">
9
12
  <h1 className="text-2xl font-bold">{t("title")}</h1>
10
13
  <p className="text-muted mt-2">{t("description")}</p>
11
14
  <button
@@ -14,6 +17,6 @@ export default function LoginPage() {
14
17
  >
15
18
  {t("login_with_google")}
16
19
  </button>
17
- </main>
20
+ </section>
18
21
  );
19
22
  }
@@ -1,12 +1,15 @@
1
+ // src/ui/Register/page-ui.tsx
1
2
  "use client";
3
+
2
4
  import { useTranslations } from "next-intl";
3
5
  import { signIn } from "next-auth/react";
4
6
  import BackButton from "@/ui/_global/BackButton";
5
7
 
6
- export default function RegisterPage() {
7
- const t = useTranslations("register");
8
+ export default function RegisterPageUI() {
9
+ const t = useTranslations("Register");
10
+
8
11
  return (
9
- <main className="py-8 px-4 max-w-md mx-auto">
12
+ <section className="py-8 px-4 max-w-md mx-auto">
10
13
  <div className="flex items-center justify-between mb-6">
11
14
  <h1 className="text-2xl font-bold">{t("title")}</h1>
12
15
  <BackButton />
@@ -18,6 +21,6 @@ export default function RegisterPage() {
18
21
  >
19
22
  {t("register_with_google")}
20
23
  </button>
21
- </main>
24
+ </section>
22
25
  );
23
26
  }
@@ -0,0 +1,17 @@
1
+ // src/ui/Settings/page-ui.tsx
2
+ "use client";
3
+
4
+ import { useTranslations } from "next-intl";
5
+
6
+ export default function SettingsPageUI() {
7
+ const t = useTranslations("Settings");
8
+
9
+ return (
10
+ <>
11
+ <section className="py-8 px-4 max-w-3xl mx-auto">
12
+ <h1 className="text-2xl font-bold">{t("title")}</h1>
13
+ <p className="text-muted mt-2">{t("description")}</p>
14
+ </section>
15
+ </>
16
+ );
17
+ }
@@ -0,0 +1,17 @@
1
+ // src/ui/UserInfo/page-ui.tsx
2
+ "use client";
3
+
4
+ import { useTranslations } from "next-intl";
5
+
6
+ export default function UserInfoPageUI() {
7
+ const t = useTranslations("UserInfo");
8
+
9
+ return (
10
+ <>
11
+ <section className="py-8 px-4 max-w-3xl mx-auto">
12
+ <h1 className="text-2xl font-bold">{t("title")}</h1>
13
+ <p className="text-muted mt-2">{t("description")}</p>
14
+ </section>
15
+ </>
16
+ );
17
+ }
@@ -1,15 +1,17 @@
1
1
  "use client"; // DO NOT FORGET , this is a client component.
2
2
  import { useRouter } from "next/navigation";
3
+ import { StepBackIcon } from "lucide-react";
4
+ import { Button } from "./Button";
3
5
 
4
6
  export default function BackButton({ className = "" }: { className?: string }) {
5
7
  const router = useRouter();
6
8
  return (
7
- <button
9
+ <Button
8
10
  type="button"
9
11
  onClick={() => router.back()}
10
12
  className={`inline-flex items-center gap-2 px-3 py-1.5 rounded border border-gray-300 bg-white text-gray-700 hover:bg-gray-100 transition ${className}`}
11
13
  >
12
- <span aria-hidden="true">←</span> Retour
13
- </button>
14
+ <StepBackIcon className="h-4 w-4" /> Retour
15
+ </Button>
14
16
  );
15
17
  }
@@ -0,0 +1,75 @@
1
+ // src/ui/_global/Button.tsx
2
+ "use client";
3
+ import { cn } from "@/lib/utils";
4
+ import { Slot } from "@radix-ui/react-slot";
5
+ import { cva } from "class-variance-authority";
6
+ import React from "react";
7
+
8
+ const buttonVariants = cva(
9
+ "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer disabled:pointer-events-none disabled:opacity-50",
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default:
14
+ "bg-primary text-primary-foreground hover:bg-primary/90 dark:text-white",
15
+ destructive:
16
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
17
+ outline:
18
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
19
+ secondary:
20
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
21
+ ghost: "hover:bg-accent hover:text-accent-foreground",
22
+ link: "text-primary underline-offset-4 hover:underline",
23
+ negative:
24
+ "bg-black text-white dark:bg-white dark:text-black hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors",
25
+ },
26
+ size: {
27
+ default: "h-10 px-4 py-2",
28
+ sm: "h-9 rounded-md px-3",
29
+ lg: "h-11 rounded-md px-8",
30
+ icon: "h-10 w-10",
31
+ },
32
+ },
33
+ defaultVariants: {
34
+ variant: "default",
35
+ size: "default",
36
+ },
37
+ }
38
+ );
39
+
40
+ import type { ButtonHTMLAttributes, DetailedHTMLProps, ReactNode } from "react";
41
+
42
+ type ButtonProps = {
43
+ className?: string;
44
+ variant?:
45
+ | "default"
46
+ | "destructive"
47
+ | "outline"
48
+ | "secondary"
49
+ | "ghost"
50
+ | "link";
51
+ size?: "default" | "sm" | "lg" | "icon";
52
+ asChild?: boolean;
53
+ children?: ReactNode;
54
+ } & DetailedHTMLProps<
55
+ ButtonHTMLAttributes<HTMLButtonElement>,
56
+ HTMLButtonElement
57
+ >;
58
+
59
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
60
+ ({ className, variant, size, asChild = false, children, ...props }, ref) => {
61
+ const Comp = asChild ? Slot : "button";
62
+ return (
63
+ <Comp
64
+ className={cn(buttonVariants({ variant, size, className }))}
65
+ ref={ref}
66
+ {...props}
67
+ >
68
+ {children}
69
+ </Comp>
70
+ );
71
+ }
72
+ );
73
+ Button.displayName = "Button";
74
+
75
+ export { Button, buttonVariants };
@@ -1,33 +1,54 @@
1
+ "use client";
1
2
  import Image from "next/image";
2
3
  import UserNav from "@/ui/_global/UserNav";
3
4
  import LocaleSwitcher from "@/ui/_global/LocaleSwitcher";
4
5
  import PublicNav from "@/ui/_global/PublicNav";
6
+ import { useEffect, useState } from "react";
7
+ import { Link } from "@/lib/i18n/navigation";
8
+ import { isConnected } from "@/lib/auth/isConnected";
9
+
10
+ type GlobalHeaderProps = {
11
+ isConnectedInitial: boolean;
12
+ };
5
13
 
6
14
  export default function GlobalHeader({
7
- isConnected,
8
- }: {
9
- isConnected: boolean;
10
- }) {
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
30
  return (
12
- <header className="sticky top-0 z-30 w-full border-b bg-white/80 backdrop-blur supports-[backdrop-filter]:bg-white/60 shadow-sm">
31
+ <header className="fixed top-0 w-full z-50 glass-effect border-b">
13
32
  <div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-6">
14
- <div className="flex items-center gap-3">
15
- {/* Logo Next.js SVG */}
16
- <Image
17
- src="/cnp-logo.svg"
18
- alt="Logo"
19
- width={63}
20
- height={63}
21
- className="mr-2"
22
- />
23
- <span className="text-lg font-semibold tracking-tight select-none">
24
- Create Next Pro
25
- </span>
26
- </div>
27
- <nav className="flex items-center gap-4">
28
- {isConnected ? <UserNav /> : <PublicNav />}
33
+ <Link href="/" className="">
34
+ <div className="flex items-center h-16">
35
+ <Image
36
+ src="/cnp-logo.svg"
37
+ alt="Logo"
38
+ width={63}
39
+ height={63}
40
+ className="mr-2"
41
+ />
42
+ <h1 className="text-lg font-semibold tracking-tight select-none">
43
+ Create Next Pro
44
+ </h1>
45
+ </div>
46
+ </Link>
47
+
48
+ {isConnectedState ? <UserNav /> : <PublicNav />}
49
+ <div className="flex items-center h-16">
29
50
  <LocaleSwitcher />
30
- </nav>
51
+ </div>
31
52
  </div>
32
53
  </header>
33
54
  );
@@ -6,10 +6,10 @@ export default function GlobalMain({
6
6
  children: React.ReactNode;
7
7
  }) {
8
8
  return (
9
- <main className="min-h-screen bg-neutral-50 font-sans text-gray-900">
10
- <section className="mx-auto w-full max-w-3xl py-16 px-6">
9
+ <main className="mt-0 min-h-screen bg-neutral-50 font-sans text-gray-900 flex flex-col">
10
+ <div className="flex-1 flex flex-col justify-center items-stretch mx-auto w-full px-6 self-stretch">
11
11
  {children}
12
- </section>
12
+ </div>
13
13
  </main>
14
14
  );
15
15
  }