@lastbrain/app 0.1.42 → 0.1.44

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 (55) hide show
  1. package/dist/__tests__/module-registry.test.js +1 -1
  2. package/dist/layouts/AdminLayout.d.ts.map +1 -1
  3. package/dist/layouts/AdminLayout.js +0 -1
  4. package/dist/layouts/AdminLayoutWithSidebar.d.ts +2 -1
  5. package/dist/layouts/AdminLayoutWithSidebar.d.ts.map +1 -1
  6. package/dist/layouts/AdminLayoutWithSidebar.js +10 -4
  7. package/dist/layouts/AuthLayout.d.ts.map +1 -1
  8. package/dist/layouts/AuthLayout.js +0 -1
  9. package/dist/layouts/AuthLayoutWithSidebar.d.ts +2 -1
  10. package/dist/layouts/AuthLayoutWithSidebar.d.ts.map +1 -1
  11. package/dist/layouts/AuthLayoutWithSidebar.js +10 -4
  12. package/dist/layouts/PublicLayout.d.ts +6 -2
  13. package/dist/layouts/PublicLayout.d.ts.map +1 -1
  14. package/dist/layouts/PublicLayout.js +4 -3
  15. package/dist/layouts/PublicLayoutWithSidebar.d.ts +2 -1
  16. package/dist/layouts/PublicLayoutWithSidebar.d.ts.map +1 -1
  17. package/dist/layouts/PublicLayoutWithSidebar.js +10 -4
  18. package/dist/scripts/db-migrations-sync.js +67 -38
  19. package/dist/scripts/dev-sync.js +1 -0
  20. package/dist/scripts/init-app.d.ts.map +1 -1
  21. package/dist/scripts/init-app.js +172 -59
  22. package/dist/scripts/module-build.d.ts.map +1 -1
  23. package/dist/scripts/module-build.js +67 -6
  24. package/dist/scripts/module-create.d.ts.map +1 -1
  25. package/dist/scripts/module-create.js +19 -0
  26. package/dist/scripts/module-delete.d.ts.map +1 -1
  27. package/dist/scripts/module-delete.js +6 -2
  28. package/dist/scripts/module-list.d.ts.map +1 -1
  29. package/dist/scripts/module-list.js +1 -1
  30. package/dist/styles.css +1 -1
  31. package/dist/templates/DefaultDoc.d.ts.map +1 -1
  32. package/dist/templates/DefaultDoc.js +6 -13
  33. package/dist/templates/DocPage.d.ts.map +1 -1
  34. package/dist/templates/DocPage.js +3 -5
  35. package/dist/templates/SimpleDocPage.js +2 -2
  36. package/dist/templates/SimpleHomePage.js +1 -1
  37. package/package.json +7 -2
  38. package/src/__tests__/module-registry.test.ts +1 -1
  39. package/src/layouts/AdminLayout.tsx +0 -2
  40. package/src/layouts/AdminLayoutWithSidebar.tsx +13 -6
  41. package/src/layouts/AuthLayout.tsx +0 -2
  42. package/src/layouts/AuthLayoutWithSidebar.tsx +13 -6
  43. package/src/layouts/PublicLayout.tsx +14 -2
  44. package/src/layouts/PublicLayoutWithSidebar.tsx +13 -6
  45. package/src/scripts/db-migrations-sync.ts +86 -57
  46. package/src/scripts/dev-sync.ts +1 -0
  47. package/src/scripts/init-app.ts +191 -58
  48. package/src/scripts/module-build.ts +77 -6
  49. package/src/scripts/module-create.ts +25 -2
  50. package/src/scripts/module-delete.ts +8 -6
  51. package/src/scripts/module-list.ts +1 -4
  52. package/src/templates/DefaultDoc.tsx +346 -455
  53. package/src/templates/DocPage.tsx +40 -22
  54. package/src/templates/SimpleDocPage.tsx +2 -2
  55. package/src/templates/SimpleHomePage.tsx +3 -3
