create-next-pro-cli 0.1.4 → 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.
- package/package.json +1 -1
- package/templates/Page/loading.tsx +2 -7
- package/templates/Page/not-found.tsx +11 -0
- package/templates/Page/page-ui.tsx +1 -1
- package/templates/Projects/default/messages/en/{dashboard.json → Dashboard.json} +2 -1
- package/templates/Projects/default/messages/en/_global_ui.json +4 -0
- package/templates/Projects/default/messages/fr/{dashboard.json → Dashboard.json} +2 -1
- package/templates/Projects/default/messages/fr/_global_ui.json +4 -0
- package/templates/Projects/default/package.json +8 -2
- package/templates/Projects/default/src/app/[locale]/(public)/Login/page.tsx +6 -0
- package/templates/Projects/default/src/app/[locale]/(public)/Register/page.tsx +6 -0
- package/templates/Projects/default/src/app/[locale]/(public)/_home/loading.tsx +5 -1
- package/templates/Projects/default/src/app/[locale]/(public)/_home/page.tsx +3 -13
- package/templates/Projects/default/src/app/[locale]/(public)/layout.tsx +0 -2
- package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/loading.tsx +10 -0
- package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/page.tsx +5 -0
- package/templates/Projects/default/src/app/[locale]/(user)/Settings/page.tsx +6 -0
- package/templates/Projects/default/src/app/[locale]/(user)/UserInfo/page.tsx +6 -0
- package/templates/Projects/default/src/app/[locale]/(user)/layout.tsx +0 -3
- package/templates/Projects/default/src/app/[locale]/layout.tsx +3 -4
- package/templates/Projects/default/src/app/[locale]/loading.tsx +3 -6
- package/templates/Projects/default/src/app/[locale]/page.tsx +1 -111
- package/templates/Projects/default/src/app/api/auth/[...nextauth]/route.ts +6 -25
- package/templates/Projects/default/src/app/api/auth/post-login/route.ts +1 -2
- package/templates/Projects/default/src/app/styles/globals.css +295 -16
- package/templates/Projects/default/src/lib/auth/disconnect.ts +11 -0
- package/templates/Projects/default/src/lib/auth/isConnected.ts +5 -3
- package/templates/Projects/default/src/lib/utils.ts +6 -0
- package/templates/Projects/default/src/ui/Dashboard/LogoutButton.tsx +27 -0
- package/templates/Projects/default/src/ui/{dashboard/page.tsx → Dashboard/page-ui.tsx} +5 -4
- package/templates/Projects/default/src/{app/[locale]/(public)/login/page.tsx → ui/Login/page-ui.tsx} +7 -4
- package/templates/Projects/default/src/{app/[locale]/(public)/register/page.tsx → ui/Register/page-ui.tsx} +7 -4
- package/templates/Projects/default/src/ui/Settings/page-ui.tsx +17 -0
- package/templates/Projects/default/src/ui/UserInfo/page-ui.tsx +17 -0
- package/templates/Projects/default/src/ui/_global/BackButton.tsx +5 -3
- package/templates/Projects/default/src/ui/_global/Button.tsx +75 -0
- package/templates/Projects/default/src/ui/_global/GlobalHeader.tsx +42 -21
- package/templates/Projects/default/src/ui/_global/GlobalMain.tsx +3 -3
- package/templates/Projects/default/src/ui/_global/Loading.tsx +13 -0
- package/templates/Projects/default/src/ui/_global/LocaleSwitcher.tsx +1 -1
- package/templates/Projects/default/src/ui/_global/PublicNav.tsx +74 -13
- package/templates/Projects/default/src/ui/_global/ThemeToggle.tsx +53 -0
- package/templates/Projects/default/src/ui/_global/UserNav.tsx +8 -5
- package/templates/Projects/default/src/ui/_home/page-ui.tsx +27 -0
- package/templates/Projects/default/public/cnp-logo.ico +0 -0
- package/templates/Projects/default/public/file.svg +0 -1
- package/templates/Projects/default/public/globe.svg +0 -1
- package/templates/Projects/default/public/next.svg +0 -1
- package/templates/Projects/default/public/vercel.svg +0 -1
- package/templates/Projects/default/public/window.svg +0 -1
- package/templates/Projects/default/src/app/[locale]/(user)/dashboard/loading.tsx +0 -17
- package/templates/Projects/default/src/app/[locale]/(user)/dashboard/page.tsx +0 -12
- package/templates/Projects/default/src/app/[locale]/(user)/settings/page.tsx +0 -12
- package/templates/Projects/default/src/app/[locale]/(user)/user_info/page.tsx +0 -12
- package/templates/Projects/default/src/app/api/auth/[...nextauth].ts +0 -0
- package/templates/Projects/default/src/app/api/me/route.ts +0 -15
- /package/templates/Projects/default/messages/en/{login.json → Login.json} +0 -0
- /package/templates/Projects/default/messages/en/{register.json → Register.json} +0 -0
- /package/templates/Projects/default/messages/en/{settings.json → Settings.json} +0 -0
- /package/templates/Projects/default/messages/en/{user_info.json → UserInfo.json} +0 -0
- /package/templates/Projects/default/messages/fr/{login.json → Login.json} +0 -0
- /package/templates/Projects/default/messages/fr/{register.json → Register.json} +0 -0
- /package/templates/Projects/default/messages/fr/{settings.json → Settings.json} +0 -0
- /package/templates/Projects/default/messages/fr/{user_info.json → UserInfo.json} +0 -0
- /package/templates/Projects/default/src/app/[locale]/(user)/{dashboard → Dashboard}/error.tsx +0 -0
- /package/templates/Projects/default/src/app/[locale]/(user)/{settings → Settings}/loading.tsx +0 -0
- /package/templates/Projects/default/src/app/[locale]/(user)/{user_info → UserInfo}/loading.tsx +0 -0
- /package/templates/Projects/default/src/ui/{dashboard → Dashboard}/StatsCard.tsx +0 -0
- /package/templates/Projects/default/src/ui/{dashboard → Dashboard}/WelcomeCard.tsx +0 -0
|
@@ -1,26 +1,305 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
--
|
|
11
|
-
--
|
|
12
|
-
--
|
|
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
|
-
|
|
16
|
-
:
|
|
17
|
-
--background: #0a0a0a;
|
|
18
|
-
--foreground: #ededed;
|
|
19
|
-
}
|
|
56
|
+
* {
|
|
57
|
+
border-color: hsl(var(--border));
|
|
20
58
|
}
|
|
21
59
|
|
|
22
60
|
body {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
7
|
-
if (
|
|
8
|
+
const accessToken = cookieStore.get("access_token")?.value;
|
|
9
|
+
if (accessToken) {
|
|
8
10
|
try {
|
|
9
|
-
verify(
|
|
11
|
+
verify(accessToken, process.env.APP_JWT_SECRET!);
|
|
10
12
|
return true;
|
|
11
13
|
} catch {
|
|
12
14
|
return false;
|
|
@@ -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/
|
|
5
|
-
import StatsCard from "@/ui/
|
|
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("
|
|
9
|
+
const t = useTranslations("Dashboard");
|
|
9
10
|
return (
|
|
10
11
|
<>
|
|
11
12
|
<div className="flex items-center justify-between mb-6">
|
package/templates/Projects/default/src/{app/[locale]/(public)/login/page.tsx → ui/Login/page-ui.tsx}
RENAMED
|
@@ -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
|
|
6
|
-
const t = useTranslations("
|
|
7
|
+
export default function LoginPageUI() {
|
|
8
|
+
const t = useTranslations("Login");
|
|
9
|
+
|
|
7
10
|
return (
|
|
8
|
-
<
|
|
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
|
-
</
|
|
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
|
|
7
|
-
const t = useTranslations("
|
|
8
|
+
export default function RegisterPageUI() {
|
|
9
|
+
const t = useTranslations("Register");
|
|
10
|
+
|
|
8
11
|
return (
|
|
9
|
-
<
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
<
|
|
13
|
-
</
|
|
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
|
-
|
|
8
|
-
}: {
|
|
9
|
-
|
|
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="
|
|
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
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
</
|
|
12
|
+
</div>
|
|
13
13
|
</main>
|
|
14
14
|
);
|
|
15
15
|
}
|