@lastbrain/app 2.0.23 → 2.0.31

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.
@@ -319,6 +319,11 @@ export default function ${wrapperName}${hasDynamicParams ? "(props: Record<strin
319
319
  .filter((seg) => seg.startsWith("[") && seg.endsWith("]"))
320
320
  .map((seg) => seg.slice(1, -1)); // Enlève les []
321
321
 
322
+ // Détecter si le module path lui-même a des params dynamiques (pas seulement [lang])
323
+ const moduleHasDynamicParams = segments.some(
324
+ (seg) => seg.startsWith("[") && seg.endsWith("]")
325
+ );
326
+
322
327
  // Générer le type des params si nécessaire
323
328
  const paramsType =
324
329
  dynamicParamNames.length > 0
@@ -333,23 +338,60 @@ export default function ${wrapperName}${hasDynamicParams ? "(props: Record<strin
333
338
  // Détecter si c'est un Server Component (entryPoint: "server")
334
339
  const isServerComponent = page.entryPoint === "server";
335
340
 
336
- // Les Server Components reçoivent params en props
341
+ // Les Server Components reçoivent params en props si la page a des params dynamiques
337
342
  // Les Client Components utilisent useParams() en interne
338
- const componentProps = isServerComponent && hasDynamicParams
339
- ? "params={params}"
340
- : "";
343
+ const componentProps =
344
+ isServerComponent && hasDynamicParams ? "params={params}" : "";
341
345
 
342
346
  // Si params existe mais n'est pas passé au composant, on doit le unwrap quand même
343
347
  const awaitParams =
344
- hasDynamicParams && !isServerComponent
348
+ hasDynamicParams && !componentProps
345
349
  ? "await params; // Unwrap params for Next.js 15\n "
346
350
  : "";
347
351
 
352
+ // Calculer le chemin relatif correct pour locales.generated
353
+ // routeDir = appDirectory + sectionPath + segments
354
+ // on doit remonter de (sectionPath.length + segments.length) niveaux pour atteindre appDirectory
355
+ // puis accéder à config/locales.generated
356
+ const pathDepth = sectionPath.length + segments.length;
357
+ const localeImportPath =
358
+ Array(pathDepth + 1)
359
+ .fill("..")
360
+ .join("/") + "/config/locales.generated";
361
+
362
+ // Pour les pages publiques (section: "public"), importer et passer LOCALE_CONFIG
363
+ const localeImport =
364
+ page.section === "public"
365
+ ? `\nimport { LOCALE_CONFIG } from "${localeImportPath}";\n`
366
+ : "";
367
+
368
+ const localeProps =
369
+ page.section === "public" && isServerComponent
370
+ ? "localeConfig={LOCALE_CONFIG}"
371
+ : "";
372
+
373
+ const allComponentProps = [componentProps, localeProps]
374
+ .filter(Boolean)
375
+ .join(" ");
376
+
377
+ // Pour les pages publiques avec metadataExport, créer une fonction wrapper qui passe LOCALE_CONFIG
378
+ // On utilise un alias pour l'import afin d'éviter le conflit de nom avec la fonction exportée
379
+ const metadataImportAlias = page.metadataExport
380
+ ? `${page.metadataExport} as ${page.metadataExport}Original`
381
+ : "";
382
+ const metadataExport =
383
+ page.section === "public" && page.metadataExport && localeImport
384
+ ? `\nexport async function generateMetadata(props: any) {
385
+ return ${page.metadataExport}Original({ ...props, localeConfig: LOCALE_CONFIG });
386
+ }`
387
+ : page.metadataExport
388
+ ? `\nexport { ${page.metadataExport}Original as generateMetadata };`
389
+ : "";
390
+
348
391
  content = `// GENERATED BY LASTBRAIN MODULE BUILD
349
- import { ${page.componentExport} } from "${importPath}";
350
- ${page.metadataExport ? `\nexport { ${page.metadataExport} as generateMetadata } from "${importPath}";\n` : ""}
392
+ import { ${page.componentExport}${page.metadataExport ? `, ${metadataImportAlias}` : ""} } from "${importPath}";${localeImport}${metadataExport}
351
393
  export default ${hasDynamicParams ? "async " : ""}function ${wrapperName}${propsSignature} {
352
- ${awaitParams}return <${page.componentExport} ${componentProps} />;
394
+ ${awaitParams}return <${page.componentExport} ${allComponentProps} />;
353
395
  }
354
396
  `;
355
397
  }
@@ -511,6 +553,7 @@ export interface MenuItem {
511
553
  component?: React.ComponentType<any>;
512
554
  componentExport?: string;
513
555
  entryPoint?: string;
556
+ badge?: string;
514
557
  }
515
558
 
516
559
  export interface MenuConfig {
@@ -1004,10 +1047,12 @@ export function ClientLayout({
1004
1047
  children,
1005
1048
  lang = "fr",
1006
1049
  translations = {},
1050
+ availableLanguages = ["fr", "en"],
1007
1051
  }: {
1008
1052
  children: ReactNode;
1009
1053
  lang?: Language;
1010
1054
  translations?: Record<string, string>;
1055
+ availableLanguages?: string[];
1011
1056
  }) {
1012
1057
  const router = useLocalizedRouter();
1013
1058
 
@@ -1019,7 +1064,7 @@ export function ClientLayout({
1019
1064
  enableSystem={false}
1020
1065
  storageKey="lastbrain-theme"
1021
1066
  >
1022
- <AppProviders lang={lang} translations={translations}>
1067
+ <AppProviders lang={lang} translations={translations} availableLanguages={availableLanguages}>
1023
1068
  <AppHeader />
1024
1069
  <div className="min-h-screen text-foreground bg-background">
1025
1070
  {children}
@@ -1057,16 +1102,19 @@ export function AppProviders({
1057
1102
  children,
1058
1103
  lang = "fr",
1059
1104
  translations = {},
1105
+ availableLanguages = ["fr", "en"],
1060
1106
  }: {
1061
1107
  children: ReactNode;
1062
1108
  lang?: Language;
1063
1109
  translations?: Record<string, string>;
1110
+ availableLanguages?: string[];
1064
1111
  }) {
1065
1112
  return (
1066
1113
  <BaseAppProviders
1067
1114
  realtimeConfig={realtimeConfig}
1068
1115
  lang={lang}
1069
1116
  translations={translations}
1117
+ availableLanguages={availableLanguages}
1070
1118
  >
1071
1119
  {children}
1072
1120
  </BaseAppProviders>
@@ -1090,12 +1138,19 @@ export function AppProviders({
1090
1138
  import { Header } from "@lastbrain/ui";
1091
1139
  import { LanguageSwitcher } from "@lastbrain/app";
1092
1140
  import { menuConfig } from "../../config/menu";
1141
+ import { LOCALE_CONFIG } from "../../config/locales.generated";
1093
1142
 
1094
1143
  export function AppHeader() {
1095
1144
  return (
1096
1145
  <Header
1097
1146
  menuConfig={menuConfig}
1098
- languageSwitcher={<LanguageSwitcher variant="minimal" />}
1147
+ languageSwitcher={
1148
+ <LanguageSwitcher
1149
+ variant="minimal"
1150
+ availableLanguages={[...LOCALE_CONFIG.languages]}
1151
+ localeMap={LOCALE_CONFIG.localeMap}
1152
+ />
1153
+ }
1099
1154
  />
1100
1155
  );
1101
1156
  }`;
@@ -1112,6 +1167,27 @@ export function AppHeader() {
1112
1167
  const langLayoutContent = `import { loadTranslations } from "@lastbrain/app/i18n/server-lang";
1113
1168
  import { ClientLayout } from "../../components/ClientLayout";
1114
1169
 
1170
+ // Fallback default config in case locales.generated.ts is not available
1171
+ const DEFAULT_LOCALE_CONFIG = {
1172
+ languages: ["fr", "en"] as const,
1173
+ locales: ["fr_FR", "en_US"],
1174
+ localeMap: { fr: "fr_FR", en: "en_US" } as Record<string, string>,
1175
+ };
1176
+
1177
+ // Type for valid languages derived from config
1178
+ type ValidLanguage = (typeof DEFAULT_LOCALE_CONFIG.languages)[number];
1179
+
1180
+ // Helper function to safely load locale config
1181
+ async function getLocaleConfig() {
1182
+
1183
+ const importedConfig = await import("../../config/locales.generated");
1184
+ if (importedConfig?.LOCALE_CONFIG && importedConfig.LOCALE_CONFIG.languages) {
1185
+ return importedConfig.LOCALE_CONFIG;
1186
+ }
1187
+
1188
+ return DEFAULT_LOCALE_CONFIG;
1189
+ }
1190
+
1115
1191
  export default async function LangLayout({
1116
1192
  children,
1117
1193
  params,
@@ -1120,13 +1196,22 @@ export default async function LangLayout({
1120
1196
  params: Promise<{ lang: string }>;
1121
1197
  }) {
1122
1198
  const { lang } = await params;
1123
- const validLang = lang === "en" || lang === "fr" ? lang : "fr";
1199
+
1200
+ // Get the locale config (either from app or fallback)
1201
+ const LOCALE_CONFIG = await getLocaleConfig();
1202
+
1203
+ // Validate lang and cast to ValidLanguage type
1204
+ const isValidLang = (val: string): val is ValidLanguage => {
1205
+ return LOCALE_CONFIG.languages.includes(val as any);
1206
+ };
1207
+
1208
+ const validLang: ValidLanguage = isValidLang(lang) ? lang : "fr";
1124
1209
 
1125
1210
  // Charger les traductions pour cette langue
1126
1211
  const translations = await loadTranslations(validLang);
1127
1212
 
1128
1213
  return (
1129
- <ClientLayout lang={validLang} translations={translations}>
1214
+ <ClientLayout lang={validLang} translations={translations} availableLanguages={[...LOCALE_CONFIG.languages]}>
1130
1215
  <div className="min-h-screen pt-16">
1131
1216
  {children}
1132
1217
  </div>
@@ -1379,6 +1464,157 @@ async function generateUserTabsConfig(moduleConfigs: ModuleBuildConfig[]) {
1379
1464
  }
1380
1465
  }
1381
1466
 
1467
+ async function generateAuthDashboard(moduleConfigs: ModuleBuildConfig[]) {
1468
+ try {
1469
+ const dashboards = moduleConfigs
1470
+ .flatMap((config) =>
1471
+ (config.authDashboard ?? []).map((dashboard) => ({
1472
+ ...dashboard,
1473
+ moduleName: config.moduleName,
1474
+ }))
1475
+ )
1476
+ .sort((a, b) => (a.order ?? 999) - (b.order ?? 999));
1477
+
1478
+ const timestamp = new Date().toISOString();
1479
+
1480
+ let configContent: string;
1481
+
1482
+ if (dashboards.length === 0) {
1483
+ configContent = `// GENERATED FILE - DO NOT EDIT MANUALLY
1484
+ // Auth dashboard configuration
1485
+ // Generated at: ${timestamp}
1486
+
1487
+ "use client";
1488
+
1489
+ import type React from "react";
1490
+
1491
+ export interface ModuleAuthDashboard {
1492
+ key: string;
1493
+ title: string;
1494
+ icon?: string;
1495
+ order?: number;
1496
+ component: React.ComponentType<Record<string, never>>;
1497
+ }
1498
+
1499
+ export const moduleAuthDashboards: ModuleAuthDashboard[] = [];
1500
+
1501
+ export default moduleAuthDashboards;
1502
+ `;
1503
+ } else {
1504
+ const dynamicImports = dashboards
1505
+ .map(
1506
+ (dashboard, index) =>
1507
+ `const AuthDashboardComponent${index} = dynamic(() => import("${dashboard.moduleName}").then(mod => ({ default: (mod as any)["${dashboard.componentExport}"] })), { ssr: true });`
1508
+ )
1509
+ .join("\n");
1510
+
1511
+ const dashboardsArray = dashboards
1512
+ .map(
1513
+ (dashboard, index) => ` {
1514
+ key: "${dashboard.key}",
1515
+ title: "${dashboard.title}",
1516
+ icon: "${dashboard.icon ?? ""}",
1517
+ order: ${dashboard.order ?? 999},
1518
+ component: AuthDashboardComponent${index},
1519
+ }`
1520
+ )
1521
+ .join(",\n");
1522
+
1523
+ configContent = `// GENERATED FILE - DO NOT EDIT MANUALLY
1524
+ // Auth dashboard configuration
1525
+ // Generated at: ${timestamp}
1526
+
1527
+ "use client";
1528
+
1529
+ import dynamic from "next/dynamic";
1530
+ import type React from "react";
1531
+
1532
+ ${dynamicImports}
1533
+
1534
+ export interface ModuleAuthDashboard {
1535
+ key: string;
1536
+ title: string;
1537
+ icon?: string;
1538
+ order?: number;
1539
+ component: React.ComponentType<Record<string, never>>;
1540
+ }
1541
+
1542
+ export const moduleAuthDashboards: ModuleAuthDashboard[] = [
1543
+ ${dashboardsArray}
1544
+ ];
1545
+
1546
+ export default moduleAuthDashboards;
1547
+ `;
1548
+ }
1549
+
1550
+ const configPath = path.join(projectRoot, "config", "auth-dashboard.ts");
1551
+ ensureDirectory(path.dirname(configPath));
1552
+ fs.writeFileSync(configPath, configContent);
1553
+
1554
+ if (isDebugMode) {
1555
+ console.log(
1556
+ `✅ Generated auth dashboard configuration: ${configPath} (${dashboards.length} item(s))`
1557
+ );
1558
+ }
1559
+
1560
+ const pagePath = path.join(
1561
+ appDirectory,
1562
+ "[lang]",
1563
+ "auth",
1564
+ "dashboard",
1565
+ "page.tsx"
1566
+ );
1567
+
1568
+ ensureDirectory(path.dirname(pagePath));
1569
+
1570
+ const pageContent = `// GENERATED BY LASTBRAIN MODULE BUILD
1571
+ "use client";
1572
+
1573
+ import { Suspense } from "react";
1574
+ import { moduleAuthDashboards } from "../../../../config/auth-dashboard";
1575
+
1576
+ export const dynamic = "force-dynamic";
1577
+
1578
+
1579
+ export default function AuthDashboardPage() {
1580
+ const dashboards = moduleAuthDashboards || [];
1581
+
1582
+ if (dashboards.length === 0) {
1583
+ return <div className="space-y-4">Aucun dashboard disponible.</div>;
1584
+ }
1585
+
1586
+ return (
1587
+ <div className="space-y-6">
1588
+ {dashboards.map((dashboard) => {
1589
+
1590
+ const Component = dashboard.component;
1591
+
1592
+ return (
1593
+ <section
1594
+ key={dashboard.key}
1595
+
1596
+ >
1597
+ <Suspense fallback={<div>Chargement...</div>}>
1598
+ <Component />
1599
+ </Suspense>
1600
+ </section>
1601
+ );
1602
+ })}
1603
+ </div>
1604
+ );
1605
+ }
1606
+ `;
1607
+
1608
+ fs.writeFileSync(pagePath, pageContent);
1609
+
1610
+ if (isDebugMode) {
1611
+ console.log(`🧭 Generated auth dashboard page: ${pagePath}`);
1612
+ }
1613
+ } catch (error) {
1614
+ console.error("❌ Error generating auth dashboard configuration:", error);
1615
+ }
1616
+ }
1617
+
1382
1618
  async function generateBucketsConfig(moduleConfigs: ModuleBuildConfig[]) {
1383
1619
  try {
1384
1620
  // Extraire les configurations storage des modules
@@ -1602,9 +1838,9 @@ export async function GET(
1602
1838
  // c'est une image avec format court qui nécessite le préfixe userId
1603
1839
  let actualStoragePath = storagePath;
1604
1840
 
1605
- if (storagePath.startsWith("product/") || storagePath.startsWith("recipe/")) {
1606
- actualStoragePath = \`\${user.id}/\${storagePath}\`;
1607
- }
1841
+
1842
+ actualStoragePath = \`\${user.id}/\${storagePath}\`;
1843
+
1608
1844
 
1609
1845
  // Vérifier que l'utilisateur a accès à cette image
1610
1846
  // Format: {userId}/recipe/{recipeId}/{filename} ou {userId}/product/{productId}/{filename}
@@ -1700,8 +1936,6 @@ async function generateFooterConfig(moduleConfigs: ModuleBuildConfig[]) {
1700
1936
  import type { FooterConfig } from "@lastbrain/ui";
1701
1937
 
1702
1938
  export const footerConfig: FooterConfig = {
1703
- companyName: "LastBrain",
1704
- companyDescription: "Plateforme de développement rapide d'applications",
1705
1939
  links: ${JSON.stringify(allFooterLinks, null, 2)},
1706
1940
  social: [],
1707
1941
  };
@@ -1724,9 +1958,129 @@ export const footerConfig: FooterConfig = {
1724
1958
 
1725
1959
  /**
1726
1960
  * Génère les fichiers i18n en concaténant les traductions des modules
1961
+ * et en incluant les traductions personnalisées de i18n/default
1962
+ */
1963
+ async function generateLocaleConfig() {
1964
+ const appI18nDir = path.join(projectRoot, "i18n");
1965
+ const appConfigDir = path.join(projectRoot, "config");
1966
+
1967
+ // Mapping standard des codes de langue vers les locales
1968
+ const LOCALE_MAP: Record<string, string> = {
1969
+ fr: "fr_FR",
1970
+ en: "en_US",
1971
+ es: "es_ES",
1972
+ de: "de_DE",
1973
+ it: "it_IT",
1974
+ pt: "pt_PT",
1975
+ nl: "nl_NL",
1976
+ ru: "ru_RU",
1977
+ ja: "ja_JP",
1978
+ zh: "zh_CN",
1979
+ ar: "ar_SA",
1980
+ hi: "hi_IN",
1981
+ };
1982
+
1983
+ try {
1984
+ // Scanner les fichiers i18n disponibles
1985
+ let languages: string[] = ["fr"]; // Fallback par défaut
1986
+
1987
+ if (fs.existsSync(appI18nDir)) {
1988
+ const files = fs.readdirSync(appI18nDir);
1989
+ languages = files
1990
+ .filter((f) => f.endsWith(".json"))
1991
+ .map((f) => f.replace(".json", ""))
1992
+ .filter((lang) => LOCALE_MAP[lang])
1993
+ .sort();
1994
+
1995
+ if (isDebugMode) {
1996
+ console.log(` 📋 Detected languages: ${languages.join(", ")}`);
1997
+ }
1998
+ }
1999
+
2000
+ const locales = languages.map((lang) => LOCALE_MAP[lang]);
2001
+ const localeMap = languages.reduce(
2002
+ (acc, lang) => {
2003
+ acc[lang] = LOCALE_MAP[lang];
2004
+ return acc;
2005
+ },
2006
+ {} as Record<string, string>
2007
+ );
2008
+
2009
+ // Générer le fichier TypeScript dans l'app (ex: apps/recipe/config/)
2010
+ ensureDirectory(appConfigDir);
2011
+
2012
+ // Générer le fichier dans config/
2013
+ const outputPath = path.join(appConfigDir, "locales.generated.ts");
2014
+ const content = `// This file is auto-generated by module-build.ts
2015
+ // Do not edit manually - regenerate with 'pnpm build:modules'
2016
+
2017
+ export interface LocaleConfig {
2018
+ languages: string[];
2019
+ locales: string[];
2020
+ localeMap: Record<string, string>;
2021
+ }
2022
+
2023
+ export const LOCALE_CONFIG: LocaleConfig = ${JSON.stringify(
2024
+ {
2025
+ languages,
2026
+ locales,
2027
+ localeMap,
2028
+ },
2029
+ null,
2030
+ 2
2031
+ )} as const;
2032
+
2033
+ /**
2034
+ * Convert language code to locale (e.g., "fr" -> "fr_FR")
1727
2035
  */
2036
+ export function langToLocale(lang: string): string {
2037
+ return LOCALE_CONFIG.localeMap[lang] || "fr_FR";
2038
+ }
2039
+
2040
+ /**
2041
+ * Get all locales except the given one
2042
+ */
2043
+ export function getAlternateLocales(lang: string): string[] {
2044
+ const currentLocale = LOCALE_CONFIG.localeMap[lang] || "fr_FR";
2045
+ return LOCALE_CONFIG.locales.filter((l) => l !== currentLocale);
2046
+ }
2047
+
2048
+ /**
2049
+ * Get locale map for metadata generation
2050
+ */
2051
+ export function getLocaleMap(): Record<string, string> {
2052
+ return LOCALE_CONFIG.localeMap;
2053
+ }
2054
+ `;
2055
+
2056
+ writeScaffoldFile(outputPath, content, "locale configuration");
2057
+
2058
+ // Générer aussi un fichier de ré-export dans le répertoire racine de l'app pour faciliter l'import
2059
+ const rootExportPath = path.join(projectRoot, "locale-helpers.ts");
2060
+ const rootExportContent = `// Re-export locale helpers from config for easy import in packages
2061
+ export { langToLocale, getAlternateLocales, getLocaleMap, LOCALE_CONFIG } from "./config/locales.generated";
2062
+ export type { LocaleConfig } from "./config/locales.generated";
2063
+ `;
2064
+
2065
+ writeScaffoldFile(
2066
+ rootExportPath,
2067
+ rootExportContent,
2068
+ "locale helpers re-export"
2069
+ );
2070
+
2071
+ if (isDebugMode) {
2072
+ console.log(
2073
+ ` ✅ Generated locale config with ${languages.length} language(s)`
2074
+ );
2075
+ }
2076
+ } catch (error) {
2077
+ console.error("❌ Error generating locale config:", error);
2078
+ }
2079
+ }
2080
+
1728
2081
  async function generateI18nFiles() {
1729
2082
  const appI18nDir = path.join(projectRoot, "i18n");
2083
+ const appI18nDefaultDir = path.join(projectRoot, "i18n", "default");
1730
2084
  const packagesDir = path.join(_monorepoRoot, "packages");
1731
2085
 
1732
2086
  interface TranslationMap {
@@ -1789,6 +2143,41 @@ async function generateI18nFiles() {
1789
2143
  }
1790
2144
  }
1791
2145
 
2146
+ // Charger les traductions personnalisées de i18n/default
2147
+ if (fs.existsSync(appI18nDefaultDir)) {
2148
+ // Découvrir dynamiquement les fichiers de langue disponibles
2149
+ const defaultFiles = fs
2150
+ .readdirSync(appI18nDefaultDir)
2151
+ .filter((file) => file.endsWith(".json"));
2152
+
2153
+ for (const defaultFile of defaultFiles) {
2154
+ const defaultFilePath = path.join(appI18nDefaultDir, defaultFile);
2155
+
2156
+ if (fs.existsSync(defaultFilePath)) {
2157
+ const lang = defaultFile.replace(".json", "");
2158
+
2159
+ if (!translations[lang]) {
2160
+ translations[lang] = {};
2161
+ }
2162
+
2163
+ try {
2164
+ const content = JSON.parse(
2165
+ fs.readFileSync(defaultFilePath, "utf-8")
2166
+ );
2167
+
2168
+ if (isDebugMode) {
2169
+ console.log(` ✓ app defaults (${lang})`);
2170
+ }
2171
+
2172
+ // Ajouter ou surcharger les traductions personnalisées
2173
+ Object.assign(translations[lang], content);
2174
+ } catch (error) {
2175
+ console.warn(` ⚠️ Error reading ${defaultFilePath}:`, error);
2176
+ }
2177
+ }
2178
+ }
2179
+ }
2180
+
1792
2181
  // Créer le dossier i18n s'il n'existe pas
1793
2182
  ensureDirectory(appI18nDir);
1794
2183
 
@@ -1820,6 +2209,77 @@ function extractModuleNameFromPath(filePath: string): string {
1820
2209
  return "unknown";
1821
2210
  }
1822
2211
 
2212
+ /**
2213
+ * Génère la page d'accueil app/[lang]/page.tsx si un module définit path: "/"
2214
+ */
2215
+ function generateHomePage(moduleConfigs: ModuleBuildConfig[]) {
2216
+ // Chercher une page avec section: "public" et path: "/"
2217
+ let homePageModule: ModuleBuildConfig | null = null;
2218
+ let homePageConfig: ModulePageConfig | null = null;
2219
+
2220
+ for (const moduleConfig of moduleConfigs) {
2221
+ const homePage = moduleConfig.pages.find(
2222
+ (page) => page.section === "public" && page.path === "/"
2223
+ );
2224
+ if (homePage) {
2225
+ homePageModule = moduleConfig;
2226
+ homePageConfig = homePage;
2227
+ break;
2228
+ }
2229
+ }
2230
+
2231
+ if (!homePageModule || !homePageConfig) {
2232
+ if (isDebugMode) {
2233
+ console.log(" ℹ️ No homepage (/) definition found in modules");
2234
+ }
2235
+ return;
2236
+ }
2237
+
2238
+ // Générer la home dans le segment public: app/[lang]/(public)/page.tsx
2239
+ const homePageDir = path.join(appDirectory, "[lang]", "(public)");
2240
+ const homePagePath = path.join(homePageDir, "page.tsx");
2241
+ ensureDirectory(homePageDir);
2242
+
2243
+ // Déterminer le chemin d'import
2244
+ const importPath = homePageConfig.entryPoint
2245
+ ? `${homePageModule.moduleName}/${homePageConfig.entryPoint}`
2246
+ : homePageModule.moduleName;
2247
+
2248
+ // Éviter les collisions de nom (HomePage importé et wrapper exporté)
2249
+ const importComponentName = homePageConfig.componentExport;
2250
+ const wrapperComponentName = `${importComponentName}AppWrapper`;
2251
+ const metadataImportAlias = homePageConfig.metadataExport
2252
+ ? `${homePageConfig.metadataExport} as ${homePageConfig.metadataExport}Original`
2253
+ : "";
2254
+
2255
+ const content = `// GENERATED BY LASTBRAIN MODULE BUILD
2256
+ // Homepage from ${homePageModule.moduleName}
2257
+ import { ${importComponentName} as ModuleHomePage${homePageConfig.metadataExport ? `, ${metadataImportAlias}` : ""} } from "${importPath}";
2258
+ import { footerConfig } from "../../../config/footer";
2259
+ import { LOCALE_CONFIG } from "../../../config/locales.generated";
2260
+ ${
2261
+ homePageConfig.metadataExport
2262
+ ? `\nexport async function generateMetadata(props: any) {
2263
+ return ${homePageConfig.metadataExport}Original({ ...props, localeConfig: LOCALE_CONFIG });
2264
+ }`
2265
+ : ""
2266
+ }
2267
+ export default function ${wrapperComponentName}() {
2268
+ return <ModuleHomePage footerConfig={footerConfig} localeConfig={LOCALE_CONFIG} />;
2269
+ }
2270
+ `;
2271
+
2272
+ fs.writeFileSync(homePagePath, content, "utf-8");
2273
+
2274
+ if (isDebugMode) {
2275
+ console.log(
2276
+ ` ✅ Generated homepage: ${homePagePath} (from ${homePageModule.moduleName})`
2277
+ );
2278
+ } else {
2279
+ console.log(`🏠 Generated homepage from ${homePageModule.moduleName}`);
2280
+ }
2281
+ }
2282
+
1823
2283
  export async function runModuleBuild() {
1824
2284
  ensureDirectory(appDirectory);
1825
2285
 
@@ -1886,6 +2346,12 @@ export async function runModuleBuild() {
1886
2346
  generateLayouts();
1887
2347
  copyModuleMigrations(moduleConfigs);
1888
2348
 
2349
+ // Générer la page d'accueil "/" si un module la définit
2350
+ if (isDebugMode) {
2351
+ console.log("🏠 Checking for homepage (/) definition...");
2352
+ }
2353
+ generateHomePage(moduleConfigs);
2354
+
1889
2355
  // Générer la configuration realtime
1890
2356
  if (isDebugMode) {
1891
2357
  console.log("🔄 Generating realtime configuration...");
@@ -1898,6 +2364,12 @@ export async function runModuleBuild() {
1898
2364
  }
1899
2365
  await generateUserTabsConfig(moduleConfigs);
1900
2366
 
2367
+ // Générer la configuration et la page auth dashboard
2368
+ if (isDebugMode) {
2369
+ console.log("📊 Generating auth dashboard configuration...");
2370
+ }
2371
+ await generateAuthDashboard(moduleConfigs);
2372
+
1901
2373
  // Générer la configuration des buckets storage
1902
2374
  if (isDebugMode) {
1903
2375
  console.log("🗄️ Generating storage buckets configuration...");
@@ -1922,6 +2394,12 @@ export async function runModuleBuild() {
1922
2394
  }
1923
2395
  await generateI18nFiles();
1924
2396
 
2397
+ // Générer la configuration des locales
2398
+ if (isDebugMode) {
2399
+ console.log("🌐 Generating locale configuration...");
2400
+ }
2401
+ await generateLocaleConfig();
2402
+
1925
2403
  // Message de succès final
1926
2404
  if (!isDebugMode) {
1927
2405
  console.log("\n✅ Module build completed successfully!");