@lastbrain/app 2.0.12 → 2.0.18

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * AUTO-GENERATED FILE - Généré par scripts/version.js
2
+ * AUTO-GENERATED FILE - Généré par scripts/sync-versions.js
3
3
  * NE PAS MODIFIER MANUELLEMENT
4
4
  * Mise à jour automatique lors de pnpm version:patch/minor/major
5
5
  */
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../config/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAmBnD,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/config/version.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAsBnD,CAAC"}
@@ -1,25 +1,28 @@
1
1
  /**
2
- * AUTO-GENERATED FILE - Généré par scripts/version.js
2
+ * AUTO-GENERATED FILE - Généré par scripts/sync-versions.js
3
3
  * NE PAS MODIFIER MANUELLEMENT
4
4
  * Mise à jour automatique lors de pnpm version:patch/minor/major
5
5
  */
6
6
  export const PACKAGE_VERSIONS = {
7
- "@lastbrain-labs/module-billing-pro": "^2.0.8",
8
- "@lastbrain-labs/module-core-cart-pro": "^2.0.8",
9
- "@lastbrain-labs/module-core-commerce-pro": "^2.0.8",
10
- "@lastbrain-labs/module-core-order-pro": "^2.0.8",
11
- "@lastbrain-labs/module-core-payment-pro": "^2.0.8",
12
- "@lastbrain-labs/module-core-product-pro": "^2.0.8",
13
- "@lastbrain-labs/module-recipes-pro": "^2.0.8",
14
- "@lastbrain/app": "^2.0.9",
15
- "@lastbrain/core": "^2.0.8",
16
- "@lastbrain/module-ai": "^2.0.8",
17
- "@lastbrain/module-auth": "^2.0.9",
18
- "@lastbrain/module-legal": "^2.0.8",
19
- "@lastbrain/module-project-board": "^2.0.8",
20
- "@lastbrain/module-tasks": "^2.0.8",
21
- "@lastbrain/ui": "^2.0.8",
22
- "apps/recipe": "^2.0.8",
23
- "apps/test-app": "^2.0.8",
24
- "apps/test-module": "^2.0.8",
7
+ "@lastbrain-labs/module-billing-pro": "^2.0.14",
8
+ "@lastbrain-labs/module-cj-analyzer-pro": "^0.1.6",
9
+ "@lastbrain-labs/module-core-cart-pro": "^2.0.14",
10
+ "@lastbrain-labs/module-core-commerce-pro": "^2.0.14",
11
+ "@lastbrain-labs/module-core-order-pro": "^2.0.14",
12
+ "@lastbrain-labs/module-core-payment-pro": "^2.0.14",
13
+ "@lastbrain-labs/module-core-product-pro": "^2.0.14",
14
+ "@lastbrain-labs/module-recipes-pro": "^2.0.14",
15
+ "@lastbrain-labs/module-shop-pro": "^0.1.6",
16
+ "@lastbrain/app": "^2.0.17",
17
+ "@lastbrain/core": "^2.0.15",
18
+ "@lastbrain/module-ai": "^2.0.14",
19
+ "@lastbrain/module-auth": "^2.0.15",
20
+ "@lastbrain/module-legal": "^2.0.14",
21
+ "@lastbrain/module-project-board": "^2.0.14",
22
+ "@lastbrain/module-tasks": "^2.0.14",
23
+ "@lastbrain/ui": "^2.0.15",
24
+ "apps/recipe": "^2.0.10",
25
+ "apps/test-app": "^2.0.10",
26
+ "apps/test-module": "^2.0.10",
27
+ "lastbrain": "^2.0.10",
25
28
  };
@@ -1,4 +1,4 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  export function AdminLayout({ children }) {
3
- return _jsx("div", { className: "pt-18 px-2 md:px-5", children: children });
3
+ return _jsx("div", { className: "max-w-screen pt-12 px-2 md:px-5", children: children });
4
4
  }