@@ -53,7 +53,7 @@ export async function initApp(options) {
53
53
  // 3. Créer la structure Next.js
54
54
  await createNextStructure(targetDir, force, useHeroUI, withAuth);
55
55
  // 4. Créer les fichiers de configuration
56
- await createConfigFiles(targetDir, force, useHeroUI);
56
+ await createConfigFiles(targetDir, force, useHeroUI, projectName);
57
57
  // 5. Créer le système de proxy storage
58
58
  await createStorageProxy(targetDir, force);
59
59
  // 6. Créer .gitignore et .env.local.example
@@ -90,6 +90,17 @@ export async function initApp(options) {
90
90
  console.log(chalk.yellow("🔧 Génération des routes des modules...\n"));
91
91
  execSync("pnpm build:modules", { cwd: targetDir, stdio: "inherit" });
92
92
  console.log(chalk.green("\n✓ Routes des modules générées\n"));
93
+ console.log(chalk.yellow("📜 Synchronisation des migrations des modules...\n"));
94
+ try {
95
+ execSync("pnpm db:migrations:sync", {
96
+ cwd: targetDir,
97
+ stdio: "inherit",
98
+ });
99
+ console.log(chalk.green("\n✓ Migrations synchronisées\n"));
100
+ }
101
+ catch (error) {
102
+ console.log(chalk.yellow("\n⚠️ Erreur de synchronisation des migrations\n"));
103
+ }
93
104
  console.log(chalk.yellow("🗄️ Initialisation de la base de données...\n"));
94
105
  try {
95
106
  execSync("pnpm db:init", { cwd: targetDir, stdio: "inherit" });
@@ -132,6 +143,7 @@ export async function initApp(options) {
132
143
  console.log(chalk.white(` cd ${relativePath}`));
133
144
  console.log(chalk.white(" pnpm install"));
134
145
  console.log(chalk.white(" pnpm build:modules"));
146
+ console.log(chalk.white(" pnpm db:migrations:sync"));
135
147
  console.log(chalk.white(" pnpm db:init"));
136
148
  console.log(chalk.white(" pnpm dev\n"));
137
149
  }
@@ -141,10 +153,9 @@ export async function initApp(options) {
141
153
  console.log(chalk.white(" 1. cd " + relativePath));
142
154
  console.log(chalk.white(" 2. pnpm install (installer les dépendances)"));
143
155
  console.log(chalk.white(" 3. pnpm build:modules (générer les routes des modules)"));
144
- console.log(chalk.white(" 4. pnpm db:init (initialiser la base de données)"));
145
- console.log(chalk.white(" 5. pnpm dev (lancer le serveur)"));
146
- console.log(chalk.white(" 4. pnpm db:init (initialiser Supabase)"));
147
- console.log(chalk.white(" 5. pnpm dev (démarrer le serveur)\n"));
156
+ console.log(chalk.white(" 4. pnpm db:migrations:sync (synchroniser les migrations)"));
157
+ console.log(chalk.white(" 5. pnpm db:init (initialiser la base de données)"));
158
+ console.log(chalk.white(" 6. pnpm dev (lancer le serveur)\n"));
148
159
  console.log(chalk.gray("Prérequis pour Supabase :"));
149
160
  console.log(chalk.white(" - Docker Desktop installé et lancé"));
150
161
  console.log(chalk.white(" - Supabase CLI : brew install supabase/tap/supabase\n"));
@@ -359,41 +370,19 @@ async function createNextStructure(targetDir, force, useHeroUI, withAuth) {
359
370
  if (!fs.existsSync(layoutDest) || force) {
360
371
  let layoutContent = "";
361
372
  if (useHeroUI) {
362
- // Layout avec HeroUI
373
+ // Layout avec HeroUI - Server Component
363
374
  layoutContent = `// GENERATED BY LASTBRAIN APP-SHELL
364
-
365
- "use client";
375
+ // Server Component pour permettre le SSR des pages enfants
366
376
 
367
377
  import "../styles/globals.css";
368
- import { HeroUIProvider } from "@heroui/system";
369
-
370
- import { ThemeProvider } from "next-themes";
371
- import { useRouter } from "next/navigation";
372
- import { AppProviders } from "../components/AppProviders";
378
+ import { ClientLayout } from "../components/ClientLayout";
373
379
  import type { PropsWithChildren } from "react";
374
- import { AppHeader } from "../components/AppHeader";
375
380
 
376
381
  export default function RootLayout({ children }: PropsWithChildren<{}>) {
377
- const router = useRouter();
378
-
379
382
  return (
380
383
  <html lang="fr" suppressHydrationWarning>
381
384
  <body className="min-h-screen">
382
- <HeroUIProvider navigate={router.push}>
383
- <ThemeProvider
384
- attribute="class"
385
- defaultTheme="dark"
386
- enableSystem={false}
387
- storageKey="lastbrain-theme"
388
- >
389
- <AppProviders>
390
- <AppHeader />
391
- <div className="min-h-screen text-foreground bg-background">
392
- {children}
393
- </div>
394
- </AppProviders>
395
- </ThemeProvider>
396
- </HeroUIProvider>
385
+ <ClientLayout>{children}</ClientLayout>
397
386
  </body>
398
387
  </html>
399
388
  );
@@ -401,34 +390,19 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
401
390
  `;
402
391
  }
403
392
  else {
404
- // Layout Tailwind CSS uniquement
393
+ // Layout Tailwind CSS uniquement - Server Component
405
394
  layoutContent = `// GENERATED BY LASTBRAIN APP-SHELL
406
-
407
- "use client";
395
+ // Server Component pour permettre le SSR des pages enfants
408
396
 
409
397
  import "../styles/globals.css";
410
- import { ThemeProvider } from "next-themes";
411
- import { AppProviders } from "../components/AppProviders";
398
+ import { ClientLayout } from "../components/ClientLayout";
412
399
  import type { PropsWithChildren } from "react";
413
- import { AppHeader } from "../components/AppHeader";
414
400
 
415
401
  export default function RootLayout({ children }: PropsWithChildren<{}>) {
416
402
  return (
417
403
  <html lang="fr" suppressHydrationWarning>
418
404
  <body className="min-h-screen">
419
- <ThemeProvider
420
- attribute="class"
421
- defaultTheme="light"
422
- enableSystem={false}
423
- storageKey="lastbrain-theme"
424
- >
425
- <AppProviders>
426
- <AppHeader />
427
- <div className="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white">
428
- {children}
429
- </div>
430
- </AppProviders>
431
- </ThemeProvider>
405
+ <ClientLayout>{children}</ClientLayout>
432
406
  </body>
433
407
  </html>
434
408
  );
@@ -457,9 +431,16 @@ export default function RootLayout({ children }: PropsWithChildren<{}>) {
457
431
  const homePageContent = `// GENERATED BY LASTBRAIN APP-SHELL
458
432
 
459
433
  import { SimpleHomePage } from "@lastbrain/app";
434
+ import { Footer } from "@lastbrain/ui";
435
+ import { footerConfig } from "../config/footer";
460
436
 
461
437
  export default function RootPage() {
462
- return <SimpleHomePage showAuth={${withAuth}} />;
438
+ return (
439
+ <>
440
+ <SimpleHomePage showAuth={${withAuth}} />
441
+ <Footer config={footerConfig} />
442
+ </>
443
+ );
463
444
  }
464
445
  `;
465
446
  await fs.writeFile(homePagePath, homePageContent);
@@ -504,6 +485,8 @@ export default function NotFound() {
504
485
  await createRoute(appDir, "admin", "admin", force);
505
486
  await createRoute(appDir, "auth", "auth", force);
506
487
  await createRoute(appDir, "docs", "public", force);
488
+ // Créer le composant ClientLayout (wrapper client avec providers)
489
+ await createClientLayout(targetDir, force, useHeroUI);
507
490
  // Créer le composant AppHeader
508
491
  await createAppHeader(targetDir, force);
509
492
  // Créer le composant AppAside
@@ -511,6 +494,81 @@ export default function NotFound() {
511
494
  // Créer le wrapper AppProviders avec configuration realtime
512
495
  await createAppProvidersWrapper(targetDir, force);
513
496
  }
497
+ async function createClientLayout(targetDir, force, useHeroUI) {
498
+ const componentsDir = path.join(targetDir, "components");
499
+ await fs.ensureDir(componentsDir);
500
+ const clientLayoutPath = path.join(componentsDir, "ClientLayout.tsx");
501
+ if (!fs.existsSync(clientLayoutPath) || force) {
502
+ let clientLayoutContent = "";
503
+ if (useHeroUI) {
504
+ // ClientLayout avec HeroUI
505
+ clientLayoutContent = `"use client";
506
+
507
+ import { HeroUIProvider } from "@heroui/system";
508
+ import { ThemeProvider } from "next-themes";
509
+ import { useRouter } from "next/navigation";
510
+ import { AppProviders } from "./AppProviders";
511
+ import { AppHeader } from "./AppHeader";
512
+ import type { ReactNode } from "react";
513
+
514
+ export function ClientLayout({ children }: { children: ReactNode }) {
515
+ const router = useRouter();
516
+
517
+ return (
518
+ <HeroUIProvider navigate={router.push}>
519
+ <ThemeProvider
520
+ attribute="class"
521
+ defaultTheme="dark"
522
+ enableSystem={false}
523
+ storageKey="lastbrain-theme"
524
+ >
525
+ <AppProviders>
526
+ <AppHeader />
527
+ <div className="min-h-screen text-foreground bg-background">
528
+ {children}
529
+ </div>
530
+ </AppProviders>
531
+ </ThemeProvider>
532
+ </HeroUIProvider>
533
+ );
534
+ }
535
+ `;
536
+ }
537
+ else {
538
+ // ClientLayout Tailwind CSS uniquement
539
+ clientLayoutContent = `"use client";
540
+
541
+ import { ThemeProvider } from "next-themes";
542
+ import { AppProviders } from "./AppProviders";
543
+ import { AppHeader } from "./AppHeader";
544
+ import type { ReactNode } from "react";
545
+
546
+ export function ClientLayout({ children }: { children: ReactNode }) {
547
+ return (
548
+ <ThemeProvider
549
+ attribute="class"
550
+ defaultTheme="light"
551
+ enableSystem={false}
552
+ storageKey="lastbrain-theme"
553
+ >
554
+ <AppProviders>
555
+ <AppHeader />
556
+ <div className="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white">
557
+ {children}
558
+ </div>
559
+ </AppProviders>
560
+ </ThemeProvider>
561
+ );
562
+ }
563
+ `;
564
+ }
565
+ await fs.writeFile(clientLayoutPath, clientLayoutContent);
566
+ console.log(chalk.green("✓ components/ClientLayout.tsx créé"));
567
+ }
568
+ else {
569
+ console.log(chalk.gray(" components/ClientLayout.tsx existe déjà (utilisez --force pour écraser)"));
570
+ }
571
+ }
514
572
  async function createRoute(appDir, routeName, layoutType, force) {
515
573
  const routeDir = path.join(appDir, routeName);
516
574
  await fs.ensureDir(routeDir);
@@ -553,11 +611,26 @@ export default function AuthLayout({
553
611
  }`;
554
612
  }
555
613
  else {
556
- // Layout standard pour les autres routes
614
+ // Layout standard pour les autres routes (ex: docs = public)
557
615
  const layoutComponent = layoutType.charAt(0).toUpperCase() + layoutType.slice(1) + "Layout";
558
- layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
616
+ if (routeName === "docs") {
617
+ // Layout docs avec footer
618
+ layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
619
+ import { footerConfig } from "../../config/footer";
620
+
621
+ export default function DocsLayout({
622
+ children,
623
+ }: {
624
+ children: React.ReactNode;
625
+ }) {
626
+ return <${layoutComponent} footerConfig={footerConfig}>{children}</${layoutComponent}>;
627
+ }`;
628
+ }
629
+ else {
630
+ layoutContent = `import { ${layoutComponent} } from "@lastbrain/app";
559
631
 
560
632
  export default ${layoutComponent};`;
633
+ }
561
634
  }
562
635
  await fs.writeFile(layoutPath, layoutContent);
563
636
  console.log(chalk.green(`✓ app/${routeName}/layout.tsx créé`));
@@ -721,7 +794,7 @@ export function AppProviders({ children }: { children: ReactNode }) {
721
794
  console.log(chalk.gray(" components/AppProviders.tsx existe déjà (utilisez --force pour écraser)"));
722
795
  }
723
796
  }
724
- async function createConfigFiles(targetDir, force, useHeroUI) {
797
+ async function createConfigFiles(targetDir, force, useHeroUI, projectName) {
725
798
  console.log(chalk.yellow("\n⚙️ Création des fichiers de configuration..."));
726
799
  // middleware.ts - Protection des routes /auth/*, /admin/* et /api/admin/*
727
800
  const middlewarePath = path.join(targetDir, "middleware.ts");
@@ -733,6 +806,24 @@ export async function middleware(request: NextRequest) {
733
806
  const { pathname } = request.nextUrl;
734
807
  const isApi = pathname.startsWith("/api/");
735
808
 
809
+ // Pages publiques d'authentification (ne pas protéger)
810
+ const publicAuthPages = [
811
+ "/signin",
812
+ "/signup",
813
+ "/reset-password",
814
+ "/forgot-password",
815
+ "/callback",
816
+ ];
817
+
818
+ const isPublicAuthPage = publicAuthPages.some((page) =>
819
+ pathname.startsWith(page)
820
+ );
821
+
822
+ // Ne pas protéger les pages publiques d'authentification
823
+ if (isPublicAuthPage) {
824
+ return NextResponse.next();
825
+ }
826
+
736
827
  // Protéger les routes /auth/* (espace membre)
737
828
  if (pathname.startsWith("/auth")) {
738
829
  try {
@@ -741,9 +832,9 @@ export async function middleware(request: NextRequest) {
741
832
  data: { session },
742
833
  } = await supabase.auth.getSession();
743
834
 
744
- // Pas de session → nettoyage des cookies + redirection vers /auth/signin
835
+ // Pas de session → nettoyage des cookies + redirection vers /signin
745
836
  if (!session) {
746
- const redirectUrl = new URL("/auth/signin", request.url);
837
+ const redirectUrl = new URL("/signin", request.url);
747
838
  redirectUrl.searchParams.set("redirect", pathname);
748
839
  const res = NextResponse.redirect(redirectUrl);
749
840
  res.cookies.delete("sb-access-token");
@@ -756,7 +847,7 @@ export async function middleware(request: NextRequest) {
756
847
  return response;
757
848
  } catch (error) {
758
849
  console.error("Middleware auth error:", error);
759
- const res = NextResponse.redirect(new URL("/auth/signin", request.url));
850
+ const res = NextResponse.redirect(new URL("/signin", request.url));
760
851
  res.cookies.delete("sb-access-token");
761
852
  res.cookies.delete("sb-refresh-token");
762
853
  res.cookies.delete("sb:token");
@@ -773,7 +864,7 @@ export async function middleware(request: NextRequest) {
773
864
  data: { session },
774
865
  } = await supabase.auth.getSession();
775
866
 
776
- // Pas de session → 401 JSON pour API, sinon redirection vers /auth/signin avec nettoyage cookies
867
+ // Pas de session → 401 JSON pour API, sinon redirection vers /signin avec nettoyage cookies
777
868
  if (!session) {
778
869
  if (isApi) {
779
870
  const res = NextResponse.json({ error: "Non authentifié" }, { status: 401 });
@@ -783,7 +874,7 @@ export async function middleware(request: NextRequest) {
783
874
  res.cookies.delete("sb:refresh-token");
784
875
  return res;
785
876
  }
786
- const redirectUrl = new URL("/auth/signin", request.url);
877
+ const redirectUrl = new URL("/signin", request.url);
787
878
  redirectUrl.searchParams.set("redirect", pathname);
788
879
  const res = NextResponse.redirect(redirectUrl);
789
880
  res.cookies.delete("sb-access-token");
@@ -847,6 +938,9 @@ export const config = {
847
938
  const nextConfig = `/** @type {import('next').NextConfig} */
848
939
  const nextConfig = {
849
940
  reactStrictMode: true,
941
+ devIndicators: {
942
+ position: 'bottom-right',
943
+ },
850
944
  };
851
945
 
852
946
  export default nextConfig;
@@ -983,6 +1077,25 @@ export const menuConfig: MenuConfig = {
983
1077
  await fs.writeFile(menuConfigPath, menuConfig);
984
1078
  console.log(chalk.green("✓ config/menu.ts créé"));
985
1079
  }
1080
+ // config/footer.ts
1081
+ const footerConfigPath = path.join(configDir, "footer.ts");
1082
+ if (!fs.existsSync(footerConfigPath) || force) {
1083
+ const footerConfig = `// Auto-generated footer configuration
1084
+ // Run "node ../../scripts/generate-footer-config.js ./apps/[your-app]" to regenerate
1085
+ "use client";
1086
+
1087
+ import type { FooterConfig } from "@lastbrain/ui";
1088
+
1089
+ export const footerConfig: FooterConfig = {
1090
+ companyName: "${projectName}",
1091
+ companyDescription: "Application LastBrain",
1092
+ links: [],
1093
+ social: [],
1094
+ };
1095
+ `;
1096
+ await fs.writeFile(footerConfigPath, footerConfig);
1097
+ console.log(chalk.green("✓ config/footer.ts créé"));
1098
+ }
986
1099
  // Créer les hooks
987
1100
  await createHooksDirectory(targetDir, force);
988
1101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAw0CA,wBAAsB,cAAc,kBAyFnC"}
1
+ {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAy4CA,wBAAsB,cAAc,kBA+FnC"}
@@ -29,7 +29,10 @@ async function loadModuleConfigs() {
29
29
  }
30
30
  const packageName = module.package;
31
31
  try {
32
- const moduleSuffix = packageName.replace("@lastbrain/module-", "");
32
+ // Extraire le suffix du module (la partie après @lastbrain/module- ou @lastbrain-labs/module-)
33
+ const moduleSuffix = packageName
34
+ .replace("@lastbrain/module-", "")
35
+ .replace("@lastbrain-labs/module-", "");
33
36
  const possibleConfigNames = [
34
37
  `${moduleSuffix}.build.config`,
35
38
  "build.config",
@@ -119,9 +122,10 @@ function toPascalCase(value) {
119
122
  .join("");
120
123
  }
121
124
  function buildPage(moduleConfig, page) {
122
- // Extraire le préfixe du module (ex: @lastbrain/module-auth -> auth)
125
+ // Extraire le préfixe du module (ex: @lastbrain/module-auth -> auth, @lastbrain-labs/module-recipes-pro -> recipes-pro)
123
126
  const modulePrefix = moduleConfig.moduleName
124
127
  .replace(/^@lastbrain\/module-/, "")
128
+ .replace(/^@lastbrain-labs\/module-/, "")
125
129
  .toLowerCase();
126
130
  if (isDebugMode) {
127
131
  console.log(`🔄 Building page for module ${modulePrefix}: ${page.path}`);
@@ -468,7 +472,13 @@ function generateDocsPage(moduleConfigs) {
468
472
  const moduleConfigurations = [];
469
473
  allModules.forEach((moduleEntry) => {
470
474
  const moduleName = moduleEntry.package;
471
- const moduleId = moduleName.replace("@lastbrain/module-", "");
475
+ // Extraire le nom du module sans le scope et sans "module-"
476
+ // Ex: @lastbrain/module-auth -> auth
477
+ // Ex: @lastbrain-labs/module-recipes-pro -> recipes
478
+ const moduleId = moduleName
479
+ .replace("@lastbrain-labs/module-", "")
480
+ .replace("@lastbrain/module-", "")
481
+ .replace(/-pro$/, ""); // Retirer le suffix -pro pour avoir le nom de base
472
482
  const docComponentName = `${toPascalCase(moduleId)}ModuleDoc`;
473
483
  // Trouver la config du module pour obtenir la description
474
484
  const moduleConfig = moduleConfigs.find((mc) => mc.moduleName === moduleName);
@@ -480,8 +490,8 @@ function generateDocsPage(moduleConfigs) {
480
490
  docImports.push(`import { ${docComponentName} } from "${moduleName}";`);
481
491
  }
482
492
  const config = {
483
- id: moduleId,
484
- name: `Module ${moduleId.charAt(0).toUpperCase() + moduleId.slice(1)}`,
493
+ id: moduleName, // Utiliser le nom complet du package comme ID
494
+ name: `Module ${toPascalCase(moduleId)}`,
485
495
  description: description,
486
496
  component: docComponentName,
487
497
  active: moduleEntry.active,
@@ -1027,7 +1037,6 @@ import { NextRequest, NextResponse } from "next/server";
1027
1037
  * GENERATED FILE - DO NOT EDIT MANUALLY
1028
1038
  * Generated at: ${timestamp}
1029
1039
  * Generated from module storage configurations
1030
- *
1031
1040
  * Buckets configurés:
1032
1041
  ${allBuckets.map((b) => ` * - ${b.name} (${b.public ? "public" : "private"})`).join("\n")}
1033
1042
  */
@@ -1120,6 +1129,53 @@ export async function GET(
1120
1129
  console.error("❌ Error generating storage proxy API:", error);
1121
1130
  }
1122
1131
  }
1132
+ async function generateFooterConfig(moduleConfigs) {
1133
+ try {
1134
+ // Extraire tous les liens footer des modules
1135
+ const allFooterLinks = moduleConfigs
1136
+ .filter((config) => config.footer && config.footer.length > 0)
1137
+ .flatMap((config) => config.footer);
1138
+ if (allFooterLinks.length === 0) {
1139
+ console.log("⏭️ No footer links found, skipping footer config generation");
1140
+ return;
1141
+ }
1142
+ // Trier les liens par position et order
1143
+ allFooterLinks.sort((a, b) => {
1144
+ const posOrder = { left: 0, center: 1, right: 2 };
1145
+ const posA = posOrder[a.position || "left"] || 0;
1146
+ const posB = posOrder[b.position || "left"] || 0;
1147
+ if (posA !== posB)
1148
+ return posA - posB;
1149
+ return (a.order || 0) - (b.order || 0);
1150
+ });
1151
+ const timestamp = new Date().toISOString();
1152
+ const content = `// Auto-generated footer configuration
1153
+ // Generated from module build configs
1154
+ // Generated at: ${timestamp}
1155
+ "use client";
1156
+
1157
+ import type { FooterConfig } from "@lastbrain/ui";
1158
+
1159
+ export const footerConfig: FooterConfig = {
1160
+ companyName: "LastBrain",
1161
+ companyDescription: "Plateforme de développement rapide d'applications",
1162
+ links: ${JSON.stringify(allFooterLinks, null, 2)},
1163
+ social: [],
1164
+ };
1165
+ `;
1166
+ const configDir = path.join(projectRoot, "config");
1167
+ ensureDirectory(configDir);
1168
+ const footerPath = path.join(configDir, "footer.ts");
1169
+ fs.writeFileSync(footerPath, content);
1170
+ if (isDebugMode) {
1171
+ console.log(`✅ Generated footer config: ${footerPath}`);
1172
+ console.log(` - ${allFooterLinks.length} footer link(s)`);
1173
+ }
1174
+ }
1175
+ catch (error) {
1176
+ console.error("❌ Error generating footer config:", error);
1177
+ }
1178
+ }
1123
1179
  export async function runModuleBuild() {
1124
1180
  ensureDirectory(appDirectory);
1125
1181
  // Nettoyer les fichiers générés précédemment
@@ -1184,6 +1240,11 @@ export async function runModuleBuild() {
1184
1240
  console.log("🔌 Generating storage proxy API...");
1185
1241
  }
1186
1242
  await generateStorageProxyApi(moduleConfigs);
1243
+ // Générer la configuration footer
1244
+ if (isDebugMode) {
1245
+ console.log("🦶 Generating footer configuration...");
1246
+ }
1247
+ await generateFooterConfig(moduleConfigs);
1187
1248
  // Message de succès final
1188
1249
  if (!isDebugMode) {
1189
1250
  console.log("\n✅ Module build completed successfully!");
@@ -1 +1 @@
1
- {"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AA+vCD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAiB1C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,iBAoLhB;AAED;;GAEG;AACH,wBAAsB,YAAY,kBAwKjC"}
1
+ {"version":3,"file":"module-create.d.ts","sourceRoot":"","sources":["../../src/scripts/module-create.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAwwCD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAiB1C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,iBAoLhB;AAED;;GAEG;AACH,wBAAsB,YAAY,kBAsLjC"}
@@ -911,6 +911,11 @@ async function generateModuleReadme(config, moduleDir) {
911
911
  const moduleNameClean = config.slug.replace("module-", "");
912
912
  let md = `# 📦 Module ${moduleNameClean}\n\n`;
913
913
  md += `> ${config.moduleName}\n\n`;
914
+ // Description section
915
+ if (config.description) {
916
+ md += `## 📝 Description\n\n`;
917
+ md += `${config.description}\n\n`;
918
+ }
914
919
  // Information section
915
920
  md += `## 📋 Informations\n\n`;
916
921
  md += `- **Nom du package**: \`${config.moduleName}\`\n`;
@@ -1286,6 +1291,18 @@ export async function createModule() {
1286
1291
  },
1287
1292
  filter: (input) => input.trim().toLowerCase(),
1288
1293
  },
1294
+ {
1295
+ type: "input",
1296
+ name: "description",
1297
+ message: "Description du module (une ligne):",
1298
+ default: "Module LastBrain",
1299
+ validate: (input) => {
1300
+ if (!input || input.trim() === "") {
1301
+ return "La description est requise";
1302
+ }
1303
+ return true;
1304
+ },
1305
+ },
1289
1306
  {
1290
1307
  type: "input",
1291
1308
  name: "pagesPublic",
@@ -1314,6 +1331,7 @@ export async function createModule() {
1314
1331
  // Construire la configuration du module
1315
1332
  const slug = `module-${answers.slug}`;
1316
1333
  const moduleName = `@lastbrain/${slug}`;
1334
+ const description = answers.description;
1317
1335
  const pages = [];
1318
1336
  // Pages publiques
1319
1337
  const publicPages = parsePagesList(answers.pagesPublic);
@@ -1388,6 +1406,7 @@ export async function createModule() {
1388
1406
  moduleName,
1389
1407
  pages,
1390
1408
  tables,
1409
+ description,
1391
1410
  };
1392
1411
  // Trouver le répertoire racine du workspace (chercher pnpm-workspace.yaml)
1393
1412
  let rootDir;
@@ -1 +1 @@
1
- {"version":3,"file":"module-delete.d.ts","sourceRoot":"","sources":["../../src/scripts/module-delete.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wBAAsB,YAAY,kBAgLjC"}
1
+ {"version":3,"file":"module-delete.d.ts","sourceRoot":"","sources":["../../src/scripts/module-delete.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,wBAAsB,YAAY,kBAkLjC"}
@@ -22,7 +22,7 @@ export async function deleteModule() {
22
22
  }
23
23
  const answers = await inquirer.prompt([
24
24
  {
25
- type: "list",
25
+ type: "select",
26
26
  name: "moduleName",
27
27
  message: "Quel module voulez-vous supprimer du monorepo ?",
28
28
  choices: AVAILABLE_MODULES.map((m) => ({
@@ -49,7 +49,11 @@ export async function deleteModule() {
49
49
  }
50
50
  console.log(chalk.blue(`\n🗑️ Suppression du module ${moduleMeta.package}...\n`));
51
51
  // 1. Supprimer le répertoire du module
52
- const moduleDir = path.join(rootDir, "packages", moduleMeta.package.replace("@lastbrain/", ""));
52
+ // Extraire le nom du package correctement (gérer @lastbrain et @lastbrain-labs)
53
+ const packageName = moduleMeta.package
54
+ .replace("@lastbrain-labs/", "")
55
+ .replace("@lastbrain/", "");
56
+ const moduleDir = path.join(rootDir, "packages", packageName);
53
57
  if (fs.existsSync(moduleDir)) {
54
58
  console.log(chalk.yellow(`📁 Suppression du répertoire: ${moduleDir}`));
55
59
  await fs.remove(moduleDir);
@@ -1 +1 @@
1
- {"version":3,"file":"module-list.d.ts","sourceRoot":"","sources":["../../src/scripts/module-list.ts"],"names":[],"mappings":"AAQA,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,iBAwElD"}
1
+ {"version":3,"file":"module-list.d.ts","sourceRoot":"","sources":["../../src/scripts/module-list.ts"],"names":[],"mappings":"AAKA,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,iBAwElD"}
@@ -1,7 +1,7 @@
1
1
  import fs from "fs-extra";
2
2
  import path from "path";
3
3
  import chalk from "chalk";
4
- import { AVAILABLE_MODULES, } from "@lastbrain/core/config/modules";
4
+ import { AVAILABLE_MODULES } from "@lastbrain/core/config/modules";
5
5
  export async function listModules(targetDir) {
6
6
  console.log(chalk.blue("\n📦 Modules disponibles:\n"));
7
7
  // Lire la config des modules installés