@lastbrain/app 2.0.11 → 2.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/version.d.ts +1 -1
- package/dist/config/version.d.ts.map +1 -1
- package/dist/config/version.js +22 -19
- package/dist/layouts/AdminLayout.js +1 -1
- package/dist/scripts/init-app.js +8 -53
- package/dist/scripts/module-create.d.ts +1 -0
- package/dist/scripts/module-create.d.ts.map +1 -1
- package/dist/scripts/module-create.js +152 -31
- package/dist/styles.css +1 -1
- package/dist/templates/DefaultDoc.d.ts.map +1 -1
- package/dist/templates/DefaultDoc.js +1 -1
- package/package.json +23 -23
- package/src/config/version.ts +29 -0
- package/src/layouts/AdminLayout.tsx +1 -1
- package/src/scripts/init-app.ts +2 -2
- package/src/scripts/module-create.ts +230 -70
- package/src/templates/DefaultDoc.tsx +48 -2
|
@@ -23,6 +23,7 @@ export interface ModuleConfig {
|
|
|
23
23
|
moduleName: string;
|
|
24
24
|
pages: PageConfig[];
|
|
25
25
|
tables: TableConfig[];
|
|
26
|
+
isPro?: boolean;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
// Noms réservés pour éviter les collisions avec le routeur/app
|
|
@@ -116,57 +117,84 @@ function getLastBrainPackageVersions(rootDir: string): {
|
|
|
116
117
|
function generatePackageJson(
|
|
117
118
|
moduleName: string,
|
|
118
119
|
slug: string,
|
|
119
|
-
rootDir: string
|
|
120
|
+
rootDir: string,
|
|
121
|
+
isPro: boolean = false
|
|
120
122
|
): string {
|
|
121
123
|
const versions = getLastBrainPackageVersions(rootDir);
|
|
122
|
-
const moduleNameOnly = slug.replace("module-", "");
|
|
124
|
+
const moduleNameOnly = slug.replace("module-", "").replace("-pro", "");
|
|
123
125
|
const buildConfigExport = `./${moduleNameOnly}.build.config`;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
const packageJson: Record<string, unknown> = {
|
|
127
|
+
name: moduleName,
|
|
128
|
+
version: "0.1.0",
|
|
129
|
+
private: false,
|
|
130
|
+
type: "module",
|
|
131
|
+
main: "dist/index.js",
|
|
132
|
+
types: "dist/index.d.ts",
|
|
133
|
+
files: ["dist", "src", "supabase"],
|
|
134
|
+
scripts: {
|
|
135
|
+
build: "tsc -p tsconfig.json",
|
|
136
|
+
dev: "tsc -p tsconfig.json --watch",
|
|
137
|
+
},
|
|
138
|
+
dependencies: {
|
|
139
|
+
"@lastbrain/core": versions.core,
|
|
140
|
+
"@lastbrain/ui": versions.ui,
|
|
141
|
+
react: "^19.0.0",
|
|
142
|
+
"lucide-react": "^0.554.0",
|
|
143
|
+
"react-dom": "^19.0.0",
|
|
144
|
+
},
|
|
145
|
+
devDependencies: {
|
|
146
|
+
typescript: "^5.4.0",
|
|
147
|
+
},
|
|
148
|
+
exports: {
|
|
149
|
+
".": {
|
|
150
|
+
types: "./dist/index.d.ts",
|
|
151
|
+
default: "./dist/index.js",
|
|
136
152
|
},
|
|
137
|
-
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
react: "^19.0.0",
|
|
141
|
-
"lucide-react": "^0.554.0",
|
|
142
|
-
"react-dom": "^19.0.0",
|
|
153
|
+
"./server": {
|
|
154
|
+
types: "./dist/server.d.ts",
|
|
155
|
+
default: "./dist/server.js",
|
|
143
156
|
},
|
|
144
|
-
|
|
145
|
-
|
|
157
|
+
[buildConfigExport]: {
|
|
158
|
+
types: `./dist/${moduleNameOnly}.build.config.d.ts`,
|
|
159
|
+
default: `./dist/${moduleNameOnly}.build.config.js`,
|
|
146
160
|
},
|
|
147
|
-
|
|
148
|
-
"."
|
|
149
|
-
|
|
150
|
-
default: "./dist/index.js",
|
|
151
|
-
},
|
|
152
|
-
"./server": {
|
|
153
|
-
types: "./dist/server.d.ts",
|
|
154
|
-
default: "./dist/server.js",
|
|
155
|
-
},
|
|
156
|
-
[buildConfigExport]: {
|
|
157
|
-
types: `./dist/${moduleNameOnly}.build.config.d.ts`,
|
|
158
|
-
default: `./dist/${moduleNameOnly}.build.config.js`,
|
|
159
|
-
},
|
|
160
|
-
"./api/*": {
|
|
161
|
-
types: "./dist/api/*.d.ts",
|
|
162
|
-
default: "./dist/api/*.js",
|
|
163
|
-
},
|
|
161
|
+
"./api/*": {
|
|
162
|
+
types: "./dist/api/*.d.ts",
|
|
163
|
+
default: "./dist/api/*.js",
|
|
164
164
|
},
|
|
165
|
-
sideEffects: false,
|
|
166
165
|
},
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
sideEffects: false,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Ajouter les champs spécifiques selon le type (Pro ou Public)
|
|
170
|
+
if (isPro) {
|
|
171
|
+
packageJson.publishConfig = {
|
|
172
|
+
registry: "https://npm.pkg.github.com",
|
|
173
|
+
};
|
|
174
|
+
packageJson.release = {
|
|
175
|
+
type: "pro",
|
|
176
|
+
registry: "https://npm.pkg.github.com",
|
|
177
|
+
};
|
|
178
|
+
packageJson.repository = {
|
|
179
|
+
type: "git",
|
|
180
|
+
url: "https://github.com/Lastbrain-labs/starter",
|
|
181
|
+
directory: `packages/${slug}`,
|
|
182
|
+
};
|
|
183
|
+
} else {
|
|
184
|
+
packageJson.publishConfig = {
|
|
185
|
+
access: "public",
|
|
186
|
+
};
|
|
187
|
+
packageJson.release = {
|
|
188
|
+
type: "public",
|
|
189
|
+
};
|
|
190
|
+
packageJson.repository = {
|
|
191
|
+
type: "git",
|
|
192
|
+
url: "https://github.com/Lastbrain-labs/lb-starter",
|
|
193
|
+
directory: `packages/${slug}`,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return JSON.stringify(packageJson, null, 2);
|
|
170
198
|
}
|
|
171
199
|
|
|
172
200
|
/**
|
|
@@ -376,13 +404,31 @@ function toPascalCase(value: string): string {
|
|
|
376
404
|
}
|
|
377
405
|
|
|
378
406
|
function generateIndexTs(pages: PageConfig[], moduleNameOnly: string): string {
|
|
407
|
+
// Grouper les pages par nom pour détecter les doublons
|
|
408
|
+
const pagesByName = new Map<string, PageConfig[]>();
|
|
409
|
+
for (const page of pages) {
|
|
410
|
+
const key = page.name;
|
|
411
|
+
const existing = pagesByName.get(key) || [];
|
|
412
|
+
existing.push(page);
|
|
413
|
+
pagesByName.set(key, existing);
|
|
414
|
+
}
|
|
415
|
+
|
|
379
416
|
const exports = pages.map((page) => {
|
|
380
417
|
const componentName = toPascalCase(page.name);
|
|
381
|
-
|
|
382
|
-
|
|
418
|
+
|
|
419
|
+
// Si le même nom de page existe dans plusieurs sections, ajouter la section au nom du composant ET du fichier
|
|
420
|
+
const duplicates = pagesByName.get(page.name) || [];
|
|
421
|
+
const hasDuplicates = duplicates.length > 1;
|
|
422
|
+
const componentNameWithSection = hasDuplicates
|
|
423
|
+
? `${componentName}${toPascalCase(page.section)}`
|
|
424
|
+
: componentName;
|
|
425
|
+
|
|
426
|
+
return `export { ${componentNameWithSection}Page } from "./web/${page.section}/${componentNameWithSection}Page";`;
|
|
383
427
|
});
|
|
384
428
|
|
|
385
|
-
|
|
429
|
+
// Nettoyer le nom pour l'alias d'export (enlever -pro si présent)
|
|
430
|
+
const cleanedNameOnly = moduleNameOnly.replace(/-pro$/, "");
|
|
431
|
+
const moduleAlias = toPascalCase(cleanedNameOnly);
|
|
386
432
|
|
|
387
433
|
return `// Client Components
|
|
388
434
|
${exports.join("\n")}
|
|
@@ -418,22 +464,28 @@ ${exports.join("\n")}
|
|
|
418
464
|
/**
|
|
419
465
|
* Génère le contenu d'une page
|
|
420
466
|
*/
|
|
421
|
-
function generatePageComponent(
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
467
|
+
function generatePageComponent(
|
|
468
|
+
pageName: string,
|
|
469
|
+
section: string,
|
|
470
|
+
componentName?: string
|
|
471
|
+
): string {
|
|
472
|
+
const finalComponentName =
|
|
473
|
+
componentName ||
|
|
474
|
+
pageName
|
|
475
|
+
.split("-")
|
|
476
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
477
|
+
.join("");
|
|
426
478
|
|
|
427
479
|
return `"use client";
|
|
428
480
|
|
|
429
481
|
import { Card, CardBody, CardHeader } from "@lastbrain/ui";
|
|
430
482
|
|
|
431
|
-
export function ${
|
|
483
|
+
export function ${finalComponentName}Page() {
|
|
432
484
|
return (
|
|
433
485
|
<div className="container mx-auto p-6">
|
|
434
486
|
<Card>
|
|
435
487
|
<CardHeader>
|
|
436
|
-
<h1 className="text-2xl font-bold">${
|
|
488
|
+
<h1 className="text-2xl font-bold">${finalComponentName}</h1>
|
|
437
489
|
</CardHeader>
|
|
438
490
|
<CardBody>
|
|
439
491
|
<p className="text-default-600">
|
|
@@ -1207,6 +1259,25 @@ async function updateModuleRegistry(config: ModuleConfig, rootDir: string) {
|
|
|
1207
1259
|
chalk.yellow(" ⚠️ Fichier de registre non trouvé, création...")
|
|
1208
1260
|
);
|
|
1209
1261
|
// Si le fichier n'existe pas, on le crée avec le module actuel
|
|
1262
|
+
const moduleName = config.slug.replace("module-", "").replace("-pro", "");
|
|
1263
|
+
const moduleNameWithSuffix = config.isPro
|
|
1264
|
+
? `${moduleName}-pro`
|
|
1265
|
+
: moduleName;
|
|
1266
|
+
const moduleEntry = config.isPro
|
|
1267
|
+
? ` {
|
|
1268
|
+
name: "${moduleNameWithSuffix}",
|
|
1269
|
+
package: "${config.moduleName}",
|
|
1270
|
+
description: "Module ${config.moduleName}",
|
|
1271
|
+
emoji: "📦",
|
|
1272
|
+
isPro: true,
|
|
1273
|
+
},`
|
|
1274
|
+
: ` {
|
|
1275
|
+
name: "${moduleNameWithSuffix}",
|
|
1276
|
+
package: "${config.moduleName}",
|
|
1277
|
+
description: "Module ${config.moduleName}",
|
|
1278
|
+
emoji: "📦",
|
|
1279
|
+
},`;
|
|
1280
|
+
|
|
1210
1281
|
const content = `/**
|
|
1211
1282
|
* Configuration centralisée des modules LastBrain
|
|
1212
1283
|
* Ce fichier est auto-généré et maintenu par les scripts de gestion des modules
|
|
@@ -1218,15 +1289,11 @@ export interface ModuleMetadata {
|
|
|
1218
1289
|
description: string;
|
|
1219
1290
|
emoji: string;
|
|
1220
1291
|
version?: string;
|
|
1292
|
+
isPro?: boolean;
|
|
1221
1293
|
}
|
|
1222
1294
|
|
|
1223
1295
|
export const AVAILABLE_MODULES: ModuleMetadata[] = [
|
|
1224
|
-
|
|
1225
|
-
name: "${config.slug.replace("module-", "")}",
|
|
1226
|
-
package: "@lastbrain/${config.slug}",
|
|
1227
|
-
description: "Module ${config.moduleName}",
|
|
1228
|
-
emoji: "📦",
|
|
1229
|
-
},
|
|
1296
|
+
${moduleEntry}
|
|
1230
1297
|
];
|
|
1231
1298
|
|
|
1232
1299
|
/**
|
|
@@ -1258,22 +1325,43 @@ export function getAvailableModuleNames(): string[] {
|
|
|
1258
1325
|
try {
|
|
1259
1326
|
let content = await fs.readFile(moduleRegistryPath, "utf-8");
|
|
1260
1327
|
|
|
1261
|
-
const moduleName = config.slug.replace("module-", "");
|
|
1262
|
-
const
|
|
1263
|
-
|
|
1264
|
-
|
|
1328
|
+
const moduleName = config.slug.replace("module-", "").replace("-pro", "");
|
|
1329
|
+
const moduleNameWithSuffix = config.isPro
|
|
1330
|
+
? `${moduleName}-pro`
|
|
1331
|
+
: moduleName;
|
|
1332
|
+
const moduleEntry = config.isPro
|
|
1333
|
+
? ` {
|
|
1334
|
+
name: "${moduleNameWithSuffix}",
|
|
1335
|
+
package: "${config.moduleName}",
|
|
1336
|
+
description: "Module ${config.moduleName}",
|
|
1337
|
+
emoji: "📦",
|
|
1338
|
+
isPro: true,
|
|
1339
|
+
},`
|
|
1340
|
+
: ` {
|
|
1341
|
+
name: "${moduleNameWithSuffix}",
|
|
1342
|
+
package: "${config.moduleName}",
|
|
1265
1343
|
description: "Module ${config.moduleName}",
|
|
1266
1344
|
emoji: "📦",
|
|
1267
1345
|
},`;
|
|
1268
1346
|
|
|
1269
1347
|
// Vérifier si le module existe déjà
|
|
1270
|
-
if (content.includes(`name: "${
|
|
1348
|
+
if (content.includes(`name: "${moduleNameWithSuffix}"`)) {
|
|
1271
1349
|
console.log(
|
|
1272
|
-
chalk.yellow(
|
|
1350
|
+
chalk.yellow(
|
|
1351
|
+
` ⚠️ Module ${moduleNameWithSuffix} déjà présent dans le registre`
|
|
1352
|
+
)
|
|
1273
1353
|
);
|
|
1274
1354
|
return;
|
|
1275
1355
|
}
|
|
1276
1356
|
|
|
1357
|
+
// Vérifier si l'interface a le champ isPro
|
|
1358
|
+
if (!content.includes("isPro?: boolean;")) {
|
|
1359
|
+
content = content.replace(
|
|
1360
|
+
/version\?: string;\s*\}/,
|
|
1361
|
+
"version?: string;\n isPro?: boolean;\n}"
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1277
1365
|
// Trouver le tableau AVAILABLE_MODULES et ajouter le module
|
|
1278
1366
|
const arrayMatch = content.match(
|
|
1279
1367
|
/export const AVAILABLE_MODULES: ModuleMetadata\[\] = \[([\s\S]*?)\];/
|
|
@@ -1355,7 +1443,12 @@ export async function createModuleStructure(
|
|
|
1355
1443
|
console.log(chalk.yellow(" 📄 package.json"));
|
|
1356
1444
|
await fs.writeFile(
|
|
1357
1445
|
path.join(moduleDir, "package.json"),
|
|
1358
|
-
generatePackageJson(
|
|
1446
|
+
generatePackageJson(
|
|
1447
|
+
config.moduleName,
|
|
1448
|
+
config.slug,
|
|
1449
|
+
rootDir,
|
|
1450
|
+
config.isPro || false
|
|
1451
|
+
)
|
|
1359
1452
|
);
|
|
1360
1453
|
|
|
1361
1454
|
// Créer tsconfig.json
|
|
@@ -1390,20 +1483,38 @@ export async function createModuleStructure(
|
|
|
1390
1483
|
|
|
1391
1484
|
// Créer les pages
|
|
1392
1485
|
console.log(chalk.blue("\n📄 Création des pages..."));
|
|
1486
|
+
|
|
1487
|
+
// Grouper les pages par nom pour détecter les doublons
|
|
1488
|
+
const pagesByName = new Map<string, PageConfig[]>();
|
|
1489
|
+
for (const page of config.pages) {
|
|
1490
|
+
const key = page.name;
|
|
1491
|
+
const existing = pagesByName.get(key) || [];
|
|
1492
|
+
existing.push(page);
|
|
1493
|
+
pagesByName.set(key, existing);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1393
1496
|
for (const page of config.pages) {
|
|
1394
1497
|
const pagePath = path.join(moduleDir, "src", "web", page.section);
|
|
1395
1498
|
await fs.ensureDir(pagePath);
|
|
1396
1499
|
|
|
1397
|
-
const
|
|
1500
|
+
const baseName = page.name
|
|
1398
1501
|
.split("-")
|
|
1399
1502
|
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
1400
1503
|
.join("");
|
|
1401
|
-
|
|
1504
|
+
|
|
1505
|
+
// Si le même nom de page existe dans plusieurs sections, ajouter la section au nom du fichier
|
|
1506
|
+
const duplicates = pagesByName.get(page.name) || [];
|
|
1507
|
+
const hasDuplicates = duplicates.length > 1;
|
|
1508
|
+
const componentNameWithSection = hasDuplicates
|
|
1509
|
+
? `${baseName}${toPascalCase(page.section)}`
|
|
1510
|
+
: baseName;
|
|
1511
|
+
|
|
1512
|
+
const fileName = `${componentNameWithSection}Page.tsx`;
|
|
1402
1513
|
|
|
1403
1514
|
console.log(chalk.yellow(` 📄 src/web/${page.section}/${fileName}`));
|
|
1404
1515
|
await fs.writeFile(
|
|
1405
1516
|
path.join(pagePath, fileName),
|
|
1406
|
-
generatePageComponent(page.name, page.section)
|
|
1517
|
+
generatePageComponent(page.name, page.section, componentNameWithSection)
|
|
1407
1518
|
);
|
|
1408
1519
|
}
|
|
1409
1520
|
|
|
@@ -1684,10 +1795,59 @@ export async function createModule() {
|
|
|
1684
1795
|
});
|
|
1685
1796
|
}
|
|
1686
1797
|
|
|
1798
|
+
// 🛡️ VALIDATION : Détecter et corriger les collisions de noms de pages
|
|
1799
|
+
const pagesByName = new Map<string, PageConfig[]>();
|
|
1800
|
+
for (const page of pages) {
|
|
1801
|
+
const existing = pagesByName.get(page.name) || [];
|
|
1802
|
+
existing.push(page);
|
|
1803
|
+
pagesByName.set(page.name, existing);
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
const correctedPages: PageConfig[] = [];
|
|
1807
|
+
const collisionsDetected = new Set<string>();
|
|
1808
|
+
|
|
1809
|
+
for (const page of pages) {
|
|
1810
|
+
const duplicates = pagesByName.get(page.name) || [];
|
|
1811
|
+
if (duplicates.length > 1) {
|
|
1812
|
+
// Collision détectée ! Ajouter un suffixe pour différencier
|
|
1813
|
+
const correctedPage: PageConfig = {
|
|
1814
|
+
...page,
|
|
1815
|
+
name: `${page.name}-${page.section}`,
|
|
1816
|
+
path: `${page.path}-${page.section}`,
|
|
1817
|
+
};
|
|
1818
|
+
|
|
1819
|
+
// Afficher l'avertissement une fois par groupe de doublons
|
|
1820
|
+
if (!collisionsDetected.has(page.name)) {
|
|
1821
|
+
collisionsDetected.add(page.name);
|
|
1822
|
+
console.log(
|
|
1823
|
+
chalk.yellow(
|
|
1824
|
+
`\n⚠️ Collision détectée pour la page "${page.name}" (existe dans plusieurs sections)`
|
|
1825
|
+
)
|
|
1826
|
+
);
|
|
1827
|
+
console.log(
|
|
1828
|
+
chalk.gray(
|
|
1829
|
+
` Les pages seront renommées automatiquement pour éviter les conflits:`
|
|
1830
|
+
)
|
|
1831
|
+
);
|
|
1832
|
+
duplicates.forEach((dup) => {
|
|
1833
|
+
console.log(
|
|
1834
|
+
chalk.gray(
|
|
1835
|
+
` - [${dup.section}] ${dup.name} → ${dup.name}-${dup.section}`
|
|
1836
|
+
)
|
|
1837
|
+
);
|
|
1838
|
+
});
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
correctedPages.push(correctedPage);
|
|
1842
|
+
} else {
|
|
1843
|
+
correctedPages.push(page);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1687
1847
|
const config: ModuleConfig & { description?: string } = {
|
|
1688
1848
|
slug,
|
|
1689
1849
|
moduleName,
|
|
1690
|
-
pages,
|
|
1850
|
+
pages: correctedPages,
|
|
1691
1851
|
tables,
|
|
1692
1852
|
description,
|
|
1693
1853
|
};
|
|
@@ -1619,7 +1619,7 @@ export function DocUsageCustom() {
|
|
|
1619
1619
|
Module LastBrain
|
|
1620
1620
|
</p>
|
|
1621
1621
|
<p className="text-xs text-slate-500 dark:text-slate-500">
|
|
1622
|
-
<strong>Pages:</strong> 1 auth,
|
|
1622
|
+
<strong>Pages:</strong> 1 auth, 2 admin
|
|
1623
1623
|
</p>
|
|
1624
1624
|
|
|
1625
1625
|
<Snippet symbol="💻" hideSymbol className="text-sm">
|
|
@@ -1686,6 +1686,29 @@ export function DocUsageCustom() {
|
|
|
1686
1686
|
</span>
|
|
1687
1687
|
</CardBody>
|
|
1688
1688
|
</Card>
|
|
1689
|
+
<Card className="hover:shadow-lg transition-shadow border-l-4 border-l-purple-500">
|
|
1690
|
+
<CardBody className="space-y-3">
|
|
1691
|
+
<div className="flex items-start justify-between">
|
|
1692
|
+
<h3 className="text-lg font-semibold">Cj Analyzer Pro</h3>
|
|
1693
|
+
<span className="text-xs bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 px-2 py-1 rounded">
|
|
1694
|
+
PRO
|
|
1695
|
+
</span>
|
|
1696
|
+
</div>
|
|
1697
|
+
<p className="text-sm text-slate-600 dark:text-slate-400">
|
|
1698
|
+
Module LastBrain
|
|
1699
|
+
</p>
|
|
1700
|
+
<p className="text-xs text-slate-500 dark:text-slate-500">
|
|
1701
|
+
<strong>Pages:</strong> 4 admin
|
|
1702
|
+
</p>
|
|
1703
|
+
|
|
1704
|
+
<Snippet symbol="💻" hideSymbol className="text-sm">
|
|
1705
|
+
{`pnpm lastbrain add-module cj-analyzer-pro`}
|
|
1706
|
+
</Snippet>
|
|
1707
|
+
<span className="text-xs text-slate-500 dark:text-slate-400 inline-flex items-center gap-1">
|
|
1708
|
+
🔒 Documentation privée
|
|
1709
|
+
</span>
|
|
1710
|
+
</CardBody>
|
|
1711
|
+
</Card>
|
|
1689
1712
|
<Card className="hover:shadow-lg transition-shadow border-l-4 border-l-purple-500">
|
|
1690
1713
|
<CardBody className="space-y-3">
|
|
1691
1714
|
<div className="flex items-start justify-between">
|
|
@@ -1698,7 +1721,7 @@ export function DocUsageCustom() {
|
|
|
1698
1721
|
Module LastBrain
|
|
1699
1722
|
</p>
|
|
1700
1723
|
<p className="text-xs text-slate-500 dark:text-slate-500">
|
|
1701
|
-
<strong>Pages:</strong> 2 auth, 1 admin
|
|
1724
|
+
<strong>Pages:</strong> 2 publique(s), 2 auth, 1 admin
|
|
1702
1725
|
</p>
|
|
1703
1726
|
|
|
1704
1727
|
<Snippet symbol="💻" hideSymbol className="text-sm">
|
|
@@ -1877,6 +1900,29 @@ export function DocUsageCustom() {
|
|
|
1877
1900
|
</span>
|
|
1878
1901
|
</CardBody>
|
|
1879
1902
|
</Card>
|
|
1903
|
+
<Card className="hover:shadow-lg transition-shadow border-l-4 border-l-purple-500">
|
|
1904
|
+
<CardBody className="space-y-3">
|
|
1905
|
+
<div className="flex items-start justify-between">
|
|
1906
|
+
<h3 className="text-lg font-semibold">Shop Pro</h3>
|
|
1907
|
+
<span className="text-xs bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 px-2 py-1 rounded">
|
|
1908
|
+
PRO
|
|
1909
|
+
</span>
|
|
1910
|
+
</div>
|
|
1911
|
+
<p className="text-sm text-slate-600 dark:text-slate-400">
|
|
1912
|
+
Module LastBrain
|
|
1913
|
+
</p>
|
|
1914
|
+
<p className="text-xs text-slate-500 dark:text-slate-500">
|
|
1915
|
+
<strong>Pages:</strong> 3 admin
|
|
1916
|
+
</p>
|
|
1917
|
+
|
|
1918
|
+
<Snippet symbol="💻" hideSymbol className="text-sm">
|
|
1919
|
+
{`pnpm lastbrain add-module shop-pro`}
|
|
1920
|
+
</Snippet>
|
|
1921
|
+
<span className="text-xs text-slate-500 dark:text-slate-400 inline-flex items-center gap-1">
|
|
1922
|
+
🔒 Documentation privée
|
|
1923
|
+
</span>
|
|
1924
|
+
</CardBody>
|
|
1925
|
+
</Card>
|
|
1880
1926
|
<Card className="hover:shadow-lg transition-shadow border-l-4 border-l-blue-500">
|
|
1881
1927
|
<CardBody className="space-y-3">
|
|
1882
1928
|
<div className="flex items-start justify-between">
|