@@ -1,4 +1,4 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  export function AuthLayout({ children }) {
3
- return _jsx("div", { className: "pt-18 px-2 md:px-5 ", children: children });
3
+ return _jsx("div", { className: "pt-18 px-2 md:px-5 max-w-screen", children: children });
4
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PublicLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/PublicLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAE1D,UAAU,iBAAiB;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,iBAAiB,2CAOzE"}
1
+ {"version":3,"file":"PublicLayout.d.ts","sourceRoot":"","sources":["../../src/layouts/PublicLayout.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAE1D,UAAU,iBAAiB;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,iBAAiB,2CASzE"}
@@ -2,5 +2,5 @@
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Footer } from "@lastbrain/ui";
4
4
  export function PublicLayout({ children, footerConfig }) {
5
- return (_jsxs(_Fragment, { children: [_jsx("section", { className: "pt-16 min-h-[calc(100vh)]", children: children }), footerConfig && _jsx(Footer, { config: footerConfig })] }));
5
+ return (_jsxs(_Fragment, { children: [_jsx("section", { className: "pt-16 min-h-[calc(100vh)] max-w-screen", children: children }), footerConfig && _jsx(Footer, { config: footerConfig })] }));
6
6
  }
@@ -5,5 +5,5 @@ import { AppProviders } from "./AppProviders";
5
5
  // Note: L'app Next.js doit importer son propre globals.css dans son layout
6
6
  // Note: La configuration realtime doit être fournie par l'app qui utilise ce layout
7
7
  export function RootLayout({ children, realtimeConfig = [], }) {
8
- return (_jsx("html", { lang: "fr", suppressHydrationWarning: true, children: _jsx("body", { className: "min-h-screen", children: _jsx(ThemeProvider, { attribute: "class", defaultTheme: "light", enableSystem: false, storageKey: "lastbrain-theme", children: _jsx(AppProviders, { realtimeConfig: realtimeConfig, children: _jsx("div", { className: " min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white", children: children }) }) }) }) }));
8
+ return (_jsx("html", { lang: "fr", suppressHydrationWarning: true, children: _jsx("body", { className: "min-h-screen max-w-screen", children: _jsx(ThemeProvider, { attribute: "class", defaultTheme: "light", enableSystem: false, storageKey: "lastbrain-theme", children: _jsx(AppProviders, { realtimeConfig: realtimeConfig, children: _jsx("div", { className: " min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-white", children: children }) }) }) }) }));
9
9
  }
@@ -5,6 +5,7 @@ import chalk from "chalk";
5
5
  import inquirer from "inquirer";
6
6
  import { execSync } from "child_process";
7
7
  import { AVAILABLE_MODULES } from "@lastbrain/core/config/modules";
8
+ import { PACKAGE_VERSIONS } from "../config/version.js";
8
9
  const __filename = fileURLToPath(import.meta.url);
9
10
  const __dirname = path.dirname(__filename);
10
11
  export async function initApp(options) {
@@ -250,59 +251,13 @@ function getLastBrainVersions(targetDir) {
250
251
  moduleAi: "workspace:*",
251
252
  };
252
253
  }
253
- // Hors monorepo, essayer de lire les versions depuis le monorepo source
254
- try {
255
- // Détecter si on est dans le contexte du package CLI en build
256
- const cliPkgPath = path.join(__dirname, "../../package.json");
257
- if (fs.existsSync(cliPkgPath)) {
258
- const cliPkg = JSON.parse(fs.readFileSync(cliPkgPath, "utf-8"));
259
- const cliVersion = `^${cliPkg.version}`;
260
- // Essayer de lire les versions depuis le monorepo (si disponible)
261
- const monorepoRoot = path.join(__dirname, "../../../../");
262
- const moduleAuthPkgPath = path.join(monorepoRoot, "packages/module-auth/package.json");
263
- const moduleAiPkgPath = path.join(monorepoRoot, "packages/module-ai/package.json");
264
- const corePkgPath = path.join(monorepoRoot, "packages/core/package.json");
265
- const uiPkgPath = path.join(monorepoRoot, "packages/ui/package.json");
266
- let moduleAuthVersion = "latest";
267
- let moduleAiVersion = "latest";
268
- let coreVersion = "latest";
269
- let uiVersion = "latest";
270
- // Lire les vraies versions depuis les package.json du monorepo
271
- if (fs.existsSync(moduleAuthPkgPath)) {
272
- const moduleAuthPkg = JSON.parse(fs.readFileSync(moduleAuthPkgPath, "utf-8"));
273
- moduleAuthVersion = `^${moduleAuthPkg.version}`;
274
- }
275
- if (fs.existsSync(moduleAiPkgPath)) {
276
- const moduleAiPkg = JSON.parse(fs.readFileSync(moduleAiPkgPath, "utf-8"));
277
- moduleAiVersion = `^${moduleAiPkg.version}`;
278
- }
279
- if (fs.existsSync(corePkgPath)) {
280
- const corePkg = JSON.parse(fs.readFileSync(corePkgPath, "utf-8"));
281
- coreVersion = `^${corePkg.version}`;
282
- }
283
- if (fs.existsSync(uiPkgPath)) {
284
- const uiPkg = JSON.parse(fs.readFileSync(uiPkgPath, "utf-8"));
285
- uiVersion = `^${uiPkg.version}`;
286
- }
287
- return {
288
- app: "^2.0.0",
289
- core: coreVersion === "latest" ? "^2.0.0" : coreVersion,
290
- ui: uiVersion === "latest" ? "^2.0.0" : uiVersion,
291
- moduleAuth: moduleAuthVersion === "latest" ? "^2.0.0" : moduleAuthVersion,
292
- moduleAi: moduleAiVersion,
293
- };
294
- }
295
- }
296
- catch (_error) {
297
- console.warn(chalk.yellow(`⚠️ Impossible de lire les versions locales (${_error}), utilisation de 'latest'`));
298
- }
299
- // Fallback: utiliser "latest"
254
+ // Hors monorepo, utiliser les versions publiées
300
255
  return {
301
- app: "latest",
302
- core: "latest",
303
- ui: "latest",
304
- moduleAuth: "latest",
305
- moduleAi: "latest",
256
+ app: PACKAGE_VERSIONS["@lastbrain/app"] || "latest",
257
+ core: PACKAGE_VERSIONS["@lastbrain/core"] || "latest",
258
+ ui: PACKAGE_VERSIONS["@lastbrain/ui"] || "latest",
259
+ moduleAuth: PACKAGE_VERSIONS["@lastbrain/module-auth"] || "latest",
260
+ moduleAi: PACKAGE_VERSIONS["@lastbrain/module-ai"] || "latest",
306
261
  };
307
262
  }
