@lastbrain/app 0.1.8 → 0.1.10

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 (39) hide show
  1. package/dist/scripts/init-app.js +7 -10
  2. package/package.json +3 -2
  3. package/src/app-shell/(admin)/layout.tsx +13 -0
  4. package/src/app-shell/(auth)/layout.tsx +13 -0
  5. package/src/app-shell/(public)/page.tsx +11 -0
  6. package/src/app-shell/layout.tsx +5 -0
  7. package/src/app-shell/not-found.tsx +28 -0
  8. package/src/auth/authHelpers.ts +24 -0
  9. package/src/auth/useAuthSession.ts +54 -0
  10. package/src/cli.ts +96 -0
  11. package/src/index.ts +21 -0
  12. package/src/layouts/AdminLayout.tsx +7 -0
  13. package/src/layouts/AppProviders.tsx +61 -0
  14. package/src/layouts/AuthLayout.tsx +7 -0
  15. package/src/layouts/PublicLayout.tsx +7 -0
  16. package/src/layouts/RootLayout.tsx +27 -0
  17. package/src/modules/module-loader.ts +14 -0
  18. package/src/scripts/README.md +262 -0
  19. package/src/scripts/db-init.ts +338 -0
  20. package/src/scripts/db-migrations-sync.ts +86 -0
  21. package/src/scripts/dev-sync.ts +218 -0
  22. package/src/scripts/init-app.ts +1076 -0
  23. package/src/scripts/module-add.ts +242 -0
  24. package/src/scripts/module-build.ts +502 -0
  25. package/src/scripts/module-create.ts +809 -0
  26. package/src/scripts/module-list.ts +37 -0
  27. package/src/scripts/module-remove.ts +367 -0
  28. package/src/scripts/readme-build.ts +60 -0
  29. package/src/styles.css +3 -0
  30. package/src/templates/AuthGuidePage.tsx +68 -0
  31. package/src/templates/DefaultDoc.tsx +462 -0
  32. package/src/templates/DocPage.tsx +381 -0
  33. package/src/templates/DocsPageWithModules.tsx +22 -0
  34. package/src/templates/MigrationsGuidePage.tsx +61 -0
  35. package/src/templates/ModuleGuidePage.tsx +71 -0
  36. package/src/templates/SimpleDocPage.tsx +587 -0
  37. package/src/templates/SimpleHomePage.tsx +385 -0
  38. package/src/templates/env.example/.env.example +6 -0
  39. package/src/templates/migrations/20201010100000_app_base.sql +228 -0
@@ -240,8 +240,8 @@ async function addDependencies(targetDir, useHeroUI, withAuth, selectedModules =
240
240
  }
241
241
  // DevDependencies
