create-next-pro-cli 0.1.0
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/.vscode/settings.json +3 -0
- package/CODE_OF_CONDUCT.md +33 -0
- package/CONTRIBUTING.md +44 -0
- package/FONCTIONALITY.md +25 -0
- package/README.md +292 -0
- package/bin.ts +2 -0
- package/bun.lock +27 -0
- package/create-next-pro-completion.sh +36 -0
- package/install.sh +18 -0
- package/package.json +32 -0
- package/public/logo.svg +76 -0
- package/src/index.ts +511 -0
- package/src/scaffold-dev.ts +89 -0
- package/src/scaffold.ts +70 -0
- package/templates/Component/Component.tsx +13 -0
- package/templates/Component/component.json +4 -0
- package/templates/Page/default.tsx +0 -0
- package/templates/Page/error.tsx +38 -0
- package/templates/Page/global-error.tsx +0 -0
- package/templates/Page/layout.tsx +0 -0
- package/templates/Page/loading.tsx +17 -0
- package/templates/Page/not-found.tsx +0 -0
- package/templates/Page/page-ui.tsx +22 -0
- package/templates/Page/page.json +4 -0
- package/templates/Page/page.tsx +24 -0
- package/templates/Page/route.ts +0 -0
- package/templates/Page/template.tsx +0 -0
- package/templates/Projects/default/README.md +36 -0
- package/templates/Projects/default/eslint.config.mjs +16 -0
- package/templates/Projects/default/messages/en/_global_ui.json +30 -0
- package/templates/Projects/default/messages/en/_home.json +4 -0
- package/templates/Projects/default/messages/en/dashboard.json +14 -0
- package/templates/Projects/default/messages/en/login.json +7 -0
- package/templates/Projects/default/messages/en/register.json +7 -0
- package/templates/Projects/default/messages/en/settings.json +4 -0
- package/templates/Projects/default/messages/en/user_info.json +4 -0
- package/templates/Projects/default/messages/en.ts +4 -0
- package/templates/Projects/default/messages/fr/_global_ui.json +30 -0
- package/templates/Projects/default/messages/fr/_home.json +4 -0
- package/templates/Projects/default/messages/fr/dashboard.json +14 -0
- package/templates/Projects/default/messages/fr/login.json +7 -0
- package/templates/Projects/default/messages/fr/register.json +7 -0
- package/templates/Projects/default/messages/fr/settings.json +4 -0
- package/templates/Projects/default/messages/fr/user_info.json +4 -0
- package/templates/Projects/default/messages/fr.ts +4 -0
- package/templates/Projects/default/messages/getMergedMessages.ts +31 -0
- package/templates/Projects/default/middleware.ts +11 -0
- package/templates/Projects/default/next.config.ts +12 -0
- package/templates/Projects/default/package.json +32 -0
- package/templates/Projects/default/postcss.config.mjs +5 -0
- package/templates/Projects/default/public/cnp-logo.ico +0 -0
- package/templates/Projects/default/public/cnp-logo.png +0 -0
- package/templates/Projects/default/public/cnp-logo.svg +76 -0
- package/templates/Projects/default/public/file.svg +1 -0
- package/templates/Projects/default/public/globe.svg +1 -0
- package/templates/Projects/default/public/next.svg +1 -0
- package/templates/Projects/default/public/vercel.svg +1 -0
- package/templates/Projects/default/public/window.svg +1 -0
- package/templates/Projects/default/src/app/[locale]/(public)/_home/loading.tsx +1 -0
- package/templates/Projects/default/src/app/[locale]/(public)/_home/page.tsx +16 -0
- package/templates/Projects/default/src/app/[locale]/(public)/layout.tsx +11 -0
- package/templates/Projects/default/src/app/[locale]/(public)/login/page.tsx +19 -0
- package/templates/Projects/default/src/app/[locale]/(public)/register/page.tsx +23 -0
- package/templates/Projects/default/src/app/[locale]/(user)/dashboard/error.tsx +38 -0
- package/templates/Projects/default/src/app/[locale]/(user)/dashboard/loading.tsx +17 -0
- package/templates/Projects/default/src/app/[locale]/(user)/dashboard/page.tsx +12 -0
- package/templates/Projects/default/src/app/[locale]/(user)/layout.tsx +12 -0
- package/templates/Projects/default/src/app/[locale]/(user)/settings/loading.tsx +17 -0
- package/templates/Projects/default/src/app/[locale]/(user)/settings/page.tsx +12 -0
- package/templates/Projects/default/src/app/[locale]/(user)/user_info/loading.tsx +17 -0
- package/templates/Projects/default/src/app/[locale]/(user)/user_info/page.tsx +12 -0
- package/templates/Projects/default/src/app/[locale]/layout.tsx +32 -0
- package/templates/Projects/default/src/app/[locale]/loading.tsx +17 -0
- package/templates/Projects/default/src/app/[locale]/not-found.tsx +22 -0
- package/templates/Projects/default/src/app/[locale]/page.tsx +116 -0
- package/templates/Projects/default/src/app/api/auth/[...nextauth]/route.ts +81 -0
- package/templates/Projects/default/src/app/api/auth/[...nextauth].ts +0 -0
- package/templates/Projects/default/src/app/api/auth/post-login/route.ts +27 -0
- package/templates/Projects/default/src/app/api/me/route.ts +15 -0
- package/templates/Projects/default/src/app/favicon.ico +0 -0
- package/templates/Projects/default/src/app/layout.tsx +11 -0
- package/templates/Projects/default/src/app/not-found.tsx +17 -0
- package/templates/Projects/default/src/app/page.tsx +6 -0
- package/templates/Projects/default/src/app/sitemap.ts +27 -0
- package/templates/Projects/default/src/app/styles/globals.css +26 -0
- package/templates/Projects/default/src/app/ui/dashboard/MainDashboard.tsx +21 -0
- package/templates/Projects/default/src/app/ui/dashboard/StatsCard.tsx +14 -0
- package/templates/Projects/default/src/app/ui/dashboard/WelcomeCard.tsx +10 -0
- package/templates/Projects/default/src/auth.config.ts +0 -0
- package/templates/Projects/default/src/config.ts +4 -0
- package/templates/Projects/default/src/lib/auth/isConnected.ts +16 -0
- package/templates/Projects/default/src/lib/i18n/navigation.ts +19 -0
- package/templates/Projects/default/src/lib/i18n/request.ts +31 -0
- package/templates/Projects/default/src/lib/i18n/routing.ts +20 -0
- package/templates/Projects/default/src/ui/_global/BackButton.tsx +15 -0
- package/templates/Projects/default/src/ui/_global/GlobalHeader.tsx +34 -0
- package/templates/Projects/default/src/ui/_global/GlobalMain.tsx +15 -0
- package/templates/Projects/default/src/ui/_global/LocaleSwitcher.tsx +38 -0
- package/templates/Projects/default/src/ui/_global/PublicNav.tsx +30 -0
- package/templates/Projects/default/src/ui/_global/UserNav.tsx +32 -0
- package/templates/Projects/default/src/ui/dashboard/StatsCard.tsx +14 -0
- package/templates/Projects/default/src/ui/dashboard/WelcomeCard.tsx +10 -0
- package/templates/Projects/default/src/ui/dashboard/page.tsx +22 -0
- package/templates/Projects/default/tailwind.config.ts +1 -0
- package/templates/Projects/default/tsconfig.json +34 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useTranslations } from "next-intl";
|
|
3
|
+
import { signIn } from "next-auth/react";
|
|
4
|
+
import BackButton from "@/ui/_global/BackButton";
|
|
5
|
+
|
|
6
|
+
export default function RegisterPage() {
|
|
7
|
+
const t = useTranslations("register");
|
|
8
|
+
return (
|
|
9
|
+
<main className="py-8 px-4 max-w-md mx-auto">
|
|
10
|
+
<div className="flex items-center justify-between mb-6">
|
|
11
|
+
<h1 className="text-2xl font-bold">{t("title")}</h1>
|
|
12
|
+
<BackButton />
|
|
13
|
+
</div>
|
|
14
|
+
<p className="text-muted mb-6">{t("description")}</p>
|
|
15
|
+
<button
|
|
16
|
+
onClick={() => signIn()}
|
|
17
|
+
className="w-full py-2 px-4 rounded bg-blue-600 text-white font-semibold hover:bg-blue-700 transition"
|
|
18
|
+
>
|
|
19
|
+
{t("register_with_google")}
|
|
20
|
+
</button>
|
|
21
|
+
</main>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// app/[locale]/error.tsx
|
|
2
|
+
"use client"; // Error boundaries must be Client Components
|
|
3
|
+
|
|
4
|
+
import { useEffect } from "react";
|
|
5
|
+
|
|
6
|
+
export default function Error({
|
|
7
|
+
error,
|
|
8
|
+
reset,
|
|
9
|
+
}: {
|
|
10
|
+
error: Error & { digest?: string };
|
|
11
|
+
reset: () => void;
|
|
12
|
+
}) {
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// Log the error to an error reporting service
|
|
15
|
+
console.error(error);
|
|
16
|
+
}, [error]);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div>
|
|
20
|
+
<h2>Something went wrong!</h2>
|
|
21
|
+
<button
|
|
22
|
+
onClick={
|
|
23
|
+
// Attempt to recover by trying to re-render the segment
|
|
24
|
+
() => reset()
|
|
25
|
+
}
|
|
26
|
+
>
|
|
27
|
+
Try again
|
|
28
|
+
</button>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
// This page is used to handle errors in the application
|
|
33
|
+
// You can customize this page to display a user-friendly error message
|
|
34
|
+
// or redirect to a different page if needed
|
|
35
|
+
// This is part of the Next.js app directory structure and will be automatically used by Next.js
|
|
36
|
+
// when an error occurs in the application
|
|
37
|
+
// Make sure to handle errors gracefully to improve user experience
|
|
38
|
+
// You can also log errors or send them to an error tracking service
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// app/[locale]/dashboard/loading.tsx
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
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 dashboard...</p>
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
// This component is used to show a loading state while the dashboard is being prepared
|
|
12
|
+
// You can customize it with a spinner, skeletons, or any loading UI you prefer
|
|
13
|
+
// It will be displayed when the dashboard is loading, for example during data fetching or component rendering
|
|
14
|
+
// This is useful for providing feedback to users that something is happening in the background
|
|
15
|
+
// You can also use this to implement a more complex loading state with animations or placeholders
|
|
16
|
+
// Make sure to keep it lightweight to avoid blocking the main thread
|
|
17
|
+
// This component is part of the Next.js app directory structure and will be automatically used by Next.js
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Dashboard from "@/ui/dashboard/page";
|
|
2
|
+
|
|
3
|
+
export default function DashboardPage() {
|
|
4
|
+
return <Dashboard />;
|
|
5
|
+
}
|
|
6
|
+
// This page is the main entry point for the dashboard
|
|
7
|
+
// It uses the `useTranslations` hook to fetch localized strings
|
|
8
|
+
// The `t` function is used to get the translated strings for the dashboard
|
|
9
|
+
// You can add more components or logic here to build your dashboard
|
|
10
|
+
// This is part of the Next.js app directory structure
|
|
11
|
+
// It will be automatically rendered when the user navigates to the dashboard
|
|
12
|
+
// Make sure to define the translations in your locale files
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// app/[locale]/(user)/layout.tsx
|
|
2
|
+
|
|
3
|
+
import UserNav from "@/ui/_global/UserNav";
|
|
4
|
+
import LocaleSwitcher from "@/ui/_global/LocaleSwitcher";
|
|
5
|
+
|
|
6
|
+
export default function UserLayout({
|
|
7
|
+
children,
|
|
8
|
+
}: {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
}) {
|
|
11
|
+
return children;
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// app/[locale]/dashboard/loading.tsx
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
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 user info...</p>
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
// This component is used to show a loading state while the dashboard is being prepared
|
|
12
|
+
// You can customize it with a spinner, skeletons, or any loading UI you prefer
|
|
13
|
+
// It will be displayed when the dashboard is loading, for example during data fetching or component rendering
|
|
14
|
+
// This is useful for providing feedback to users that something is happening in the background
|
|
15
|
+
// You can also use this to implement a more complex loading state with animations or placeholders
|
|
16
|
+
// Make sure to keep it lightweight to avoid blocking the main thread
|
|
17
|
+
// This component is part of the Next.js app directory structure and will be automatically used by Next.js
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useTranslations } from "next-intl";
|
|
3
|
+
|
|
4
|
+
export default function UserInfoPage() {
|
|
5
|
+
const t = useTranslations("settings");
|
|
6
|
+
return (
|
|
7
|
+
<main className="py-8 px-4 max-w-3xl mx-auto">
|
|
8
|
+
<h1 className="text-2xl font-bold">{t("title")}</h1>
|
|
9
|
+
<p className="text-muted mt-2">{t("description")}</p>
|
|
10
|
+
</main>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// app/[locale]/dashboard/loading.tsx
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
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 user info...</p>
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
// This component is used to show a loading state while the dashboard is being prepared
|
|
12
|
+
// You can customize it with a spinner, skeletons, or any loading UI you prefer
|
|
13
|
+
// It will be displayed when the dashboard is loading, for example during data fetching or component rendering
|
|
14
|
+
// This is useful for providing feedback to users that something is happening in the background
|
|
15
|
+
// You can also use this to implement a more complex loading state with animations or placeholders
|
|
16
|
+
// Make sure to keep it lightweight to avoid blocking the main thread
|
|
17
|
+
// This component is part of the Next.js app directory structure and will be automatically used by Next.js
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useTranslations } from "next-intl";
|
|
3
|
+
|
|
4
|
+
export default function UserInfoPage() {
|
|
5
|
+
const t = useTranslations("user_info");
|
|
6
|
+
return (
|
|
7
|
+
<main className="py-8 px-4 max-w-3xl mx-auto">
|
|
8
|
+
<h1 className="text-2xl font-bold">{t("title")}</h1>
|
|
9
|
+
<p className="text-muted mt-2">{t("description")}</p>
|
|
10
|
+
</main>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// app/[locale]/layout.tsx
|
|
2
|
+
import { setRequestLocale } from "next-intl/server";
|
|
3
|
+
import { NextIntlClientProvider, hasLocale, Locale } from "next-intl";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import "@/app/styles/globals.css"; // Import global styles
|
|
6
|
+
import GlobalHeader from "@/ui/_global/GlobalHeader";
|
|
7
|
+
import GlobalMain from "@/ui/_global/GlobalMain";
|
|
8
|
+
import { isConnected } from "@/lib/auth/isConnected";
|
|
9
|
+
|
|
10
|
+
export default async function LocaleLayout({
|
|
11
|
+
children,
|
|
12
|
+
params,
|
|
13
|
+
}: {
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
params: Promise<{ locale: string }>;
|
|
16
|
+
}) {
|
|
17
|
+
const { locale } = await params;
|
|
18
|
+
setRequestLocale(locale);
|
|
19
|
+
|
|
20
|
+
const isConnectedValue = await isConnected();
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<html lang={locale}>
|
|
24
|
+
<body>
|
|
25
|
+
<NextIntlClientProvider>
|
|
26
|
+
<GlobalHeader isConnected={isConnectedValue} />
|
|
27
|
+
<GlobalMain>{children}</GlobalMain>
|
|
28
|
+
</NextIntlClientProvider>
|
|
29
|
+
</body>
|
|
30
|
+
</html>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// app/[locale]/dashboard/loading.tsx
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
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
|
+
);
|
|
10
|
+
}
|
|
11
|
+
// This component is used to show a loading state while the dashboard is being prepared
|
|
12
|
+
// You can customize it with a spinner, skeletons, or any loading UI you prefer
|
|
13
|
+
// It will be displayed when the dashboard is loading, for example during data fetching or component rendering
|
|
14
|
+
// This is useful for providing feedback to users that something is happening in the background
|
|
15
|
+
// You can also use this to implement a more complex loading state with animations or placeholders
|
|
16
|
+
// Make sure to keep it lightweight to avoid blocking the main thread
|
|
17
|
+
// This component is part of the Next.js app directory structure and will be automatically used by Next.js
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// app/[locale]/not-found.tsx
|
|
2
|
+
import { Link } from "@/lib/i18n/navigation";
|
|
3
|
+
|
|
4
|
+
export default function NotFound() {
|
|
5
|
+
return (
|
|
6
|
+
<div>
|
|
7
|
+
<h2>Not Found</h2>
|
|
8
|
+
<p>Could not find requested resource</p>
|
|
9
|
+
<Link href="/">Return Home</Link>
|
|
10
|
+
</div>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
// This page is used to handle 404 errors in the application
|
|
14
|
+
// You can customize this page to display a user-friendly error message
|
|
15
|
+
// or redirect to a different page if needed
|
|
16
|
+
// This is part of the Next.js app directory structure and will be automatically used by Next.js
|
|
17
|
+
// when a requested page is not found
|
|
18
|
+
// Make sure to handle 404 errors gracefully to improve user experience
|
|
19
|
+
// You can also log errors or send them to an error tracking service
|
|
20
|
+
// This component can be styled with CSS or Tailwind CSS to match your application's design
|
|
21
|
+
// You can also add additional links or actions for the user to take, such as searching or navigating to other parts of the site
|
|
22
|
+
// This component is lightweight and does not block the main thread
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import Image from "next/image";
|
|
3
|
+
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
|
+
|
|
11
|
+
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
|
+
);
|
|
53
|
+
}
|
|
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.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { sign } from "jsonwebtoken";
|
|
2
|
+
import NextAuth from "next-auth";
|
|
3
|
+
//import { authOptions } from "@/auth.config";
|
|
4
|
+
import type {
|
|
5
|
+
NextAuthOptions,
|
|
6
|
+
Session,
|
|
7
|
+
User,
|
|
8
|
+
Account,
|
|
9
|
+
Profile,
|
|
10
|
+
} from "next-auth";
|
|
11
|
+
import GoogleProvider from "next-auth/providers/google";
|
|
12
|
+
import type { JWT } from "next-auth/jwt";
|
|
13
|
+
import { getToken } from "next-auth/jwt";
|
|
14
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
15
|
+
|
|
16
|
+
const authOptions: NextAuthOptions = {
|
|
17
|
+
providers: [
|
|
18
|
+
GoogleProvider({
|
|
19
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
20
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
21
|
+
}),
|
|
22
|
+
],
|
|
23
|
+
callbacks: {
|
|
24
|
+
async jwt({ token, user }: { token: JWT; user?: User }) {
|
|
25
|
+
if (user) {
|
|
26
|
+
token.id = user.id;
|
|
27
|
+
token.email = user.email;
|
|
28
|
+
token.name = user.name;
|
|
29
|
+
}
|
|
30
|
+
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";
|
|
41
|
+
return token;
|
|
42
|
+
},
|
|
43
|
+
async session({ session, token }: { session: Session; token: JWT }) {
|
|
44
|
+
// On ne met rien dans la session NextAuth
|
|
45
|
+
return session;
|
|
46
|
+
},
|
|
47
|
+
async signIn({
|
|
48
|
+
user,
|
|
49
|
+
account,
|
|
50
|
+
profile,
|
|
51
|
+
email,
|
|
52
|
+
credentials,
|
|
53
|
+
}: {
|
|
54
|
+
user: User;
|
|
55
|
+
account: Account | null;
|
|
56
|
+
profile?: Profile;
|
|
57
|
+
email?: string | { verificationRequest?: boolean };
|
|
58
|
+
credentials?: Record<string, unknown>;
|
|
59
|
+
}) {
|
|
60
|
+
return true;
|
|
61
|
+
},
|
|
62
|
+
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
|
+
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");
|
|
71
|
+
return baseUrl + customCallbackRoute;
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
// Ajoute ici d'autres options/callbacks si besoin
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Suppression de customJwtCookie : la pose du cookie se fait dans /api/auth/post-login
|
|
78
|
+
|
|
79
|
+
const handler = NextAuth(authOptions);
|
|
80
|
+
|
|
81
|
+
export { handler as GET, handler as POST };
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getToken } from "next-auth/jwt";
|
|
2
|
+
import { sign } from "jsonwebtoken";
|
|
3
|
+
import { NextResponse, NextRequest } from "next/server";
|
|
4
|
+
import { redirect } from "@/lib/i18n/navigation";
|
|
5
|
+
// import { NextURL } from "next/dist/server/web/next-url";
|
|
6
|
+
|
|
7
|
+
export async function GET(req: NextRequest) {
|
|
8
|
+
const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });
|
|
9
|
+
if (token) {
|
|
10
|
+
const appJwt = sign(
|
|
11
|
+
{ id: token.id, email: token.email, name: token.name },
|
|
12
|
+
process.env.APP_JWT_SECRET!,
|
|
13
|
+
{ expiresIn: "7d" }
|
|
14
|
+
);
|
|
15
|
+
const res = NextResponse.redirect(req.nextUrl.origin + "/");
|
|
16
|
+
res.cookies.set("app_token", appJwt, {
|
|
17
|
+
httpOnly: true,
|
|
18
|
+
secure: process.env.NODE_ENV === "production",
|
|
19
|
+
sameSite: "strict",
|
|
20
|
+
path: "/",
|
|
21
|
+
maxAge: 60 * 60 * 24 * 7,
|
|
22
|
+
});
|
|
23
|
+
return res;
|
|
24
|
+
}
|
|
25
|
+
// Si pas de token, redirige vers login
|
|
26
|
+
return redirect({ href: "/login", locale: "en" });
|
|
27
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { verify } from "jsonwebtoken";
|
|
3
|
+
|
|
4
|
+
export async function GET(req: NextRequest) {
|
|
5
|
+
const cookie = req.cookies.get("app_token")?.value;
|
|
6
|
+
if (!cookie) {
|
|
7
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const user = verify(cookie, process.env.APP_JWT_SECRET!);
|
|
11
|
+
return NextResponse.json({ user });
|
|
12
|
+
} catch {
|
|
13
|
+
return NextResponse.json({ error: "Invalid token" }, { status: 401 });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
type Props = {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// Since we have a `not-found.tsx` page on the root, a layout file
|
|
8
|
+
// is required, even if it's just passing children through.
|
|
9
|
+
export default function RootLayout({ children }: Props) {
|
|
10
|
+
return children;
|
|
11
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Error from "next/error";
|
|
4
|
+
|
|
5
|
+
// This page renders when a route like `/unknown.txt` is requested.
|
|
6
|
+
// In this case, the layout at `app/[locale]/layout.tsx` receives
|
|
7
|
+
// an invalid value as the `[locale]` param and calls `notFound()`.
|
|
8
|
+
|
|
9
|
+
export default function GlobalNotFound() {
|
|
10
|
+
return (
|
|
11
|
+
<html lang="en">
|
|
12
|
+
<body>
|
|
13
|
+
<Error statusCode={404} />;
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { MetadataRoute } from "next";
|
|
2
|
+
import { Locale } from "next-intl";
|
|
3
|
+
import { host } from "@/config";
|
|
4
|
+
import { routing } from "@/lib/i18n/routing";
|
|
5
|
+
import { getPathname } from "@/lib/i18n/navigation";
|
|
6
|
+
|
|
7
|
+
export default function sitemap(): MetadataRoute.Sitemap {
|
|
8
|
+
return [...getEntries("/"), ...getEntries("/pathnames")];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type Href = Parameters<typeof getPathname>[0]["href"];
|
|
12
|
+
|
|
13
|
+
function getEntries(href: Href) {
|
|
14
|
+
return routing.locales.map((locale) => ({
|
|
15
|
+
url: getUrl(href, locale),
|
|
16
|
+
alternates: {
|
|
17
|
+
languages: Object.fromEntries(
|
|
18
|
+
routing.locales.map((cur) => [cur, getUrl(href, cur)])
|
|
19
|
+
),
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getUrl(href: Href, locale: Locale) {
|
|
25
|
+
const pathname = getPathname({ locale, href });
|
|
26
|
+
return host + pathname;
|
|
27
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
--background: #ffffff;
|
|
5
|
+
--foreground: #171717;
|
|
6
|
+
}
|
|
7
|
+
|
|
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);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@media (prefers-color-scheme: dark) {
|
|
16
|
+
:root {
|
|
17
|
+
--background: #0a0a0a;
|
|
18
|
+
--foreground: #ededed;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
body {
|
|
23
|
+
background: var(--background);
|
|
24
|
+
color: var(--foreground);
|
|
25
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
26
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useTranslations } from "next-intl";
|
|
2
|
+
import BackButton from "@/ui/_global/BackButton";
|
|
3
|
+
import WelcomeCard from "@/ui/dashboard/WelcomeCard";
|
|
4
|
+
import StatsCard from "@/ui/dashboard/StatsCard";
|
|
5
|
+
|
|
6
|
+
export default function DashboardPage() {
|
|
7
|
+
const t = useTranslations("dashboard");
|
|
8
|
+
return (
|
|
9
|
+
<main className="py-8 px-4 max-w-3xl mx-auto">
|
|
10
|
+
<div className="flex items-center justify-between mb-6">
|
|
11
|
+
<h1 className="text-2xl font-bold">{t("title")}</h1>
|
|
12
|
+
<BackButton />
|
|
13
|
+
</div>
|
|
14
|
+
<p className="text-muted mb-8">{t("description")}</p>
|
|
15
|
+
<section className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
16
|
+
<WelcomeCard t={t} />
|
|
17
|
+
<StatsCard t={t} />
|
|
18
|
+
</section>
|
|
19
|
+
</main>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export default function StatsCard({ t }: { t: (key: string) => string }) {
|
|
4
|
+
return (
|
|
5
|
+
<div className="rounded border bg-white p-6 shadow-sm">
|
|
6
|
+
<h2 className="text-lg font-semibold mb-2">Statistiques</h2>
|
|
7
|
+
<ul className="text-sm text-gray-600 space-y-1">
|
|
8
|
+
<li>• {t("widgets.stats.views")}: 123</li>
|
|
9
|
+
<li>• {t("widgets.stats.likes")}: 45</li>
|
|
10
|
+
<li>• {t("widgets.stats.comments")}: 7</li>
|
|
11
|
+
</ul>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export default function WelcomeCard({ t }: { t: (key: string) => string }) {
|
|
4
|
+
return (
|
|
5
|
+
<div className="rounded border bg-white p-6 shadow-sm">
|
|
6
|
+
<h2 className="text-lg font-semibold mb-2">{t("widgets.welcome")}</h2>
|
|
7
|
+
<p className="text-gray-600">{t("widgets.description")}</p>
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
File without changes
|