create-next-pro-cli 0.1.25 → 0.1.27
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/README.md +205 -292
- package/create-next-pro-completion.sh +8 -38
- package/create-next-pro-completion.zsh +13 -0
- package/dist/bin.bun.js +1127 -628
- package/dist/bin.node.js +1170 -617
- package/dist/bin.node.js.map +1 -1
- package/dist/create-next-pro +35 -6
- package/package.json +49 -27
- package/templates/Projects/default/.env.example +17 -0
- package/templates/Projects/default/.github/workflows/quality.yml +24 -0
- package/templates/Projects/default/.gitignore.template +47 -0
- package/templates/Projects/default/.prettierignore +3 -0
- package/templates/Projects/default/README.md +66 -21
- package/templates/Projects/default/bun.lock +1152 -0
- package/templates/Projects/default/eslint.config.mjs +27 -11
- package/templates/Projects/default/messages/en/_global_ui.json +23 -10
- package/templates/Projects/default/messages/en.ts +17 -2
- package/templates/Projects/default/messages/fr/_global_ui.json +23 -10
- package/templates/Projects/default/messages/fr.ts +17 -2
- package/templates/Projects/default/next.config.ts +43 -3
- package/templates/Projects/default/package.json +42 -24
- package/templates/Projects/default/playwright.config.ts +26 -0
- package/templates/Projects/default/pnpm-workspace.yaml +5 -0
- package/templates/Projects/default/public/{cnp-logo.svg → logo.svg} +1 -1
- package/templates/Projects/default/src/app/[locale]/(public)/layout.tsx +8 -1
- package/templates/Projects/default/src/app/[locale]/(public)/login/page.tsx +8 -0
- package/templates/Projects/default/src/app/[locale]/(public)/register/page.tsx +8 -0
- package/templates/Projects/default/src/app/[locale]/(user)/dashboard/error.tsx +25 -0
- package/templates/Projects/default/src/app/[locale]/(user)/dashboard/loading.tsx +5 -0
- package/templates/Projects/default/src/app/[locale]/(user)/{Dashboard → dashboard}/page.tsx +1 -1
- package/templates/Projects/default/src/app/[locale]/(user)/layout.tsx +24 -2
- package/templates/Projects/default/src/app/[locale]/(user)/settings/loading.tsx +5 -0
- package/templates/Projects/default/src/app/[locale]/(user)/{Settings → settings}/page.tsx +1 -2
- package/templates/Projects/default/src/app/[locale]/(user)/userInfo/loading.tsx +5 -0
- package/templates/Projects/default/src/app/[locale]/(user)/{UserInfo → userInfo}/page.tsx +1 -2
- package/templates/Projects/default/src/app/[locale]/layout.tsx +34 -10
- package/templates/Projects/default/src/app/[locale]/loading.tsx +0 -9
- package/templates/Projects/default/src/app/[locale]/not-found.tsx +6 -15
- package/templates/Projects/default/src/app/[locale]/page.tsx +10 -1
- package/templates/Projects/default/src/app/api/auth/[...nextauth]/route.ts +8 -60
- package/templates/Projects/default/src/app/not-found.tsx +1 -1
- package/templates/Projects/default/src/app/sitemap.ts +2 -2
- package/templates/Projects/default/src/app/styles/globals.css +166 -113
- package/templates/Projects/default/src/auth.ts +20 -0
- package/templates/Projects/default/src/config.ts +3 -3
- package/templates/Projects/default/src/env.ts +65 -0
- package/templates/Projects/default/src/lib/i18n/messages.ts +8 -0
- package/templates/Projects/default/src/lib/i18n/request.ts +2 -16
- package/templates/Projects/default/src/lib/security/csp.ts +16 -0
- package/templates/Projects/default/src/lib/utils.ts +2 -1
- package/templates/Projects/default/src/proxy.ts +13 -0
- package/templates/Projects/default/src/ui/_global/BackButton.tsx +4 -2
- package/templates/Projects/default/src/ui/_global/Button.tsx +3 -8
- package/templates/Projects/default/src/ui/_global/GlobalHeader.tsx +10 -28
- package/templates/Projects/default/src/ui/_global/GlobalMain.tsx +1 -1
- package/templates/Projects/default/src/ui/_global/LocaleSwitcher.tsx +9 -10
- package/templates/Projects/default/src/ui/_global/PublicNav.tsx +51 -17
- package/templates/Projects/default/src/ui/_global/ThemeToggle.tsx +45 -39
- package/templates/Projects/default/src/ui/_global/UserNav.tsx +6 -6
- package/templates/Projects/default/src/ui/_home/page-ui.tsx +5 -7
- package/templates/Projects/default/src/ui/{Dashboard → dashboard}/LogoutButton.tsx +9 -7
- package/templates/Projects/default/src/ui/{Dashboard → dashboard}/StatsCard.tsx +4 -4
- package/templates/Projects/default/src/ui/{Dashboard → dashboard}/page-ui.tsx +7 -8
- package/templates/Projects/default/src/ui/login/page-ui.tsx +36 -0
- package/templates/Projects/default/src/ui/register/page-ui.tsx +38 -0
- package/templates/Projects/default/src/ui/settings/page-ui.tsx +15 -0
- package/templates/Projects/default/src/ui/userInfo/page-ui.tsx +15 -0
- package/templates/Projects/default/tailwind.config.ts +81 -1
- package/templates/Projects/default/tests/consumer/validate-template.ts +66 -0
- package/templates/Projects/default/tests/e2e/template-remediation.playwright.ts +106 -0
- package/templates/Projects/default/tests/rendering/verify-rendering.ts +56 -0
- package/templates/Projects/default/tests/unit/csp.test.ts +19 -0
- package/templates/Projects/default/tests/unit/env.test.ts +76 -0
- package/templates/Projects/default/tsconfig.json +6 -6
- package/templates/Projects/default/messages/getMergedMessages.ts +0 -31
- package/templates/Projects/default/middleware.ts +0 -11
- package/templates/Projects/default/src/app/[locale]/(public)/Login/page.tsx +0 -6
- package/templates/Projects/default/src/app/[locale]/(public)/Register/page.tsx +0 -6
- package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/error.tsx +0 -38
- package/templates/Projects/default/src/app/[locale]/(user)/Dashboard/loading.tsx +0 -10
- package/templates/Projects/default/src/app/[locale]/(user)/Settings/loading.tsx +0 -17
- package/templates/Projects/default/src/app/[locale]/(user)/UserInfo/loading.tsx +0 -17
- package/templates/Projects/default/src/app/api/auth/post-login/route.ts +0 -26
- package/templates/Projects/default/src/app/api/hello/route.ts +0 -5
- package/templates/Projects/default/src/app/layout.tsx +0 -11
- package/templates/Projects/default/src/app/page.tsx +0 -6
- package/templates/Projects/default/src/auth.config.ts +0 -0
- package/templates/Projects/default/src/lib/auth/disconnect.ts +0 -11
- package/templates/Projects/default/src/lib/auth/isConnected.ts +0 -18
- package/templates/Projects/default/src/lib/sample/example.ts +0 -3
- package/templates/Projects/default/src/lib/sample/index.ts +0 -3
- package/templates/Projects/default/src/ui/Login/page-ui.tsx +0 -22
- package/templates/Projects/default/src/ui/Register/page-ui.tsx +0 -26
- package/templates/Projects/default/src/ui/Settings/page-ui.tsx +0 -17
- package/templates/Projects/default/src/ui/UserInfo/page-ui.tsx +0 -17
- /package/templates/Projects/default/messages/en/{Dashboard.json → dashboard.json} +0 -0
- /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/{UserInfo.json → userInfo.json} +0 -0
- /package/templates/Projects/default/messages/fr/{Dashboard.json → dashboard.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/{UserInfo.json → userInfo.json} +0 -0
- /package/templates/Projects/default/public/{cnp-logo.png → logo.png} +0 -0
- /package/templates/Projects/default/src/ui/{Dashboard → dashboard}/WelcomeCard.tsx +0 -0
|
@@ -1,62 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
3
|
-
NextAuthOptions,
|
|
4
|
-
Session,
|
|
5
|
-
User,
|
|
6
|
-
Account,
|
|
7
|
-
Profile,
|
|
8
|
-
} from "next-auth";
|
|
9
|
-
import GoogleProvider from "next-auth/providers/google";
|
|
10
|
-
import type { JWT } from "next-auth/jwt";
|
|
1
|
+
import { handlers, isAuthConfigured } from "@/auth";
|
|
11
2
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}),
|
|
18
|
-
],
|
|
19
|
-
callbacks: {
|
|
20
|
-
async jwt({ token, user }: { token: JWT; user?: User }) {
|
|
21
|
-
if (user) {
|
|
22
|
-
token.id = user.id;
|
|
23
|
-
token.email = user.email;
|
|
24
|
-
token.name = user.name;
|
|
25
|
-
}
|
|
26
|
-
console.log("JWT Callback:", token);
|
|
3
|
+
const unavailable = () =>
|
|
4
|
+
Response.json(
|
|
5
|
+
{ error: "Authentication is not configured for this deployment." },
|
|
6
|
+
{ status: 503 },
|
|
7
|
+
);
|
|
27
8
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
async session({ session, token }: { session: Session; token: JWT }) {
|
|
31
|
-
if (session.user) {
|
|
32
|
-
session.user.email = token.email;
|
|
33
|
-
session.user.name = token.name;
|
|
34
|
-
}
|
|
35
|
-
return session;
|
|
36
|
-
},
|
|
37
|
-
async signIn({
|
|
38
|
-
user,
|
|
39
|
-
account,
|
|
40
|
-
profile,
|
|
41
|
-
email,
|
|
42
|
-
credentials,
|
|
43
|
-
}: {
|
|
44
|
-
user: User;
|
|
45
|
-
account: Account | null;
|
|
46
|
-
profile?: Profile;
|
|
47
|
-
email?: string | { verificationRequest?: boolean };
|
|
48
|
-
credentials?: Record<string, unknown>;
|
|
49
|
-
}) {
|
|
50
|
-
return true;
|
|
51
|
-
},
|
|
52
|
-
async redirect({ url, baseUrl }: { url: string; baseUrl: string }) {
|
|
53
|
-
const customCallbackRoute = "/api/auth/post-login";
|
|
54
|
-
|
|
55
|
-
return baseUrl + customCallbackRoute;
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const handler = NextAuth(authOptions);
|
|
61
|
-
|
|
62
|
-
export { handler as GET, handler as POST };
|
|
9
|
+
export const GET = isAuthConfigured() ? handlers.GET : unavailable;
|
|
10
|
+
export const POST = isAuthConfigured() ? handlers.POST : unavailable;
|
|
@@ -5,7 +5,7 @@ import { routing } from "@/lib/i18n/routing";
|
|
|
5
5
|
import { getPathname } from "@/lib/i18n/navigation";
|
|
6
6
|
|
|
7
7
|
export default function sitemap(): MetadataRoute.Sitemap {
|
|
8
|
-
return [
|
|
8
|
+
return ["/", "/login", "/register"].flatMap((href) => getEntries(href));
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
type Href = Parameters<typeof getPathname>[0]["href"];
|
|
@@ -15,7 +15,7 @@ function getEntries(href: Href) {
|
|
|
15
15
|
url: getUrl(href, locale),
|
|
16
16
|
alternates: {
|
|
17
17
|
languages: Object.fromEntries(
|
|
18
|
-
routing.locales.map((cur) => [cur, getUrl(href, cur)])
|
|
18
|
+
routing.locales.map((cur) => [cur, getUrl(href, cur)]),
|
|
19
19
|
),
|
|
20
20
|
},
|
|
21
21
|
}));
|
|
@@ -1,68 +1,76 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
|
-
|
|
3
|
-
@tailwind base;
|
|
4
|
-
@tailwind components;
|
|
5
|
-
@tailwind utilities;
|
|
2
|
+
@config "../../../tailwind.config.ts";
|
|
6
3
|
|
|
7
4
|
/* @import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap"); */
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
.
|
|
11
|
-
|
|
12
|
-
|
|
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%;
|
|
6
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
7
|
+
@custom-variant light (&:where(.light, .light *));
|
|
8
|
+
|
|
9
|
+
:root {
|
|
30
10
|
--radius: 0.75rem;
|
|
31
11
|
}
|
|
32
12
|
|
|
33
|
-
/* Light mode :
|
|
13
|
+
/* Light mode : background white, text black */
|
|
14
|
+
:root,
|
|
34
15
|
.light {
|
|
35
|
-
--background:
|
|
36
|
-
--foreground: 0 0
|
|
37
|
-
--card:
|
|
38
|
-
--card-foreground: 0 0
|
|
39
|
-
--popover:
|
|
40
|
-
--popover-foreground: 0 0
|
|
41
|
-
--primary: 0 0
|
|
42
|
-
--primary-foreground:
|
|
43
|
-
--secondary:
|
|
44
|
-
--secondary-foreground: 0 0
|
|
45
|
-
--muted:
|
|
46
|
-
--muted-foreground:
|
|
47
|
-
--accent:
|
|
48
|
-
--accent-foreground: 0 0
|
|
49
|
-
--destructive:
|
|
50
|
-
--destructive-foreground:
|
|
51
|
-
--border:
|
|
52
|
-
--input:
|
|
53
|
-
--ring: 0 0
|
|
16
|
+
--background: 245 245 245; /* white*/
|
|
17
|
+
--foreground: 0 0 0; /* black */
|
|
18
|
+
--card: 255 255 255;
|
|
19
|
+
--card-foreground: 0 0 0;
|
|
20
|
+
--popover: 255 255 255;
|
|
21
|
+
--popover-foreground: 0 0 0;
|
|
22
|
+
--primary: 0 0 0; /* black */
|
|
23
|
+
--primary-foreground: 255 255 255; /* white */
|
|
24
|
+
--secondary: 220 220 220; /* very light gray */
|
|
25
|
+
--secondary-foreground: 0 0 0;
|
|
26
|
+
--muted: 242 242 242;
|
|
27
|
+
--muted-foreground: 51 51 51; /* dark gray */
|
|
28
|
+
--accent: 242 242 242;
|
|
29
|
+
--accent-foreground: 0 0 0;
|
|
30
|
+
--destructive: 239 68 68;
|
|
31
|
+
--destructive-foreground: 255 255 255;
|
|
32
|
+
--border: 204 204 204;
|
|
33
|
+
--input: 204 204 204;
|
|
34
|
+
--ring: 0 0 0;
|
|
35
|
+
--radius: 0.75rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Dark mode : background black, text white */
|
|
39
|
+
.dark {
|
|
40
|
+
--background: 0 0 0; /* black */
|
|
41
|
+
--foreground: 255 255 255; /* white */
|
|
42
|
+
--card: 0 0 0;
|
|
43
|
+
--card-foreground: 255 255 255;
|
|
44
|
+
--popover: 0 0 0;
|
|
45
|
+
--popover-foreground: 255 255 255;
|
|
46
|
+
--primary: 255 255 255; /* white */
|
|
47
|
+
--primary-foreground: 0 0 0; /* black */
|
|
48
|
+
--secondary: 30 30 30; /* dark gray */
|
|
49
|
+
--secondary-foreground: 255 255 255;
|
|
50
|
+
--muted: 51 51 51; /* dark gray */
|
|
51
|
+
--muted-foreground: 204 204 204;
|
|
52
|
+
--accent: 51 51 51; /* dark gray */
|
|
53
|
+
--accent-foreground: 255 255 255;
|
|
54
|
+
--destructive: 127 29 29;
|
|
55
|
+
--destructive-foreground: 255 255 255;
|
|
56
|
+
--border: 51 51 51;
|
|
57
|
+
--input: 51 51 51;
|
|
58
|
+
--ring: 255 255 255;
|
|
59
|
+
--radius: 0.75rem;
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
* {
|
|
57
|
-
border-color:
|
|
63
|
+
border-color: rgb(var(--border));
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
body {
|
|
61
67
|
font-family: "Inter", sans-serif;
|
|
62
|
-
background:
|
|
63
|
-
color:
|
|
68
|
+
background: rgb(var(--background));
|
|
69
|
+
color: rgb(var(--foreground));
|
|
64
70
|
line-height: 1.6;
|
|
65
|
-
transition:
|
|
71
|
+
transition:
|
|
72
|
+
background 0.3s,
|
|
73
|
+
color 0.3s;
|
|
66
74
|
}
|
|
67
75
|
|
|
68
76
|
.gradient-text {
|
|
@@ -84,51 +92,10 @@ body {
|
|
|
84
92
|
border: 1px solid #444;
|
|
85
93
|
}
|
|
86
94
|
.light .glass-effect {
|
|
87
|
-
background: rgba(
|
|
95
|
+
background: rgba(255, 255, 255, 0.8);
|
|
88
96
|
border: 1px solid #ccc;
|
|
89
97
|
}
|
|
90
98
|
|
|
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
99
|
@keyframes typing {
|
|
133
100
|
0%,
|
|
134
101
|
80%,
|
|
@@ -142,31 +109,116 @@ body {
|
|
|
142
109
|
}
|
|
143
110
|
}
|
|
144
111
|
|
|
112
|
+
/* 1) Déclarer des variables animables (pour interpolation fluide) */
|
|
113
|
+
@property --c1x {
|
|
114
|
+
syntax: "<percentage>";
|
|
115
|
+
inherits: true;
|
|
116
|
+
initial-value: 25%;
|
|
117
|
+
}
|
|
118
|
+
@property --c1y {
|
|
119
|
+
syntax: "<percentage>";
|
|
120
|
+
inherits: true;
|
|
121
|
+
initial-value: 25%;
|
|
122
|
+
}
|
|
123
|
+
@property --c2x {
|
|
124
|
+
syntax: "<percentage>";
|
|
125
|
+
inherits: true;
|
|
126
|
+
initial-value: 75%;
|
|
127
|
+
}
|
|
128
|
+
@property --c2y {
|
|
129
|
+
syntax: "<percentage>";
|
|
130
|
+
inherits: true;
|
|
131
|
+
initial-value: 75%;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* alpha (0..1) */
|
|
135
|
+
@property --a1 {
|
|
136
|
+
syntax: "<number>";
|
|
137
|
+
inherits: true;
|
|
138
|
+
initial-value: 0.9;
|
|
139
|
+
}
|
|
140
|
+
@property --a2 {
|
|
141
|
+
syntax: "<number>";
|
|
142
|
+
inherits: true;
|
|
143
|
+
initial-value: 0.9;
|
|
144
|
+
}
|
|
145
|
+
|
|
145
146
|
.hero-pattern {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
/* 2) Animation légère */
|
|
148
|
+
animation: heroDrift 60s ease-in-out infinite;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* 3) Keyframes: déplacement de quelques % + alpha léger */
|
|
152
|
+
@keyframes heroDrift {
|
|
153
|
+
0% {
|
|
154
|
+
--c1x: 20%;
|
|
155
|
+
--c1y: 25%;
|
|
156
|
+
--c2x: 75%;
|
|
157
|
+
--c2y: 75%;
|
|
158
|
+
--a1: 0.9;
|
|
159
|
+
--a2: 0.99;
|
|
160
|
+
}
|
|
161
|
+
50% {
|
|
162
|
+
--c1x: 28%;
|
|
163
|
+
--c1y: 60%;
|
|
164
|
+
--c2x: 65%;
|
|
165
|
+
--c2y: 45%;
|
|
166
|
+
--a1: 0.99;
|
|
167
|
+
--a2: 0.95;
|
|
168
|
+
}
|
|
169
|
+
100% {
|
|
170
|
+
--c1x: 20%;
|
|
171
|
+
--c1y: 25%;
|
|
172
|
+
--c2x: 75%;
|
|
173
|
+
--c2y: 75%;
|
|
174
|
+
--a1: 0.9;
|
|
175
|
+
--a2: 0.99;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* 4) Tu reconstruis ensuite tes gradients en utilisant ces variables */
|
|
180
|
+
|
|
181
|
+
/* DARK */
|
|
182
|
+
.dark .hero-pattern {
|
|
183
|
+
background-image:
|
|
184
|
+
radial-gradient(
|
|
185
|
+
circle at var(--c1x) var(--c1y),
|
|
186
|
+
rgba(0, 0, 0, var(--a1)) 0%,
|
|
187
|
+
transparent 80%
|
|
150
188
|
),
|
|
151
189
|
radial-gradient(
|
|
152
|
-
circle at
|
|
153
|
-
rgba(
|
|
154
|
-
transparent
|
|
190
|
+
circle at var(--c2x) var(--c2y),
|
|
191
|
+
rgba(31, 31, 31, var(--a2)) 0%,
|
|
192
|
+
transparent 80%
|
|
155
193
|
);
|
|
156
194
|
}
|
|
195
|
+
|
|
196
|
+
/* LIGHT (mêmes positions animées, mais alpha + stops adaptés) */
|
|
157
197
|
.light .hero-pattern {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
198
|
+
/* option: alpha un peu plus bas en light */
|
|
199
|
+
--a1: 0.1;
|
|
200
|
+
--a2: 0.5;
|
|
201
|
+
|
|
202
|
+
background-image:
|
|
203
|
+
radial-gradient(
|
|
204
|
+
circle at var(--c1x) var(--c1y),
|
|
205
|
+
rgba(150, 150, 150, var(--a1)) 0%,
|
|
206
|
+
transparent 40%
|
|
162
207
|
),
|
|
163
208
|
radial-gradient(
|
|
164
|
-
circle at
|
|
165
|
-
rgba(
|
|
209
|
+
circle at var(--c2x) var(--c2y),
|
|
210
|
+
rgba(150, 150, 150, var(--a2)) 0%,
|
|
166
211
|
transparent 50%
|
|
167
212
|
);
|
|
168
213
|
}
|
|
169
214
|
|
|
215
|
+
/* 5) Accessibilité */
|
|
216
|
+
@media (prefers-reduced-motion: reduce) {
|
|
217
|
+
.hero-pattern {
|
|
218
|
+
animation: none;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
170
222
|
.light .mobil-menu {
|
|
171
223
|
background: rgba(255, 255, 255, 0.9);
|
|
172
224
|
}
|
|
@@ -175,16 +227,12 @@ body {
|
|
|
175
227
|
background: rgba(0, 0, 0, 0.9);
|
|
176
228
|
}
|
|
177
229
|
|
|
178
|
-
.light .text-white,
|
|
230
|
+
/* .light .text-white,
|
|
179
231
|
.light .text-gray-300,
|
|
180
232
|
.light .text-gray-400 {
|
|
181
233
|
color: black;
|
|
182
|
-
}
|
|
234
|
+
} */
|
|
183
235
|
|
|
184
|
-
.light button.text-white:hover,
|
|
185
|
-
.light a:hover {
|
|
186
|
-
color: rgb(142, 197, 255);
|
|
187
|
-
}
|
|
188
236
|
.gsi-material-button {
|
|
189
237
|
user-select: none;
|
|
190
238
|
-webkit-user-select: none;
|
|
@@ -212,7 +260,10 @@ body {
|
|
|
212
260
|
padding: 0 12px;
|
|
213
261
|
position: relative;
|
|
214
262
|
text-align: center;
|
|
215
|
-
transition:
|
|
263
|
+
transition:
|
|
264
|
+
background-color 0.218s,
|
|
265
|
+
border-color 0.218s,
|
|
266
|
+
box-shadow 0.218s;
|
|
216
267
|
vertical-align: middle;
|
|
217
268
|
white-space: nowrap;
|
|
218
269
|
width: auto;
|
|
@@ -293,9 +344,11 @@ body {
|
|
|
293
344
|
}
|
|
294
345
|
|
|
295
346
|
.gsi-material-button:not(:disabled):hover {
|
|
296
|
-
-webkit-box-shadow:
|
|
347
|
+
-webkit-box-shadow:
|
|
348
|
+
0 1px 2px 0 rgba(60, 64, 67, 0.3),
|
|
297
349
|
0 1px 3px 1px rgba(60, 64, 67, 0.15);
|
|
298
|
-
box-shadow:
|
|
350
|
+
box-shadow:
|
|
351
|
+
0 1px 2px 0 rgba(60, 64, 67, 0.3),
|
|
299
352
|
0 1px 3px 1px rgba(60, 64, 67, 0.15);
|
|
300
353
|
}
|
|
301
354
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import NextAuth from "next-auth";
|
|
2
|
+
import Google from "next-auth/providers/google";
|
|
3
|
+
import { getAuthEnv } from "@/env";
|
|
4
|
+
|
|
5
|
+
const authEnv = getAuthEnv();
|
|
6
|
+
|
|
7
|
+
export const { handlers, auth, signIn, signOut } = NextAuth({
|
|
8
|
+
secret: authEnv?.secret,
|
|
9
|
+
trustHost: process.env.AUTH_TRUST_HOST === "true",
|
|
10
|
+
providers: authEnv
|
|
11
|
+
? [
|
|
12
|
+
Google({
|
|
13
|
+
clientId: authEnv.googleClientId,
|
|
14
|
+
clientSecret: authEnv.googleClientSecret,
|
|
15
|
+
}),
|
|
16
|
+
]
|
|
17
|
+
: [],
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const isAuthConfigured = () => authEnv !== null;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const env = process.env;
|
|
2
|
+
|
|
3
|
+
function firstDefined(...names: string[]) {
|
|
4
|
+
for (const name of names) {
|
|
5
|
+
const value = env[name];
|
|
6
|
+
if (value && value.trim().length > 0) {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function requireEnv(...names: string[]) {
|
|
13
|
+
const value = firstDefined(...names);
|
|
14
|
+
if (!value) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Missing required environment variable: ${names.join(" or ")}`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type AuthEnv = {
|
|
23
|
+
secret: string;
|
|
24
|
+
googleClientId: string;
|
|
25
|
+
googleClientSecret: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function getAuthEnv() {
|
|
29
|
+
if (env.AUTH_DISABLED === "true") return null;
|
|
30
|
+
|
|
31
|
+
const values = {
|
|
32
|
+
secret: firstDefined("AUTH_SECRET", "NEXTAUTH_SECRET"),
|
|
33
|
+
googleClientId: firstDefined("AUTH_GOOGLE_ID", "GOOGLE_CLIENT_ID"),
|
|
34
|
+
googleClientSecret: firstDefined(
|
|
35
|
+
"AUTH_GOOGLE_SECRET",
|
|
36
|
+
"GOOGLE_CLIENT_SECRET",
|
|
37
|
+
),
|
|
38
|
+
};
|
|
39
|
+
const configuredValues = Object.values(values).filter(Boolean).length;
|
|
40
|
+
|
|
41
|
+
if (configuredValues === 0) return null;
|
|
42
|
+
if (configuredValues !== 3) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
"Incomplete Auth.js configuration: secret, Google client ID and Google client secret must be set together",
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return values as AuthEnv;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getPublicUrl() {
|
|
52
|
+
const configured = firstDefined(
|
|
53
|
+
"NEXT_PUBLIC_APP_URL",
|
|
54
|
+
"PROJECT_PRODUCTION_URL",
|
|
55
|
+
);
|
|
56
|
+
if (!configured) {
|
|
57
|
+
return `http://localhost:${env.PORT || "3000"}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const candidate = configured.startsWith("http")
|
|
61
|
+
? configured
|
|
62
|
+
: `https://${configured}`;
|
|
63
|
+
|
|
64
|
+
return new URL(candidate).origin;
|
|
65
|
+
}
|
|
@@ -2,30 +2,16 @@
|
|
|
2
2
|
import { getRequestConfig } from "next-intl/server";
|
|
3
3
|
import { hasLocale } from "next-intl";
|
|
4
4
|
import { routing } from "./routing";
|
|
5
|
+
import { getMessages } from "./messages";
|
|
5
6
|
|
|
6
7
|
export default getRequestConfig(async ({ requestLocale }) => {
|
|
7
|
-
//console.log("Requesting locale... :", requestLocale);
|
|
8
|
-
// Typically corresponds to the `[locale]` segment
|
|
9
|
-
//console.log("Routing :", routing);
|
|
10
8
|
const requested = await requestLocale;
|
|
11
|
-
//console.log(`Requested locale: "${requested}"`);
|
|
12
9
|
const locale = hasLocale(routing.locales, requested)
|
|
13
10
|
? requested
|
|
14
11
|
: routing.defaultLocale;
|
|
15
12
|
|
|
16
|
-
//console.log(`Locale requested: "${locale}"`);
|
|
17
|
-
|
|
18
13
|
return {
|
|
19
14
|
locale,
|
|
20
|
-
messages: (
|
|
15
|
+
messages: getMessages(locale),
|
|
21
16
|
};
|
|
22
17
|
});
|
|
23
|
-
|
|
24
|
-
// This file is used to configure internationalization (i18n) for the Next.js application
|
|
25
|
-
// It imports the getRequestConfig function from next-intl/server to set up locale-specific messages
|
|
26
|
-
// The messages are loaded dynamically based on the requested locale
|
|
27
|
-
// The locales and defaultLocale are imported from the routing module
|
|
28
|
-
// This allows the application to support multiple languages, such as English and French
|
|
29
|
-
// The messages are expected to be in JSON format and located in the messages directory
|
|
30
|
-
// This setup is essential for providing localized content to users based on their language preferences
|
|
31
|
-
// Make sure to create the necessary message files for each locale in the specified directory
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function createContentSecurityPolicy(isDevelopment: boolean) {
|
|
2
|
+
return [
|
|
3
|
+
"default-src 'self'",
|
|
4
|
+
`script-src 'self' 'unsafe-inline'${isDevelopment ? " 'unsafe-eval'" : ""}`,
|
|
5
|
+
"style-src 'self' 'unsafe-inline'",
|
|
6
|
+
"img-src 'self' blob: data: https://lh3.googleusercontent.com",
|
|
7
|
+
"font-src 'self' data:",
|
|
8
|
+
"connect-src 'self' https://accounts.google.com",
|
|
9
|
+
"frame-src https://accounts.google.com",
|
|
10
|
+
"object-src 'none'",
|
|
11
|
+
"base-uri 'self'",
|
|
12
|
+
"form-action 'self' https://accounts.google.com",
|
|
13
|
+
"frame-ancestors 'none'",
|
|
14
|
+
"upgrade-insecure-requests",
|
|
15
|
+
].join("; ");
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import createMiddleware from "next-intl/middleware";
|
|
2
|
+
import type { NextRequest } from "next/server";
|
|
3
|
+
import { routing } from "@/lib/i18n/routing";
|
|
4
|
+
|
|
5
|
+
const handleI18nRouting = createMiddleware(routing);
|
|
6
|
+
|
|
7
|
+
export function proxy(request: NextRequest) {
|
|
8
|
+
return handleI18nRouting(request);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const config = {
|
|
12
|
+
matcher: ["/((?!api|_next|_vercel|.*\\..*).*)"],
|
|
13
|
+
};
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
"use client";
|
|
1
|
+
"use client";
|
|
2
|
+
import { useTranslations } from "next-intl";
|
|
2
3
|
import { useRouter } from "next/navigation";
|
|
3
4
|
import { StepBackIcon } from "lucide-react";
|
|
4
5
|
import { Button } from "./Button";
|
|
5
6
|
|
|
6
7
|
export default function BackButton({ className = "" }: { className?: string }) {
|
|
7
8
|
const router = useRouter();
|
|
9
|
+
const t = useTranslations("_global_ui");
|
|
8
10
|
return (
|
|
9
11
|
<Button
|
|
10
12
|
type="button"
|
|
11
13
|
onClick={() => router.back()}
|
|
12
14
|
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}`}
|
|
13
15
|
>
|
|
14
|
-
<StepBackIcon className="h-4 w-4" />
|
|
16
|
+
<StepBackIcon className="h-4 w-4" /> {t("back")}
|
|
15
17
|
</Button>
|
|
16
18
|
);
|
|
17
19
|
}
|
|
@@ -34,7 +34,7 @@ const buttonVariants = cva(
|
|
|
34
34
|
variant: "default",
|
|
35
35
|
size: "default",
|
|
36
36
|
},
|
|
37
|
-
}
|
|
37
|
+
},
|
|
38
38
|
);
|
|
39
39
|
|
|
40
40
|
import type { ButtonHTMLAttributes, DetailedHTMLProps, ReactNode } from "react";
|
|
@@ -42,12 +42,7 @@ import type { ButtonHTMLAttributes, DetailedHTMLProps, ReactNode } from "react";
|
|
|
42
42
|
type ButtonProps = {
|
|
43
43
|
className?: string;
|
|
44
44
|
variant?:
|
|
45
|
-
| "
|
|
46
|
-
| "destructive"
|
|
47
|
-
| "outline"
|
|
48
|
-
| "secondary"
|
|
49
|
-
| "ghost"
|
|
50
|
-
| "link";
|
|
45
|
+
"default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
|
|
51
46
|
size?: "default" | "sm" | "lg" | "icon";
|
|
52
47
|
asChild?: boolean;
|
|
53
48
|
children?: ReactNode;
|
|
@@ -68,7 +63,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
68
63
|
{children}
|
|
69
64
|
</Comp>
|
|
70
65
|
);
|
|
71
|
-
}
|
|
66
|
+
},
|
|
72
67
|
);
|
|
73
68
|
Button.displayName = "Button";
|
|
74
69
|
|