@lastbrain/app 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scripts/init-app.js +7 -10
- package/package.json +3 -2
- package/src/app-shell/(admin)/layout.tsx +13 -0
- package/src/app-shell/(auth)/layout.tsx +13 -0
- package/src/app-shell/(public)/page.tsx +11 -0
- package/src/app-shell/layout.tsx +5 -0
- package/src/app-shell/not-found.tsx +28 -0
- package/src/auth/authHelpers.ts +24 -0
- package/src/auth/useAuthSession.ts +54 -0
- package/src/cli.ts +96 -0
- package/src/index.ts +21 -0
- package/src/layouts/AdminLayout.tsx +7 -0
- package/src/layouts/AppProviders.tsx +61 -0
- package/src/layouts/AuthLayout.tsx +7 -0
- package/src/layouts/PublicLayout.tsx +7 -0
- package/src/layouts/RootLayout.tsx +27 -0
- package/src/modules/module-loader.ts +14 -0
- package/src/scripts/README.md +262 -0
- package/src/scripts/db-init.ts +338 -0
- package/src/scripts/db-migrations-sync.ts +86 -0
- package/src/scripts/dev-sync.ts +218 -0
- package/src/scripts/init-app.ts +1076 -0
- package/src/scripts/module-add.ts +242 -0
- package/src/scripts/module-build.ts +502 -0
- package/src/scripts/module-create.ts +809 -0
- package/src/scripts/module-list.ts +37 -0
- package/src/scripts/module-remove.ts +367 -0
- package/src/scripts/readme-build.ts +60 -0
- package/src/styles.css +3 -0
- package/src/templates/AuthGuidePage.tsx +68 -0
- package/src/templates/DefaultDoc.tsx +462 -0
- package/src/templates/DocPage.tsx +381 -0
- package/src/templates/DocsPageWithModules.tsx +22 -0
- package/src/templates/MigrationsGuidePage.tsx +61 -0
- package/src/templates/ModuleGuidePage.tsx +71 -0
- package/src/templates/SimpleDocPage.tsx +587 -0
- package/src/templates/SimpleHomePage.tsx +385 -0
- package/src/templates/env.example/.env.example +6 -0
- package/src/templates/migrations/20201010100000_app_base.sql +228 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
// GENERATED BY LASTBRAIN TEMPLATE (Documentation Page)
|
|
2
|
+
"use client";
|
|
3
|
+
import React, { useState, useEffect } from "react";
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardBody,
|
|
7
|
+
CardHeader,
|
|
8
|
+
Listbox,
|
|
9
|
+
ListboxItem,
|
|
10
|
+
Chip,
|
|
11
|
+
Button,
|
|
12
|
+
Drawer,
|
|
13
|
+
DrawerContent,
|
|
14
|
+
DrawerHeader,
|
|
15
|
+
DrawerBody,
|
|
16
|
+
Snippet,
|
|
17
|
+
} from "@lastbrain/ui";
|
|
18
|
+
import {
|
|
19
|
+
Menu,
|
|
20
|
+
Home,
|
|
21
|
+
Sparkles,
|
|
22
|
+
Rocket,
|
|
23
|
+
Building2,
|
|
24
|
+
Package,
|
|
25
|
+
Database,
|
|
26
|
+
Palette,
|
|
27
|
+
BookOpen,
|
|
28
|
+
Link,
|
|
29
|
+
Blocks,
|
|
30
|
+
} from "lucide-react";
|
|
31
|
+
import { DefaultDocumentation } from "./DefaultDoc.js";
|
|
32
|
+
|
|
33
|
+
interface ModuleDocConfig {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
content: React.ReactNode;
|
|
38
|
+
available: boolean;
|
|
39
|
+
color?:
|
|
40
|
+
| "primary"
|
|
41
|
+
| "secondary"
|
|
42
|
+
| "success"
|
|
43
|
+
| "danger"
|
|
44
|
+
| "warning"
|
|
45
|
+
| "default"
|
|
46
|
+
| undefined;
|
|
47
|
+
number?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface DocPageProps {
|
|
51
|
+
modules?: ModuleDocConfig[];
|
|
52
|
+
defaultContent?: React.ReactNode;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function DocPage({ modules = [], defaultContent }: DocPageProps) {
|
|
56
|
+
const [selectedModule, setSelectedModule] = useState<string>("default");
|
|
57
|
+
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
|
58
|
+
|
|
59
|
+
// Détecter la section visible lors du scroll
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const handleScroll = () => {
|
|
62
|
+
const sections = [
|
|
63
|
+
"default",
|
|
64
|
+
"section-welcome",
|
|
65
|
+
"section-quickstart",
|
|
66
|
+
"section-architecture",
|
|
67
|
+
"section-create-module",
|
|
68
|
+
"section-database",
|
|
69
|
+
"section-ui",
|
|
70
|
+
"section-module-docs",
|
|
71
|
+
"section-links",
|
|
72
|
+
...(modules.length > 0 ? ["section-modules"] : []),
|
|
73
|
+
...modules.map((m) => `module-${m.id}`),
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// Trouver la section actuellement visible
|
|
77
|
+
let currentSection = "default";
|
|
78
|
+
|
|
79
|
+
// Si on est tout en haut de la page
|
|
80
|
+
if (window.scrollY < 100) {
|
|
81
|
+
currentSection = "default";
|
|
82
|
+
} else {
|
|
83
|
+
// Parcourir les sections pour trouver celle qui est visible
|
|
84
|
+
for (const sectionId of sections) {
|
|
85
|
+
if (sectionId === "default") continue;
|
|
86
|
+
|
|
87
|
+
const element = document.getElementById(sectionId);
|
|
88
|
+
if (element) {
|
|
89
|
+
const rect = element.getBoundingClientRect();
|
|
90
|
+
// Section est visible si son top est dans la moitié supérieure de l'écran
|
|
91
|
+
if (rect.top <= window.innerHeight / 3 && rect.bottom >= 0) {
|
|
92
|
+
currentSection = sectionId.startsWith("module-")
|
|
93
|
+
? sectionId.replace("module-", "")
|
|
94
|
+
: sectionId;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setSelectedModule(currentSection);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Écouter le scroll
|
|
105
|
+
window.addEventListener("scroll", handleScroll);
|
|
106
|
+
// Appeler une fois au chargement
|
|
107
|
+
handleScroll();
|
|
108
|
+
|
|
109
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
110
|
+
}, [modules]);
|
|
111
|
+
|
|
112
|
+
const scrollToSection = (sectionId: string) => {
|
|
113
|
+
setIsDrawerOpen(false); // Fermer le drawer après sélection
|
|
114
|
+
if (sectionId === "default") {
|
|
115
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const elementId = sectionId.startsWith("section-")
|
|
120
|
+
? sectionId
|
|
121
|
+
: `module-${sectionId}`;
|
|
122
|
+
const element = document.getElementById(elementId);
|
|
123
|
+
if (element) {
|
|
124
|
+
const yOffset = -70;
|
|
125
|
+
const y =
|
|
126
|
+
element.getBoundingClientRect().top + window.pageYOffset + yOffset;
|
|
127
|
+
window.scrollTo({ top: y, behavior: "smooth" });
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const navigationItems = [
|
|
132
|
+
{
|
|
133
|
+
id: "default",
|
|
134
|
+
name: "Documentation",
|
|
135
|
+
description: "Accueil",
|
|
136
|
+
icon: Home,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "section-welcome",
|
|
140
|
+
name: "Bienvenue",
|
|
141
|
+
description: "",
|
|
142
|
+
icon: Sparkles,
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "section-quickstart",
|
|
146
|
+
name: "Démarrage rapide",
|
|
147
|
+
description: "",
|
|
148
|
+
icon: Rocket,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: "section-architecture",
|
|
152
|
+
name: "Architecture",
|
|
153
|
+
description: "",
|
|
154
|
+
icon: Building2,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "section-create-module",
|
|
158
|
+
name: "Créer un module",
|
|
159
|
+
description: "",
|
|
160
|
+
icon: Package,
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: "section-database",
|
|
164
|
+
name: "Base de données",
|
|
165
|
+
description: "",
|
|
166
|
+
icon: Database,
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: "section-ui",
|
|
170
|
+
name: "Interface utilisateur",
|
|
171
|
+
description: "",
|
|
172
|
+
icon: Palette,
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: "section-module-docs",
|
|
176
|
+
name: "Doc des modules",
|
|
177
|
+
description: "",
|
|
178
|
+
icon: BookOpen,
|
|
179
|
+
},
|
|
180
|
+
{ id: "section-links", name: "Liens utiles", description: "", icon: Link },
|
|
181
|
+
...(modules.length > 0
|
|
182
|
+
? [
|
|
183
|
+
{
|
|
184
|
+
id: "section-modules",
|
|
185
|
+
name: "Modules disponibles",
|
|
186
|
+
description: "",
|
|
187
|
+
icon: Blocks,
|
|
188
|
+
number: modules.length,
|
|
189
|
+
},
|
|
190
|
+
]
|
|
191
|
+
: []),
|
|
192
|
+
// Afficher seulement les modules actifs dans la navigation
|
|
193
|
+
...modules
|
|
194
|
+
.filter((m) => m.available)
|
|
195
|
+
.map((m) => ({
|
|
196
|
+
id: m.id,
|
|
197
|
+
name: m.name,
|
|
198
|
+
description: m.description,
|
|
199
|
+
icon: Package,
|
|
200
|
+
color: "primary",
|
|
201
|
+
})),
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
const NavigationListbox = () => (
|
|
205
|
+
<Listbox
|
|
206
|
+
aria-label="Navigation"
|
|
207
|
+
selectionMode="single"
|
|
208
|
+
selectedKeys={selectedModule ? [selectedModule] : []}
|
|
209
|
+
onSelectionChange={(keys) => {
|
|
210
|
+
const key = Array.from(keys)[0] as string;
|
|
211
|
+
if (key) {
|
|
212
|
+
scrollToSection(key);
|
|
213
|
+
} else {
|
|
214
|
+
setSelectedModule("");
|
|
215
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
216
|
+
}
|
|
217
|
+
}}
|
|
218
|
+
items={navigationItems}
|
|
219
|
+
>
|
|
220
|
+
{(item: any) => {
|
|
221
|
+
const IconComponent = item.icon;
|
|
222
|
+
return (
|
|
223
|
+
<ListboxItem
|
|
224
|
+
key={item.id}
|
|
225
|
+
textValue={item.name}
|
|
226
|
+
description={item.description}
|
|
227
|
+
color={item.color}
|
|
228
|
+
variant="solid"
|
|
229
|
+
endContent={
|
|
230
|
+
item.number && (
|
|
231
|
+
<Chip size="sm" color="primary">
|
|
232
|
+
{item.number ?? 0}
|
|
233
|
+
</Chip>
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
className={`${
|
|
237
|
+
selectedModule === item.id ? "bg-default-200/40" : ""
|
|
238
|
+
}`}
|
|
239
|
+
startContent={<IconComponent size={18} className="shrink-0" />}
|
|
240
|
+
>
|
|
241
|
+
{item.name}
|
|
242
|
+
</ListboxItem>
|
|
243
|
+
);
|
|
244
|
+
}}
|
|
245
|
+
</Listbox>
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<div className="w-full pt-8 md:pt-12 pb-24 lg:pb-8">
|
|
250
|
+
<div className="container mx-auto md:px-4 py-8">
|
|
251
|
+
{/* Mobile menu button */}
|
|
252
|
+
<div className="fixed w-full h-16 left-0 bottom-0 bg-background/20 backdrop-blur-lg z-50 lg:hidden p-2">
|
|
253
|
+
<div className="flex justify-center">
|
|
254
|
+
<Button
|
|
255
|
+
isIconOnly
|
|
256
|
+
variant="solid"
|
|
257
|
+
onPress={() => setIsDrawerOpen(true)}
|
|
258
|
+
>
|
|
259
|
+
<Menu size={24} />
|
|
260
|
+
</Button>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
{/* Mobile Drawer */}
|
|
265
|
+
<Drawer
|
|
266
|
+
isOpen={isDrawerOpen}
|
|
267
|
+
onOpenChange={setIsDrawerOpen}
|
|
268
|
+
placement="left"
|
|
269
|
+
>
|
|
270
|
+
<DrawerContent>
|
|
271
|
+
<DrawerHeader>
|
|
272
|
+
<h2 className="text-xl font-semibold">Navigation</h2>
|
|
273
|
+
</DrawerHeader>
|
|
274
|
+
<DrawerBody>
|
|
275
|
+
<NavigationListbox />
|
|
276
|
+
</DrawerBody>
|
|
277
|
+
</DrawerContent>
|
|
278
|
+
</Drawer>
|
|
279
|
+
|
|
280
|
+
<div className="flex gap-8">
|
|
281
|
+
{/* Desktop Navigation sidebar */}
|
|
282
|
+
<aside className="hidden lg:block w-64 shrink-0 sticky top-18 self-start">
|
|
283
|
+
<Card>
|
|
284
|
+
<CardHeader className="pb-2">
|
|
285
|
+
<h2 className="text-xl font-semibold">Navigation</h2>
|
|
286
|
+
</CardHeader>
|
|
287
|
+
<CardBody>
|
|
288
|
+
<NavigationListbox />
|
|
289
|
+
</CardBody>
|
|
290
|
+
</Card>
|
|
291
|
+
</aside>
|
|
292
|
+
|
|
293
|
+
{/* Main content */}
|
|
294
|
+
<main className="flex-1 w-full min-w-0 space-y-8">
|
|
295
|
+
{/* Default documentation */}
|
|
296
|
+
{defaultContent ? (
|
|
297
|
+
<div>{defaultContent}</div>
|
|
298
|
+
) : (
|
|
299
|
+
<DefaultDocumentation />
|
|
300
|
+
)}
|
|
301
|
+
|
|
302
|
+
{/* Modules section */}
|
|
303
|
+
{modules.length > 0 && (
|
|
304
|
+
<div className="space-y-6">
|
|
305
|
+
<Card id="section-modules" className="scroll-mt-32">
|
|
306
|
+
<CardHeader>
|
|
307
|
+
<h2 className="text-2xl font-semibold">
|
|
308
|
+
Modules disponibles
|
|
309
|
+
</h2>
|
|
310
|
+
</CardHeader>
|
|
311
|
+
<CardBody>
|
|
312
|
+
<p className="text-slate-600 dark:text-slate-400 mb-4">
|
|
313
|
+
Voici la liste de tous les modules disponibles dans
|
|
314
|
+
LastBrain. Les modules actifs sont utilisés dans votre
|
|
315
|
+
application.
|
|
316
|
+
</p>
|
|
317
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
318
|
+
{modules.map((module) => (
|
|
319
|
+
<Card
|
|
320
|
+
key={module.id}
|
|
321
|
+
isPressable={module.available}
|
|
322
|
+
onPress={() =>
|
|
323
|
+
module.available && scrollToSection(module.id)
|
|
324
|
+
}
|
|
325
|
+
className={`${
|
|
326
|
+
module.available
|
|
327
|
+
? "cursor-pointer hover:shadow-lg"
|
|
328
|
+
: "opacity-70"
|
|
329
|
+
} transition-shadow`}
|
|
330
|
+
>
|
|
331
|
+
<CardBody>
|
|
332
|
+
<div className="flex items-start justify-between mb-2">
|
|
333
|
+
<h3 className="text-lg font-semibold">
|
|
334
|
+
{module.name}
|
|
335
|
+
</h3>
|
|
336
|
+
<Chip
|
|
337
|
+
size="sm"
|
|
338
|
+
color={module.available ? "success" : "warning"}
|
|
339
|
+
variant="flat"
|
|
340
|
+
>
|
|
341
|
+
{module.available ? "Actif" : "Inactif"}
|
|
342
|
+
</Chip>
|
|
343
|
+
</div>
|
|
344
|
+
<p className="text-sm text-slate-600 dark:text-slate-400 mb-2">
|
|
345
|
+
{module.description}
|
|
346
|
+
</p>
|
|
347
|
+
{!module.available && (
|
|
348
|
+
<div className="flex justify-between items-center text-xs text-default-700 mt-2">
|
|
349
|
+
<span>Pour activer : </span>
|
|
350
|
+
<Snippet hideSymbol color="primary">
|
|
351
|
+
{`pnpm lastbrain add-module ${module.id}`}
|
|
352
|
+
</Snippet>
|
|
353
|
+
</div>
|
|
354
|
+
)}
|
|
355
|
+
</CardBody>
|
|
356
|
+
</Card>
|
|
357
|
+
))}
|
|
358
|
+
</div>
|
|
359
|
+
</CardBody>
|
|
360
|
+
</Card>
|
|
361
|
+
|
|
362
|
+
{/* Module documentation - seulement pour les modules actifs */}
|
|
363
|
+
{modules
|
|
364
|
+
.filter((m) => m.available)
|
|
365
|
+
.map((module) => (
|
|
366
|
+
<div
|
|
367
|
+
key={module.id}
|
|
368
|
+
id={`module-${module.id}`}
|
|
369
|
+
className="scroll-mt-32"
|
|
370
|
+
>
|
|
371
|
+
{module.content}
|
|
372
|
+
</div>
|
|
373
|
+
))}
|
|
374
|
+
</div>
|
|
375
|
+
)}
|
|
376
|
+
</main>
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
);
|
|
381
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Template for docs page with dynamic module loading
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { DocPage } from "./DocPage.js";
|
|
4
|
+
|
|
5
|
+
// This will be replaced by module-build script with actual imports
|
|
6
|
+
// MODULES_IMPORTS_PLACEHOLDER
|
|
7
|
+
|
|
8
|
+
interface ModuleDocConfig {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
content: React.ReactNode;
|
|
13
|
+
available: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function DocsPageWithModules() {
|
|
17
|
+
// This will be replaced by module-build script with actual module configs
|
|
18
|
+
// MODULES_CONFIG_PLACEHOLDER
|
|
19
|
+
const modules: ModuleDocConfig[] = [];
|
|
20
|
+
|
|
21
|
+
return <DocPage modules={modules} />;
|
|
22
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// GENERATED BY LASTBRAIN TEMPLATE (Migrations Guide)
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Card, Code, Divider, Chip } from "@lastbrain/ui";
|
|
4
|
+
|
|
5
|
+
export function MigrationsGuidePage() {
|
|
6
|
+
return (
|
|
7
|
+
<div className="max-w-5xl mx-auto px-6 py-10 space-y-10">
|
|
8
|
+
<header className="space-y-3">
|
|
9
|
+
<h1 className="text-3xl font-bold">Guide Migrations</h1>
|
|
10
|
+
<p className="text-slate-600 dark:text-slate-300 text-sm">
|
|
11
|
+
Gestion séparée des scripts UP et DOWN par module.
|
|
12
|
+
</p>
|
|
13
|
+
<div className="flex gap-2 flex-wrap">
|
|
14
|
+
<Chip color="primary">UP</Chip>
|
|
15
|
+
<Chip color="secondary">DOWN</Chip>
|
|
16
|
+
<Chip color="success">Reset</Chip>
|
|
17
|
+
<Chip color="warning">Push</Chip>
|
|
18
|
+
</div>
|
|
19
|
+
</header>
|
|
20
|
+
<Divider />
|
|
21
|
+
<Card className="p-6 space-y-3">
|
|
22
|
+
<h2 className="text-lg font-semibold">Emplacement</h2>
|
|
23
|
+
<Code className="text-xs block">supabase/migrations/ (UP)</Code>
|
|
24
|
+
<Code className="text-xs block">supabase/migrations-down/ (DOWN)</Code>
|
|
25
|
+
</Card>
|
|
26
|
+
<Card className="p-6 space-y-3">
|
|
27
|
+
<h2 className="text-lg font-semibold">Cycle</h2>
|
|
28
|
+
<ol className="list-decimal pl-5 text-sm space-y-1 text-slate-600 dark:text-slate-400">
|
|
29
|
+
<li>Ajouter fichier SQL (ex: 002_add_profiles.sql)</li>
|
|
30
|
+
<li>Rebuild module si nouveaux scripts copiés</li>
|
|
31
|
+
<li>
|
|
32
|
+
<Code className="text-xs inline">supabase db push</Code> pour
|
|
33
|
+
appliquer
|
|
34
|
+
</li>
|
|
35
|
+
<li>
|
|
36
|
+
<Code className="text-xs inline">supabase db reset</Code> en dev
|
|
37
|
+
complet
|
|
38
|
+
</li>
|
|
39
|
+
<li>DOWNs exécutés via remove-module (option down)</li>
|
|
40
|
+
</ol>
|
|
41
|
+
</Card>
|
|
42
|
+
<Card className="p-6 space-y-2">
|
|
43
|
+
<h2 className="text-lg font-semibold">Exemple fichier UP</h2>
|
|
44
|
+
<Code className="text-xs whitespace-pre-wrap">{`-- 002_add_profiles.sql
|
|
45
|
+
CREATE TABLE profiles (
|
|
46
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
47
|
+
email text NOT NULL UNIQUE,
|
|
48
|
+
created_at timestamptz DEFAULT now()
|
|
49
|
+
);`}</Code>
|
|
50
|
+
</Card>
|
|
51
|
+
<Card className="p-6 space-y-2">
|
|
52
|
+
<h2 className="text-lg font-semibold">Exemple fichier DOWN</h2>
|
|
53
|
+
<Code className="text-xs whitespace-pre-wrap">{`-- 002_add_profiles.down.sql
|
|
54
|
+
DROP TABLE IF EXISTS profiles CASCADE;`}</Code>
|
|
55
|
+
</Card>
|
|
56
|
+
<footer className="text-center text-xs text-slate-500 dark:text-slate-500">
|
|
57
|
+
Évitez reset en production; privilégiez push contrôlé.
|
|
58
|
+
</footer>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// GENERATED BY LASTBRAIN TEMPLATE (Module Guide)
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Card, Code, Divider, Chip } from "@lastbrain/ui";
|
|
4
|
+
|
|
5
|
+
export function ModuleGuidePage() {
|
|
6
|
+
return (
|
|
7
|
+
<div className="max-w-5xl mx-auto px-6 py-10 space-y-10">
|
|
8
|
+
<header className="space-y-3">
|
|
9
|
+
<h1 className="text-3xl font-bold">Guide Modules</h1>
|
|
10
|
+
<p className="text-slate-600 dark:text-slate-300 text-sm">
|
|
11
|
+
Créez, enregistrez et déployez des modules LastBrain réutilisables.
|
|
12
|
+
</p>
|
|
13
|
+
<div className="flex gap-2 flex-wrap">
|
|
14
|
+
<Chip color="primary">Pages</Chip>
|
|
15
|
+
<Chip color="secondary">APIs</Chip>
|
|
16
|
+
<Chip color="success">Migrations</Chip>
|
|
17
|
+
</div>
|
|
18
|
+
</header>
|
|
19
|
+
<Divider />
|
|
20
|
+
<Card className="p-6 space-y-4">
|
|
21
|
+
<h2 className="text-lg font-semibold">Structure d'un module</h2>
|
|
22
|
+
<Code className="text-xs block">packages/module-xyz/</Code>
|
|
23
|
+
<Code className="text-xs block">├─ src/</Code>
|
|
24
|
+
<Code className="text-xs block">│ ├─ index.ts</Code>
|
|
25
|
+
<Code className="text-xs block">│ ├─ xyz.build.config.ts</Code>
|
|
26
|
+
<Code className="text-xs block">│ ├─ web/public/XyzPage.tsx</Code>
|
|
27
|
+
<Code className="text-xs block">│ └─ api/public/info.ts</Code>
|
|
28
|
+
<Code className="text-xs block">
|
|
29
|
+
└─ supabase/migrations/001_xyz_init.sql
|
|
30
|
+
</Code>
|
|
31
|
+
</Card>
|
|
32
|
+
<Card className="p-6 space-y-3">
|
|
33
|
+
<h2 className="text-lg font-semibold">Config de build</h2>
|
|
34
|
+
<Code className="text-xs whitespace-pre-wrap">{`const xyzBuildConfig = {
|
|
35
|
+
moduleName: "@lastbrain/module-xyz",
|
|
36
|
+
pages: [
|
|
37
|
+
{ section: "public", path: "/xyz", componentExport: "XyzPage" }
|
|
38
|
+
],
|
|
39
|
+
apis: [
|
|
40
|
+
{ method: "GET", path: "/api/xyz/info", handlerExport: "GET", entryPoint: "api/public/info", authRequired: false }
|
|
41
|
+
],
|
|
42
|
+
migrations: { enabled: true, priority: 30, path: "supabase/migrations", files: ["001_xyz_init.sql"] }
|
|
43
|
+
};`}</Code>
|
|
44
|
+
</Card>
|
|
45
|
+
<Card className="p-6 space-y-2">
|
|
46
|
+
<h2 className="text-lg font-semibold">Cycle</h2>
|
|
47
|
+
<ol className="list-decimal pl-5 text-sm space-y-1 text-slate-600 dark:text-slate-400">
|
|
48
|
+
<li>Créer dossier module + config</li>
|
|
49
|
+
<li>
|
|
50
|
+
Exporter composants/API dans <code>index.ts</code>
|
|
51
|
+
</li>
|
|
52
|
+
<li>
|
|
53
|
+
<Code className="text-xs inline">pnpm build</Code> dans le module
|
|
54
|
+
</li>
|
|
55
|
+
<li>
|
|
56
|
+
<Code className="text-xs inline">
|
|
57
|
+
pnpm lastbrain add-module xyz
|
|
58
|
+
</Code>
|
|
59
|
+
</li>
|
|
60
|
+
<li>
|
|
61
|
+
<Code className="text-xs inline">pnpm build:modules</Code>
|
|
62
|
+
</li>
|
|
63
|
+
<li>
|
|
64
|
+
Appliquer migrations si besoin (
|
|
65
|
+
<Code className="text-xs inline">supabase db push</Code>)
|
|
66
|
+
</li>
|
|
67
|
+
</ol>
|
|
68
|
+
</Card>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|