242
242
  const requiredDevDeps = {
243
- "@tailwindcss/postcss": "^4.0.0",
244
- tailwindcss: "^4.0.0",
243
+ tailwindcss: "^3.4.0",
244
+ postcss: "^8.4.0",
245
245
  autoprefixer: "^10.4.20",
246
246
  typescript: "^5.4.0",
247
247
  "@types/node": "^20.0.0",
@@ -270,8 +270,6 @@ async function createNextStructure(targetDir, force, useHeroUI, withAuth) {
270
270
  "use client";
271
271
 
272
272
  import "../styles/globals.css";
273
- import "@lastbrain/ui/dist/styles.css";
274
- import "@lastbrain/app/dist/styles.css";
275
273
  import { HeroUIProvider } from "@heroui/system";
276
274
 
277
275
  import { ThemeProvider } from "next-themes";
@@ -351,8 +349,9 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
351
349
  // Créer globals.css
352
350
  const globalsPath = path.join(stylesDir, "globals.css");
353
351
  if (!fs.existsSync(globalsPath) || force) {
354
- const globalsContent = `@config "../tailwind.config.mjs";
355
- @import "tailwindcss";
352
+ const globalsContent = `@tailwind base;
353
+ @tailwind components;
354
+ @tailwind utilities;
356
355
  `;
357
356
  await fs.writeFile(globalsPath, globalsContent);
358
357
  console.log(chalk.green("✓ styles/globals.css créé"));
@@ -678,14 +677,12 @@ export default config;
678
677
  // postcss.config.mjs
679
678
  const postcssConfigPath = path.join(targetDir, "postcss.config.mjs");
680
679
  if (!fs.existsSync(postcssConfigPath) || force) {
681
- const postcssConfig = `const config = {
680
+ const postcssConfig = `export default {
682
681
  plugins: {
683
- '@tailwindcss/postcss': {},
682
+ tailwindcss: {},
684
683
  autoprefixer: {},
685
684
  },
686
685
  };
687
-
688
- export default config;
689
686
  `;
690
687
  await fs.writeFile(postcssConfigPath, postcssConfig);
691
688
  console.log(chalk.green("✓ postcss.config.mjs créé"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lastbrain/app",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Framework modulaire Next.js avec CLI et système de modules",
5
5
  "private": false,
6
6
  "type": "module",
@@ -27,7 +27,8 @@
27
27
  "lastbrain": "./dist/cli.js"
28
28
  },
29
29
  "files": [
30
- "dist"
30
+ "dist",
31
+ "src"
31
32
  ],
32
33
  "dependencies": {
33
34
  "@lastbrain/core": "0.1.0",
@@ -0,0 +1,13 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+
5
+ export default function AdminShellLayout({ children }: PropsWithChildren<{}>) {
6
+ return (
7
+ <div className="mx-auto max-w-5xl px-4 py-16">
8
+ <h1 className="text-3xl font-bold">Section Admin</h1>
9
+ <p className="text-slate-600">Gestion avancée des modules.</p>
10
+ {children}
11
+ </div>
12
+ );
13
+ }
@@ -0,0 +1,13 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+
5
+ export default function AuthLayout({ children }: PropsWithChildren<{}>) {
6
+ return (
7
+ <div className="mx-auto max-w-5xl px-4 py-16">
8
+ <h1 className="text-3xl font-bold">Section Auth</h1>
9
+ <p className="text-slate-600">Les pages authentifiées regroupées ici.</p>
10
+ {children}
11
+ </div>
12
+ );
13
+ }
@@ -0,0 +1,11 @@
1
+ "use client";
2
+
3
+ export default function PublicPage() {
4
+ return (
5
+ <div className="mx-auto max-w-3xl space-y-4 px-4 py-16 text-center">
6
+ <h1 className="text-3xl font-bold">Bienvenue sur LastBrain</h1>
7
+ <p className="text-slate-600">Cette coquille publique présente la structure par défaut.</p>
8
+ <button className="rounded-full bg-slate-900 px-5 py-2 text-white">Connexion</button>
9
+ </div>
10
+ );
11
+ }
@@ -0,0 +1,5 @@
1
+ "use client";
2
+
3
+ import { RootLayout } from "../layouts/RootLayout.js";
4
+
5
+ export default RootLayout;
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { Button } from "@lastbrain/ui";
3
+ import { useRouter } from "next/navigation";
4
+
5
+ export default function NotFound() {
6
+ const router = useRouter();
7
+ return (
8
+ <div className="flex min-h-screen items-center justify-center bg-background">
9
+ <div className="mx-auto max-w-md text-center">
10
+ <h1 className="mb-4 text-6xl font-bold text-foreground">404</h1>
11
+ <h2 className="mb-4 text-2xl font-semibold text-foreground">
12
+ Page non trouvée
13
+ </h2>
14
+ <p className="mb-8 text-muted-foreground">
15
+ La page que vous recherchez n'existe pas ou a été déplacée.
16
+ </p>
17
+ <Button
18
+ onPress={() => {
19
+ router.back();
20
+ }}
21
+ className="inline-flex items-center justify-center rounded-md bg-primary px-6 py-3 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90"
22
+ >
23
+ Retour à l'accueil
24
+ </Button>
25
+ </div>
26
+ </div>
27
+ );
28
+ }
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ import { supabaseBrowserClient } from "@lastbrain/core";
4
+
5
+ export async function signOut() {
6
+ const { error } = await supabaseBrowserClient.auth.signOut();
7
+ if (error) {
8
+ console.error("Error signing out:", error);
9
+ throw error;
10
+ }
11
+ }
12
+
13
+ export async function signIn(email: string, password: string) {
14
+ const { data, error } = await supabaseBrowserClient.auth.signInWithPassword({
15
+ email,
16
+ password,
17
+ });
18
+
19
+ if (error) {
20
+ throw error;
21
+ }
22
+
23
+ return data;
24
+ }
@@ -0,0 +1,54 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { supabaseBrowserClient } from "@lastbrain/core";
5
+ import type { User } from "@supabase/supabase-js";
6
+
7
+ export function useAuthSession() {
8
+ const [user, setUser] = useState<User | null>(null);
9
+ const [loading, setLoading] = useState(true);
10
+ const [isSuperAdmin, setIsSuperAdmin] = useState(false);
11
+
12
+ useEffect(() => {
13
+ const checkSuperAdmin = async (userId: string) => {
14
+ try {
15
+ const { data, error } = await supabaseBrowserClient.rpc(
16
+ "is_superadmin",
17
+ { user_id: userId }
18
+ );
19
+ if (!error && data) {
20
+ setIsSuperAdmin(data);
21
+ }
22
+ } catch (error) {
23
+ console.error("Error checking superadmin status:", error);
24
+ setIsSuperAdmin(false);
25
+ }
26
+ };
27
+
28
+ // Récupérer la session initiale
29
+ supabaseBrowserClient.auth.getSession().then(({ data: { session } }) => {
30
+ setUser(session?.user ?? null);
31
+ if (session?.user) {
32
+ checkSuperAdmin(session.user.id);
33
+ }
34
+ setLoading(false);
35
+ });
36
+
37
+ // Écouter les changements d'auth
38
+ const {
39
+ data: { subscription },
40
+ } = supabaseBrowserClient.auth.onAuthStateChange((_event, session) => {
41
+ setUser(session?.user ?? null);
42
+ if (session?.user) {
43
+ checkSuperAdmin(session.user.id);
44
+ } else {
45
+ setIsSuperAdmin(false);
46
+ }
47
+ setLoading(false);
48
+ });
49
+
50
+ return () => subscription.unsubscribe();
51
+ }, []);
52
+
53
+ return { user, loading, isSuperAdmin };
54
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import path from "path";
5
+ import { initApp } from "./scripts/init-app.js";
6
+ import { addModule } from "./scripts/module-add.js";
7
+ import { removeModule } from "./scripts/module-remove.js";
8
+ import { listModules } from "./scripts/module-list.js";
9
+ import { createModule } from "./scripts/module-create.js";
10
+
11
+ const program = new Command();
12
+
13
+ program
14
+ .name("lastbrain")
15
+ .description("CLI pour créer et gérer des applications LastBrain")
16
+ .version("0.1.0");
17
+
18
+ program
19
+ .command("init [directory]")
20
+ .description("Initialise une nouvelle application Next.js LastBrain")
21
+ .option("-f, --force", "Écrase les fichiers existants")
22
+ .option("--no-heroui", "Ne pas utiliser HeroUI (Tailwind CSS uniquement)")
23
+ .option("--with-auth", "Inclure le module d'authentification")
24
+ .option(
25
+ "--no-interactive",
26
+ "Mode non-interactif (skip la sélection des modules)"
27
+ )
28
+ .action(async (directory: string | undefined, options) => {
29
+ try {
30
+ const targetDir = directory
31
+ ? path.resolve(process.cwd(), directory)
32
+ : process.cwd();
33
+
34
+ await initApp({
35
+ force: options.force || false,
36
+ targetDir,
37
+ projectName: directory || path.basename(process.cwd()),
38
+ useHeroUI: options.heroui !== false, // Par défaut true, false si --no-heroui
39
+ withAuth: options.withAuth || false,
40
+ interactive: options.interactive !== false,
41
+ });
42
+ } catch (error) {
43
+ console.error("❌ Erreur lors de l'initialisation:", error);
44
+ process.exit(1);
45
+ }
46
+ });
47
+
48
+ program
49
+ .command("add-module <module>")
50
+ .description("Ajoute un module à l'application")
51
+ .action(async (moduleName: string) => {
52
+ try {
53
+ await addModule(moduleName, process.cwd());
54
+ } catch (error) {
55
+ console.error("❌ Erreur lors de l'ajout du module:", error);
56
+ process.exit(1);
57
+ }
58
+ });
59
+
60
+ program
61
+ .command("remove-module <module>")
62
+ .description("Supprime un module de l'application")
63
+ .action(async (moduleName: string) => {
64
+ try {
65
+ await removeModule(moduleName, process.cwd());
66
+ } catch (error) {
67
+ console.error("❌ Erreur lors de la suppression du module:", error);
68
+ process.exit(1);
69
+ }
70
+ });
71
+
72
+ program
73
+ .command("list-modules")
74
+ .description("Liste les modules disponibles et installés")
75
+ .action(async () => {
76
+ try {
77
+ await listModules(process.cwd());
78
+ } catch (error) {
79
+ console.error("❌ Erreur lors de la liste des modules:", error);
80
+ process.exit(1);
81
+ }
82
+ });
83
+
84
+ program
85
+ .command("create-module")
86
+ .description("Crée un nouveau module dans packages/")
87
+ .action(async () => {
88
+ try {
89
+ await createModule();
90
+ } catch (error) {
91
+ console.error("❌ Erreur lors de la création du module:", error);
92
+ process.exit(1);
93
+ }
94
+ });
95
+
96
+ program.parse();
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ export {
2
+ AppProviders,
3
+ useAuth,
4
+ useModules,
5
+ useNotifications,
6
+ } from "./layouts/AppProviders.js";
7
+ export { useAuthSession } from "./auth/useAuthSession.js";
8
+ export { signOut, signIn } from "./auth/authHelpers.js";
9
+ export { RootLayout } from "./layouts/RootLayout.js";
10
+ export { PublicLayout } from "./layouts/PublicLayout.js";
11
+ export { AuthLayout } from "./layouts/AuthLayout.js";
12
+ export { AdminLayout } from "./layouts/AdminLayout.js";
13
+ export { getModuleConfigs } from "./modules/module-loader.js";
14
+ // Templates de pages (starter + docs)
15
+
16
+ export { SimpleHomePage } from "./templates/SimpleHomePage.js";
17
+ export { DocPage } from "./templates/DocPage.js";
18
+ export { SimpleDocPage } from "./templates/SimpleDocPage.js";
19
+ export { ModuleGuidePage } from "./templates/ModuleGuidePage.js";
20
+ export { AuthGuidePage } from "./templates/AuthGuidePage.js";
21
+ export { MigrationsGuidePage } from "./templates/MigrationsGuidePage.js";
@@ -0,0 +1,7 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+
5
+ export function AdminLayout({ children }: PropsWithChildren<{}>) {
6
+ return <div className="pt-18 px-2 md:px-5">{children}</div>;
7
+ }
@@ -0,0 +1,61 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+ import { createContext, useContext, useMemo } from "react";
5
+ import { getModuleConfigs } from "../modules/module-loader.js";
6
+ import { Header, ToastProvider } from "@lastbrain/ui";
7
+ import { useAuthSession } from "../auth/useAuthSession.js";
8
+ import type { User } from "@supabase/supabase-js";
9
+ import { useRouter } from "next/navigation.js";
10
+
11
+ const ModuleContext = createContext(getModuleConfigs());
12
+ const NotificationContext = createContext({ messages: [] as string[] });
13
+ const AuthContext = createContext<{
14
+ user: User | null;
15
+ loading: boolean;
16
+ isSuperAdmin: boolean;
17
+ }>({
18
+ user: null,
19
+ loading: true,
20
+ isSuperAdmin: false,
21
+ });
22
+
23
+ export function useModules() {
24
+ return useContext(ModuleContext);
25
+ }
26
+
27
+ export function useNotifications() {
28
+ return useContext(NotificationContext);
29
+ }
30
+
31
+ export function useAuth() {
32
+ return useContext(AuthContext);
33
+ }
34
+
35
+ export function AppProviders({ children }: PropsWithChildren<{}>) {
36
+ const modules = useMemo(() => getModuleConfigs(), []);
37
+ const notifications = useMemo(() => ({ messages: [] as string[] }), []);
38
+ const { user, loading, isSuperAdmin } = useAuthSession();
39
+ // const router = useRouter();
40
+ const authValue = useMemo(
41
+ () => ({ user, loading, isSuperAdmin }),
42
+ [user, loading, isSuperAdmin]
43
+ );
44
+
45
+ // const handleLogout = async () => {
46
+ // const { signOut } = await import("../auth/authHelpers.js");
47
+ // await signOut();
48
+ // router.push("/auth/signin");
49
+ // };
50
+
51
+ return (
52
+ <AuthContext.Provider value={authValue}>
53
+ <ModuleContext.Provider value={modules}>
54
+ <NotificationContext.Provider value={notifications}>
55
+ {children}
56
+ <ToastProvider placement="top-right" toastOffset={75} />
57
+ </NotificationContext.Provider>
58
+ </ModuleContext.Provider>
59
+ </AuthContext.Provider>
60
+ );
61
+ }
@@ -0,0 +1,7 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+
5
+ export function AuthLayout({ children }: PropsWithChildren<{}>) {
6
+ return <div className="pt-18 px-2 md:px-5 ">{children}</div>;
7
+ }
@@ -0,0 +1,7 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+
5
+ export function PublicLayout({ children }: PropsWithChildren<{}>) {
6
+ return <section className=" px-4 ">{children}</section>;
7
+ }
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import type { PropsWithChildren } from "react";
4
+ import { ThemeProvider } from "next-themes";
5
+ import { AppProviders } from "./AppProviders.js";
6
+
7
+ // Note: L'app Next.js doit importer son propre globals.css dans son layout
8
+ export function RootLayout({ children }: PropsWithChildren<{}>) {
9
+ return (
10
+ <html lang="fr" suppressHydrationWarning>
11
+ <body className="min-h-screen">
12
+ <ThemeProvider
13
+ attribute="class"
14
+ defaultTheme="light"
15
+ enableSystem={false}
16
+ storageKey="lastbrain-theme"
17
+ >
18
+ <AppProviders>
19
+ <div className=" min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white">
20
+ {children}
21
+ </div>
22
+ </AppProviders>
23
+ </ThemeProvider>
24
+ </body>
25
+ </html>
26
+ );
27
+ }
@@ -0,0 +1,14 @@
1
+ import type { ModuleBuildConfig } from "@lastbrain/core";
2
+
3
+ // Ce fichier est utilisé côté client par AppProviders
4
+ // Le chargement des modules se fait uniquement au build-time dans module-build.ts
5
+ // Côté client, on exporte un tableau vide car les modules sont déjà compilés
6
+ export const moduleConfigs: ModuleBuildConfig[] = [];
7
+
8
+ export function getModuleConfigs() {
9
+ return moduleConfigs;
10
+ }
11
+
12
+ export function registerModuleConfig(config: ModuleBuildConfig) {
13
+ moduleConfigs.push(config);
14
+ }