308
263
  async function addDependencies(targetDir, useHeroUI, withAuth, selectedModules = []) {
@@ -1838,7 +1793,7 @@ async function addScriptsToPackageJson(targetDir) {
1838
1793
  start: "next start",
1839
1794
  lint: "next lint",
1840
1795
  lastbrain: scriptsPrefix,
1841
- "build:modules": `${scriptsPrefix} module:build`,
1796
+ "build:modules": `${scriptsPrefix} module:build && prettier --write \\"**/*.{js,jsx,ts,tsx,json,md}\\"`,
1842
1797
  "db:migrations:sync": `${scriptsPrefix} db:migrations:sync`,
1843
1798
  "db:init": `${scriptsPrefix} db:init`,
1844
1799
  "readme:create": `${scriptsPrefix} readme:create`,
@@ -1 +1 @@
1
- {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAi6CA,wBAAsB,cAAc,kBA+FnC"}
1
+ {"version":3,"file":"module-build.d.ts","sourceRoot":"","sources":["../../src/scripts/module-build.ts"],"names":[],"mappings":"AAs6CA,wBAAsB,cAAc,kBA+FnC"}
@@ -236,9 +236,13 @@ export default function ${wrapperName}${hasDynamicParams ? "(props: Record<strin
236
236
  `;
237
237
  }
238
238
  else {
239
+ // Déterminer le chemin d'import
240
+ const importPath = page.entryPoint
241
+ ? `${moduleConfig.moduleName}/${page.entryPoint}`
242
+ : moduleConfig.moduleName;
239
243
  content = `// GENERATED BY LASTBRAIN MODULE BUILD
240
- import { ${page.componentExport} } from "${moduleConfig.moduleName}";
241
-
244
+ import { ${page.componentExport} } from "${importPath}";
245
+ ${page.metadataExport ? `\nexport { ${page.metadataExport} as generateMetadata } from "${importPath}";\n` : ""}
242
246
  export default function ${wrapperName}${hasDynamicParams ? "(props: Record<string, unknown>)" : "()"} {
243
247
  return <${page.componentExport} ${hasDynamicParams ? "{...props}" : ""} />;
244
248
  }
@@ -12,6 +12,7 @@ export interface ModuleConfig {
12
12
  moduleName: string;
13
13
  pages: PageConfig[];
14
14
  tables: TableConfig[];
15
+ isPro?: boolean;
15
16
  }
16
17
  /**
17
18
  * Trouve le répertoire racine du workspace (avec pnpm-workspace.yaml)
@@ -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;AAswCD;;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"}
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;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA61CD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAiB1C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,MAAM,iBA2MhB;AAED;;GAEG;AACH,wBAAsB,YAAY,kBAuOjC"}
@@ -73,11 +73,11 @@ function getLastBrainPackageVersions(rootDir) {
73
73
  /**
74
74
  * Génère le contenu du package.json
75
75
  */
76
- function generatePackageJson(moduleName, slug, rootDir) {
76
+ function generatePackageJson(moduleName, slug, rootDir, isPro = false) {
77
77
  const versions = getLastBrainPackageVersions(rootDir);
78
- const moduleNameOnly = slug.replace("module-", "");
78
+ const moduleNameOnly = slug.replace("module-", "").replace("-pro", "");
79
79
  const buildConfigExport = `./${moduleNameOnly}.build.config`;
80
- return JSON.stringify({
80
+ const packageJson = {
81
81
  name: moduleName,
82
82
  version: "0.1.0",
83
83
  private: false,
@@ -118,7 +118,36 @@ function generatePackageJson(moduleName, slug, rootDir) {
118
118
  },
119
119
  },
120
120
  sideEffects: false,
121
- }, null, 2);
121
+ };
122
+ // Ajouter les champs spécifiques selon le type (Pro ou Public)
123
+ if (isPro) {
124
+ packageJson.publishConfig = {
125
+ registry: "https://npm.pkg.github.com",
126
+ };
127
+ packageJson.release = {
128
+ type: "pro",
129
+ registry: "https://npm.pkg.github.com",
130
+ };
131
+ packageJson.repository = {
132
+ type: "git",
133
+ url: "https://github.com/Lastbrain-labs/starter",
134
+ directory: `packages/${slug}`,
135
+ };
136
+ }
137
+ else {
138
+ packageJson.publishConfig = {
139
+ access: "public",
140
+ };
141
+ packageJson.release = {
142
+ type: "public",
143
+ };
144
+ packageJson.repository = {
145
+ type: "git",
146
+ url: "https://github.com/Lastbrain-labs/lb-starter",
147
+ directory: `packages/${slug}`,
148
+ };
149
+ }
150
+ return JSON.stringify(packageJson, null, 2);
122
151
  }
123
152
  /**
124
153
  * Génère le contenu du tsconfig.json
@@ -301,12 +330,27 @@ function toPascalCase(value) {
301
330
  .join("");
302
331
  }
303
332
  function generateIndexTs(pages, moduleNameOnly) {
333
+ // Grouper les pages par nom pour détecter les doublons
334
+ const pagesByName = new Map();
335
+ for (const page of pages) {
336
+ const key = page.name;
337
+ const existing = pagesByName.get(key) || [];
338
+ existing.push(page);
339
+ pagesByName.set(key, existing);
340
+ }
304
341
  const exports = pages.map((page) => {
305
342
  const componentName = toPascalCase(page.name);
306
- const fileName = toPascalCase(page.name);
307
- return `export { ${componentName}Page } from "./web/${page.section}/${fileName}Page";`;
343
+ // Si le même nom de page existe dans plusieurs sections, ajouter la section au nom du composant ET du fichier
344
+ const duplicates = pagesByName.get(page.name) || [];
345
+ const hasDuplicates = duplicates.length > 1;
346
+ const componentNameWithSection = hasDuplicates
347
+ ? `${componentName}${toPascalCase(page.section)}`
348
+ : componentName;
349
+ return `export { ${componentNameWithSection}Page } from "./web/${page.section}/${componentNameWithSection}Page";`;
308
350
  });
309
- const moduleAlias = toPascalCase(moduleNameOnly);
351
+ // Nettoyer le nom pour l'alias d'export (enlever -pro si présent)
352
+ const cleanedNameOnly = moduleNameOnly.replace(/-pro$/, "");
353
+ const moduleAlias = toPascalCase(cleanedNameOnly);
310
354
  return `// Client Components
311
355
  ${exports.join("\n")}
312
356
 
@@ -335,21 +379,22 @@ ${exports.join("\n")}
335
379
  /**
336
380
  * Génère le contenu d'une page
337
381
  */
338
- function generatePageComponent(pageName, section) {
339
- const componentName = pageName
340
- .split("-")
341
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
342
- .join("");
382
+ function generatePageComponent(pageName, section, componentName) {
383
+ const finalComponentName = componentName ||
384
+ pageName
385
+ .split("-")
386
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
387
+ .join("");
343
388
  return `"use client";
344
389
 
345
390
  import { Card, CardBody, CardHeader } from "@lastbrain/ui";
346
391
 
347
- export function ${componentName}Page() {
392
+ export function ${finalComponentName}Page() {
348
393
  return (
349
394
  <div className="container mx-auto p-6">
350
395
  <Card>
351
396
  <CardHeader>
352
- <h1 className="text-2xl font-bold">${componentName}</h1>
397
+ <h1 className="text-2xl font-bold">${finalComponentName}</h1>
353
398
  </CardHeader>
354
399
  <CardBody>
355
400
  <p className="text-default-600">
@@ -1051,6 +1096,24 @@ async function updateModuleRegistry(config, rootDir) {
1051
1096
  if (!fs.existsSync(moduleRegistryPath)) {
1052
1097
  console.log(chalk.yellow(" ⚠️ Fichier de registre non trouvé, création..."));
1053
1098
  // Si le fichier n'existe pas, on le crée avec le module actuel
1099
+ const moduleName = config.slug.replace("module-", "").replace("-pro", "");
1100
+ const moduleNameWithSuffix = config.isPro
1101
+ ? `${moduleName}-pro`
1102
+ : moduleName;
1103
+ const moduleEntry = config.isPro
1104
+ ? ` {
1105
+ name: "${moduleNameWithSuffix}",
1106
+ package: "${config.moduleName}",
1107
+ description: "Module ${config.moduleName}",
1108
+ emoji: "📦",
1109
+ isPro: true,
1110
+ },`
1111
+ : ` {
1112
+ name: "${moduleNameWithSuffix}",
1113
+ package: "${config.moduleName}",
1114
+ description: "Module ${config.moduleName}",
1115
+ emoji: "📦",
1116
+ },`;
1054
1117
  const content = `/**
1055
1118
  * Configuration centralisée des modules LastBrain
1056
1119
  * Ce fichier est auto-généré et maintenu par les scripts de gestion des modules
@@ -1062,15 +1125,11 @@ export interface ModuleMetadata {
1062
1125
  description: string;
1063
1126
  emoji: string;
1064
1127
  version?: string;
1128
+ isPro?: boolean;
1065
1129
  }
1066
1130
 
1067
1131
  export const AVAILABLE_MODULES: ModuleMetadata[] = [
1068
- {
1069
- name: "${config.slug.replace("module-", "")}",
1070
- package: "@lastbrain/${config.slug}",
1071
- description: "Module ${config.moduleName}",
1072
- emoji: "📦",
1073
- },
1132
+ ${moduleEntry}
1074
1133
  ];
1075
1134
 
1076
1135
  /**
@@ -1100,18 +1159,33 @@ export function getAvailableModuleNames(): string[] {
1100
1159
  }
1101
1160
  try {
1102
1161
  let content = await fs.readFile(moduleRegistryPath, "utf-8");
1103
- const moduleName = config.slug.replace("module-", "");
1104
- const moduleEntry = ` {
1105
- name: "${moduleName}",
1106
- package: "@lastbrain/${config.slug}",
1162
+ const moduleName = config.slug.replace("module-", "").replace("-pro", "");
1163
+ const moduleNameWithSuffix = config.isPro
1164
+ ? `${moduleName}-pro`
1165
+ : moduleName;
1166
+ const moduleEntry = config.isPro
1167
+ ? ` {
1168
+ name: "${moduleNameWithSuffix}",
1169
+ package: "${config.moduleName}",
1170
+ description: "Module ${config.moduleName}",
1171
+ emoji: "📦",
1172
+ isPro: true,
1173
+ },`
1174
+ : ` {
1175
+ name: "${moduleNameWithSuffix}",
1176
+ package: "${config.moduleName}",
1107
1177
  description: "Module ${config.moduleName}",
1108
1178
  emoji: "📦",
1109
1179
  },`;
1110
1180
  // Vérifier si le module existe déjà
1111
- if (content.includes(`name: "${moduleName}"`)) {
1112
- console.log(chalk.yellow(` ⚠️ Module ${moduleName} déjà présent dans le registre`));
1181
+ if (content.includes(`name: "${moduleNameWithSuffix}"`)) {
1182
+ console.log(chalk.yellow(` ⚠️ Module ${moduleNameWithSuffix} déjà présent dans le registre`));
1113
1183
  return;
1114
1184
  }
1185
+ // Vérifier si l'interface a le champ isPro
1186
+ if (!content.includes("isPro?: boolean;")) {
1187
+ content = content.replace(/version\?: string;\s*\}/, "version?: string;\n isPro?: boolean;\n}");
1188
+ }
1115
1189
  // Trouver le tableau AVAILABLE_MODULES et ajouter le module
1116
1190
  const arrayMatch = content.match(/export const AVAILABLE_MODULES: ModuleMetadata\[\] = \[([\s\S]*?)\];/);
1117
1191
  if (arrayMatch) {
@@ -1165,7 +1239,7 @@ export async function createModuleStructure(config, rootDir) {
1165
1239
  await fs.ensureDir(path.join(moduleDir, "supabase", "migrations-down"));
1166
1240
  // Créer package.json
1167
1241
  console.log(chalk.yellow(" 📄 package.json"));
1168
- await fs.writeFile(path.join(moduleDir, "package.json"), generatePackageJson(config.moduleName, config.slug, rootDir));
1242
+ await fs.writeFile(path.join(moduleDir, "package.json"), generatePackageJson(config.moduleName, config.slug, rootDir, config.isPro || false));
1169
1243
  // Créer tsconfig.json
1170
1244
  console.log(chalk.yellow(" 📄 tsconfig.json"));
1171
1245
  await fs.writeFile(path.join(moduleDir, "tsconfig.json"), generateTsConfig());
@@ -1184,16 +1258,30 @@ export async function createModuleStructure(config, rootDir) {
1184
1258
  await fs.writeFile(path.join(moduleDir, "src", "components", "Doc.tsx"), generateDocComponent(config));
1185
1259
  // Créer les pages
1186
1260
  console.log(chalk.blue("\n📄 Création des pages..."));
1261
+ // Grouper les pages par nom pour détecter les doublons
1262
+ const pagesByName = new Map();
1263
+ for (const page of config.pages) {
1264
+ const key = page.name;
1265
+ const existing = pagesByName.get(key) || [];
1266
+ existing.push(page);
1267
+ pagesByName.set(key, existing);
1268
+ }
1187
1269
  for (const page of config.pages) {
1188
1270
  const pagePath = path.join(moduleDir, "src", "web", page.section);
1189
1271
  await fs.ensureDir(pagePath);
1190
- const componentName = page.name
1272
+ const baseName = page.name
1191
1273
  .split("-")
1192
1274
  .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
1193
1275
  .join("");
1194
- const fileName = `${componentName}Page.tsx`;
1276
+ // Si le même nom de page existe dans plusieurs sections, ajouter la section au nom du fichier
1277
+ const duplicates = pagesByName.get(page.name) || [];
1278
+ const hasDuplicates = duplicates.length > 1;
1279
+ const componentNameWithSection = hasDuplicates
1280
+ ? `${baseName}${toPascalCase(page.section)}`
1281
+ : baseName;
1282
+ const fileName = `${componentNameWithSection}Page.tsx`;
1195
1283
  console.log(chalk.yellow(` 📄 src/web/${page.section}/${fileName}`));
1196
- await fs.writeFile(path.join(pagePath, fileName), generatePageComponent(page.name, page.section));
1284
+ await fs.writeFile(path.join(pagePath, fileName), generatePageComponent(page.name, page.section, componentNameWithSection));
1197
1285
  }
1198
1286
  // Créer les routes API
1199
1287
  if (config.tables.length > 0) {
@@ -1401,10 +1489,43 @@ export async function createModule() {
1401
1489
  sections,
1402
1490
  });
1403
1491
  }
1492
+ // 🛡️ VALIDATION : Détecter et corriger les collisions de noms de pages
1493
+ const pagesByName = new Map();
1494
+ for (const page of pages) {
1495
+ const existing = pagesByName.get(page.name) || [];
1496
+ existing.push(page);
1497
+ pagesByName.set(page.name, existing);
1498
+ }
1499
+ const correctedPages = [];
1500
+ const collisionsDetected = new Set();
1501
+ for (const page of pages) {
1502
+ const duplicates = pagesByName.get(page.name) || [];
1503
+ if (duplicates.length > 1) {
1504
+ // Collision détectée ! Ajouter un suffixe pour différencier
1505
+ const correctedPage = {
1506
+ ...page,
1507
+ name: `${page.name}-${page.section}`,
1508
+ path: `${page.path}-${page.section}`,
1509
+ };
1510
+ // Afficher l'avertissement une fois par groupe de doublons
1511
+ if (!collisionsDetected.has(page.name)) {
1512
+ collisionsDetected.add(page.name);
1513
+ console.log(chalk.yellow(`\n⚠️ Collision détectée pour la page "${page.name}" (existe dans plusieurs sections)`));
1514
+ console.log(chalk.gray(` Les pages seront renommées automatiquement pour éviter les conflits:`));
1515
+ duplicates.forEach((dup) => {
1516
+ console.log(chalk.gray(` - [${dup.section}] ${dup.name} → ${dup.name}-${dup.section}`));
1517
+ });
1518
+ }
1519
+ correctedPages.push(correctedPage);
1520
+ }
1521
+ else {
1522
+ correctedPages.push(page);
1523
+ }
1524
+ }
1404
1525
  const config = {
1405
1526
  slug,
1406
1527
  moduleName,
1407
- pages,
1528
+ pages: correctedPages,
1408
1529
  tables,
1409
1530
  description,
1410
1531
  };