@llmnative/react 1.19.1 → 1.19.2

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 (41) hide show
  1. package/README.md +2 -2
  2. package/dist/index.css +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +2 -2
  6. package/dist/index.mjs.map +1 -1
  7. package/llms-full.txt +1140 -0
  8. package/llms.txt +82 -0
  9. package/package.json +6 -2
  10. package/scripts/cli/setup-project.js +60 -36
  11. package/templates/_shared/layouts/Default.tsx +23 -0
  12. package/templates/_shared/sections/Footer.tsx +9 -0
  13. package/templates/_shared/sections/Header.tsx +24 -0
  14. package/templates/_shared/sections/PageHeader.tsx +10 -0
  15. package/templates/_shared/sections/Sidebar.tsx +22 -0
  16. package/templates/admin/conf/menu.ts +14 -0
  17. package/templates/admin/data/mockData.ts +21 -0
  18. package/templates/admin/pages/home/HomePage.tsx +32 -0
  19. package/templates/admin/pages/roles/RolesPage.tsx +25 -0
  20. package/templates/admin/pages/settings/SettingsPage.tsx +24 -0
  21. package/templates/admin/pages/users/UsersPage.tsx +54 -0
  22. package/templates/blank/conf/menu.ts +8 -0
  23. package/templates/blank/data/mockData.ts +6 -0
  24. package/templates/blank/pages/home/HomePage.tsx +25 -0
  25. package/templates/crm/conf/menu.ts +14 -0
  26. package/templates/crm/data/mockData.ts +24 -0
  27. package/templates/crm/pages/companies/CompaniesPage.tsx +42 -0
  28. package/templates/crm/pages/contacts/ContactsPage.tsx +48 -0
  29. package/templates/crm/pages/deals/DealsPage.tsx +61 -0
  30. package/templates/crm/pages/home/HomePage.tsx +35 -0
  31. package/templates/inventory/conf/menu.ts +12 -0
  32. package/templates/inventory/data/mockData.ts +16 -0
  33. package/templates/inventory/pages/categories/CategoriesPage.tsx +23 -0
  34. package/templates/inventory/pages/home/HomePage.tsx +35 -0
  35. package/templates/inventory/pages/products/ProductsPage.tsx +56 -0
  36. package/templates/project/conf/menu.ts +14 -0
  37. package/templates/project/data/mockData.ts +21 -0
  38. package/templates/project/pages/home/HomePage.tsx +33 -0
  39. package/templates/project/pages/projects/ProjectsPage.tsx +50 -0
  40. package/templates/project/pages/tasks/TasksPage.tsx +67 -0
  41. package/templates/project/pages/team/TeamPage.tsx +25 -0
package/llms.txt ADDED
@@ -0,0 +1,82 @@
1
+ # @llmnative/react
2
+
3
+ > Framework React AI-first per generazione deterministica di UI da configurazione dichiarativa.
4
+ > Data-driven di default, schema-driven opzionale. Ottimizzato per minimo consumo di token AI.
5
+ > Apache-2.0 — versione 1.0.0
6
+
7
+ ## Install
8
+
9
+ Requisito: **Node.js 24 LTS** (`>=24.18.0 <25`, `engines` del pacchetto).
10
+
11
+ ```bash
12
+ npm install @llmnative/react
13
+ npx @llmnative/react create # scaffold progetto Vite completo
14
+ npx @llmnative/react create --yes --provider=mock # scaffold senza backend
15
+ ```
16
+
17
+ ```tsx
18
+ import '@llmnative/react/dist/index.css' // necessario una volta sola
19
+ ```
20
+
21
+ ## Cosa fa
22
+
23
+ `Grid` genera tabelle e gallery CRUD complete (fetch real-time, modal add/edit/delete, sorting, pagination, groupBy) da una configurazione colonne. `Form` gestisce stato, validazione, oggetti annidati e lifecycle hooks verso qualsiasi provider. I provider (Firebase, Supabase, mock) si swappano in `<App>` senza toccare i componenti.
24
+
25
+ ## 5 pattern core — 90% dei casi d'uso
26
+
27
+ ```tsx
28
+ // 1 — Lista CRUD con real-time
29
+ <Grid path="/users" columns={[{ key: 'name', label: 'Nome', sortable: true }, { key: 'email', label: 'Email' }]} actions={['add','edit','delete']} pagination={{ limit: 20 }} />
30
+
31
+ // 2 — Form standalone
32
+ <Form path="/products" appearance="card" showBack>
33
+ <Input name="title" label="Titolo" required />
34
+ <Select name="status" label="Stato" options={['draft','published']} />
35
+ </Form>
36
+
37
+ // 3 — Oggetti annidati e array dinamici
38
+ <Input name="address.city" label="Città" />
39
+ <Repeat name="items" defaultLength={1}>{(i) => <Input name={`items.${i}.name`} />}</Repeat>
40
+
41
+ // 4 — Colonne con renderer custom e Badge
42
+ <Grid path="/orders" columns={[{ key: 'status', render: ({ value }) => <Badge variant="success">{value}</Badge> }, { key: 'amount', render: ({ value }) => `€ ${value.toFixed(2)}` }]} />
43
+
44
+ // 5 — Form con lifecycle hooks
45
+ <Form path="/posts" onLoad={d => ({...d, price: d.price/100})} onSave={async ({record}) => ({...record, price: record.price*100})} onComplete={async ({action}) => { if (action === 'save') navigate('/posts') }} />
46
+ ```
47
+
48
+ ## Provider matrix
49
+
50
+ | Servizio | Driver disponibili | Obbligatorio |
51
+ |---|---|---|
52
+ | data | `mock`, `dbRealtime` (Firebase RTDB), `firestoreDb` (Firestore), `supabaseDb` | sì (fallback: mock) |
53
+ | storage | `firestorage`, `supabaseStorage` | no |
54
+ | auth | `googleAuth`, `firebaseAuth`, `supabaseAuth`, `dropboxAuth` | sì (fallback: googleAuth) |
55
+ | email | `gmail` | no |
56
+ | ai | `openai`, `gemini`, `anthropic`, `deepseek`, `mistral`, `openrouter`, `opencode`, `cloudflare`, `groq` | no |
57
+ | credentials | `googleServiceAccount` | no |
58
+
59
+ ```tsx
60
+ <App providers={{ firebase: { apiKey, authDomain, databaseURL }, services: { data: 'dbRealtime', storage: 'firestorage', auth: 'googleAuth' } }} />
61
+ ```
62
+
63
+ ## Struttura componenti
64
+
65
+ ```
66
+ ui/ → primitivi stateless: Alert, Badge, Button, Card, DescriptionList, Image, Modal, Table, Gallery, Tab, Repeat, Icon, LocaleSwitcher, ThemeSwitcher, ProviderSwitcher, LayoutBuilder, ContextMenu
67
+ ui/fields/ → campi form: Input, Select, UploadImage, UploadDocument, UploadCSV, CodeEditor, ImageField, RichText, Prompt, Crop, ContextMenu
68
+ blocks/ → composizioni: Menu, Breadcrumbs, Notifications, Search, Carousel, Dropdown, Toolbar, SideNav, ListCard, AuthButton
69
+ widgets/ → smart (hanno stato e provider): Grid, Form, MarkdownReader, ImageEditor, TabDynamic
70
+ ```
71
+
72
+ Tutti gli import da `'@llmnative/react'` — mai da sottodirectory.
73
+
74
+ ## Non adatto per
75
+
76
+ - Marketing sites, e-commerce, landing page con layout custom
77
+ - UI pixel-perfect che non mappano a CRUD
78
+ - Applicazioni senza dati strutturati
79
+
80
+ ## Riferimento completo
81
+
82
+ → [llms-full.txt](llms-full.txt) — API di ogni componente, ogni provider, ogni hook, tipi TypeScript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llmnative/react",
3
- "version": "1.19.1",
3
+ "version": "1.19.2",
4
4
  "description": "LLM-native React framework for deterministic UI generation. Data-driven by default, schema-driven optional. Ultra-low token consumption — built for AI agents, not just humans.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -27,6 +27,7 @@
27
27
  "test": "vitest run",
28
28
  "test:e2e": "npx playwright test",
29
29
  "test:e2e:proxy-scaffold": "node scripts/e2e/test-proxy-scaffold.js",
30
+ "test:e2e:scaffold": "node scripts/e2e/test-scaffold-templates.js",
30
31
  "test:e2e:proxy-runtime": "vitest run tests/e2e/proxy-runtime.test.ts --environment node",
31
32
  "test:integration": "npx firebase emulators:exec --only database,firestore,storage --project demo-test \"npx vitest run --config vitest.integration.config.mts\"",
32
33
  "test:watch": "vitest",
@@ -41,7 +42,10 @@
41
42
  "dist/**/*.map",
42
43
  "bin",
43
44
  "scripts/cli",
44
- "themes"
45
+ "templates",
46
+ "themes",
47
+ "llms.txt",
48
+ "llms-full.txt"
45
49
  ],
46
50
  "repository": {
47
51
  "type": "git",
@@ -37,6 +37,8 @@ function resetProjectDirectory() {
37
37
  '.env.example',
38
38
  '.firebaserc',
39
39
  'database.rules.json',
40
+ 'firestore.rules',
41
+ 'firestore.indexes.json',
40
42
  'firebase.json',
41
43
  'storage.rules',
42
44
  'index.html',
@@ -167,17 +169,18 @@ function normalizeFirebaseProjectId(projectName) {
167
169
 
168
170
  function createPackageJson(params) {
169
171
  const packagePath = path.resolve(__dirname, '../../package.json');
170
- const version = JSON.parse(fs.readFileSync(packagePath, 'utf8')).version;
172
+ const frameworkPackage = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
173
+ const version = frameworkPackage.version;
171
174
 
172
175
  ensureFile(path.join(root, 'package.json'), JSON.stringify({
173
176
  name: normalizeFirebaseProjectId(params.projectname) || 'llmnative-app',
174
177
  version: '0.1.0',
175
- private: true,
176
- type: 'module',
177
- engines: {
178
- node: '>=24.18.0 <25',
179
- },
180
- packageManager: 'npm@11.11.0',
178
+ private: true,
179
+ type: 'module',
180
+ engines: {
181
+ node: '>=24.18.0 <25',
182
+ },
183
+ packageManager: 'npm@11.11.0',
181
184
  scripts: {
182
185
  dev: 'vite',
183
186
  build: 'vite build',
@@ -185,23 +188,24 @@ function createPackageJson(params) {
185
188
  typecheck: 'tsc --noEmit',
186
189
  },
187
190
  dependencies: {
188
- '@phosphor-icons/react': '^2.1.10',
189
- firebase: '^12.16.0',
190
- 'lucide-react': '^1.26.0',
191
- react: '^19.2.8',
192
- 'react-dom': '^19.2.8',
193
- '@llmnative/react': `^${version}`,
194
- 'react-router-dom': '^7.18.1',
195
- },
196
- devDependencies: {
197
- '@tailwindcss/postcss': '^4.3.3',
198
- '@types/react': '^19.2.17',
199
- '@types/react-dom': '^19.2.3',
200
- '@vitejs/plugin-react': '^6.0.4',
201
- autoprefixer: '^10.5.4',
202
- postcss: '^8.5.23',
203
- typescript: '6.0.3',
204
- vite: '^8.1.5',
191
+ '@phosphor-icons/react': '^2.1.10',
192
+ firebase: '^12.16.0',
193
+ 'lucide-react': '^1.26.0',
194
+ react: '^19.2.8',
195
+ 'react-dom': '^19.2.8',
196
+ '@llmnative/react': `^${version}`,
197
+ 'react-router-dom': '^7.18.1',
198
+ },
199
+ devDependencies: {
200
+ '@tailwindcss/postcss': '^4.3.3',
201
+ '@types/react': '^19.2.17',
202
+ '@types/react-dom': '^19.2.3',
203
+ '@vitejs/plugin-react': '^6.0.4',
204
+ autoprefixer: '^10.5.4',
205
+ postcss: '^8.5.23',
206
+ // Same compiler the framework is built and type-checked with.
207
+ typescript: frameworkPackage.devDependencies.typescript,
208
+ vite: '^8.1.5',
205
209
  },
206
210
  }, null, 2));
207
211
  }
@@ -293,7 +297,8 @@ function createFirebaseConfig(params) {
293
297
  const hostingSite = params.hosting === '' ? params.projectname : params.hosting;
294
298
 
295
299
  ensureFile(path.join(root, 'firebase.json'), JSON.stringify({
296
- database: { rules: 'database.rules.json' },
300
+ firestore: { rules: 'firestore.rules', indexes: 'firestore.indexes.json' },
301
+ storage: { rules: 'storage.rules' },
297
302
  ...(includeHosting && {
298
303
  hosting: {
299
304
  site: normalizeFirebaseProjectId(hostingSite),
@@ -305,11 +310,21 @@ function createFirebaseConfig(params) {
305
310
  }),
306
311
  }, null, 2));
307
312
 
308
- ensureFile(path.join(root, 'database.rules.json'), JSON.stringify({
309
- rules: {
310
- '.read': 'auth != null',
311
- '.write': 'auth != null',
312
- },
313
+ ensureFile(path.join(root, 'firestore.rules'), `
314
+ rules_version = '2';
315
+
316
+ service cloud.firestore {
317
+ match /databases/{database}/documents {
318
+ match /{document=**} {
319
+ allow read, write: if request.auth != null;
320
+ }
321
+ }
322
+ }
323
+ `);
324
+
325
+ ensureFile(path.join(root, 'firestore.indexes.json'), JSON.stringify({
326
+ indexes: [],
327
+ fieldOverrides: [],
313
328
  }, null, 2));
314
329
 
315
330
  ensureFile(path.join(root, 'storage.rules'), `
@@ -352,6 +367,10 @@ function copyTemplateFiles(params) {
352
367
  const sharedSrc = path.resolve(__dirname, '../../templates/_shared');
353
368
  const templateSrc = path.resolve(__dirname, `../../templates/${params.template}`);
354
369
 
370
+ if (!fs.existsSync(sharedSrc) || !fs.existsSync(templateSrc)) {
371
+ throw new Error(`Template "${params.template}" not found in ${path.dirname(templateSrc)}`);
372
+ }
373
+
355
374
  // Copy shared layouts and sections
356
375
  copyDir(path.join(sharedSrc, 'layouts'), path.join(root, 'src/layouts'));
357
376
  copyDir(path.join(sharedSrc, 'sections'), path.join(root, 'src/sections'));
@@ -437,7 +456,7 @@ const selectedProvider = env.VITE_PROVIDER ?? '${params.provider}';
437
456
  const selectedAIProvider = env.VITE_AI_PROVIDER ?? '${params.aiProvider ?? 'none'}';
438
457
 
439
458
  const dataDriver =
440
- selectedProvider === 'firebase' ? 'dbRealtime'
459
+ selectedProvider === 'firebase' ? 'firestoreDb'
441
460
  : selectedProvider === 'supabase' ? 'supabaseDb'
442
461
  : 'mock';
443
462
 
@@ -446,6 +465,11 @@ const storageDriver =
446
465
  : selectedProvider === 'supabase' ? 'supabaseStorage'
447
466
  : undefined;
448
467
 
468
+ const authDriver =
469
+ selectedProvider === 'firebase' ? 'firebaseAuth'
470
+ : selectedProvider === 'supabase' ? 'supabaseAuth'
471
+ : 'googleAuth';
472
+
449
473
  const aiDriver = selectedAIProvider !== 'none'
450
474
  ? selectedAIProvider
451
475
  : undefined;
@@ -503,10 +527,11 @@ export const providers: AppProvidersConfig = {
503
527
  clientId: env.VITE_DROPBOX_CLIENT_ID ?? '',
504
528
  rootPath: env.VITE_DROPBOX_ROOT_PATH ?? '',
505
529
  },
530
+ ai: aiConfig,
506
531
  services: {
507
532
  data: dataDriver,
508
533
  ...(storageDriver ? { storage: storageDriver } : {}),
509
- auth: 'googleAuth',
534
+ auth: authDriver,
510
535
  ...(aiDriver ? { ai: aiDriver } : {}),
511
536
  },
512
537
  };
@@ -518,18 +543,17 @@ import { createRoot } from 'react-dom/client';
518
543
  import { App } from '@llmnative/react';
519
544
  import './styles/globals.css';
520
545
 
521
- import { aiConfig, appConfig, providers } from './conf/app';
546
+ import { appConfig, providers } from './conf/app';
522
547
  import { menu } from './conf/menu';
523
-
524
- const env = import.meta.env;
548
+ import Default from './layouts/Default';
525
549
 
526
550
  createRoot(document.getElementById('root')!).render(
527
551
  <React.StrictMode>
528
552
  <App
529
553
  importPage={(pageSource) => import(/* @vite-ignore */ pageSource)}
530
554
  menuConfig={menu}
555
+ LayoutDefault={Default}
531
556
  providers={providers}
532
- aiConfig={aiConfig}
533
557
  iconProvider={appConfig.iconProvider}
534
558
  themeProvider={{
535
559
  theme: appConfig.theme,
@@ -0,0 +1,23 @@
1
+ import React, { useState } from 'react';
2
+ import Header from '../sections/Header';
3
+ import Sidebar from '../sections/Sidebar';
4
+ import PageHeader from '../sections/PageHeader';
5
+ import Footer from '../sections/Footer';
6
+
7
+ export default function Default({ children }: { children?: React.ReactNode }) {
8
+ const [navOpen, setNavOpen] = useState(false);
9
+
10
+ return (
11
+ <div className="flex h-screen flex-col overflow-hidden">
12
+ <Header onMenuToggle={() => setNavOpen(open => !open)} />
13
+ <div className="flex min-h-0 flex-1 overflow-hidden">
14
+ <Sidebar open={navOpen} onClose={() => setNavOpen(false)} />
15
+ <main className="min-h-0 min-w-0 flex-1 overflow-auto p-4">
16
+ <PageHeader />
17
+ {children}
18
+ </main>
19
+ </div>
20
+ <Footer />
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+
3
+ export default function Footer() {
4
+ return (
5
+ <footer className="shrink-0 border-t bg-background px-4 py-2 text-end text-sm text-muted-foreground">
6
+ Powered by [projectname]
7
+ </footer>
8
+ );
9
+ }
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { ActionButton, AuthButton, Brand } from '@llmnative/react';
3
+
4
+ type HeaderProps = {
5
+ onMenuToggle: () => void;
6
+ };
7
+
8
+ export default function Header({ onMenuToggle }: HeaderProps) {
9
+ return (
10
+ <header className="flex h-14 shrink-0 items-center gap-3 border-b bg-background px-4">
11
+ <ActionButton
12
+ icon="menu"
13
+ ariaLabel="Toggle navigation"
14
+ variant="link"
15
+ wrapperClassName="lg:hidden"
16
+ onClick={onMenuToggle}
17
+ />
18
+ <Brand url="/" label="[projectname]" />
19
+ <div className="ms-auto">
20
+ <AuthButton />
21
+ </div>
22
+ </header>
23
+ );
24
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { Breadcrumbs } from '@llmnative/react';
3
+
4
+ export default function PageHeader() {
5
+ return (
6
+ <div className="mb-4">
7
+ <Breadcrumbs />
8
+ </div>
9
+ );
10
+ }
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { Modal, SideNav } from '@llmnative/react';
3
+
4
+ type SidebarProps = {
5
+ open: boolean;
6
+ onClose: () => void;
7
+ };
8
+
9
+ export default function Sidebar({ open, onClose }: SidebarProps) {
10
+ return (
11
+ <>
12
+ <div className="hidden lg:flex">
13
+ <SideNav menuKey="main" />
14
+ </div>
15
+ {open && (
16
+ <Modal title="[projectname]" position="left" size="sm" footer={false} onClose={onClose}>
17
+ <SideNav menuKey="main" embedded />
18
+ </Modal>
19
+ )}
20
+ </>
21
+ );
22
+ }
@@ -0,0 +1,14 @@
1
+ import type { MenuConfig } from '@llmnative/react';
2
+ import HomePage from '../pages/home/HomePage';
3
+ import UsersPage from '../pages/users/UsersPage';
4
+ import RolesPage from '../pages/roles/RolesPage';
5
+ import SettingsPage from '../pages/settings/SettingsPage';
6
+
7
+ export const menu: MenuConfig = {
8
+ main: [
9
+ { path: '/', title: 'Overview', icon: 'layout-dashboard', page: HomePage, end: true },
10
+ { path: '/users', title: 'Users', icon: 'users', page: UsersPage, group: 'Management' },
11
+ { path: '/roles', title: 'Roles', icon: 'shield', page: RolesPage, group: 'Management' },
12
+ { path: '/settings', title: 'Settings', icon: 'settings', page: SettingsPage, group: 'System' },
13
+ ],
14
+ };
@@ -0,0 +1,21 @@
1
+ export const mockData = {
2
+ '/users': {
3
+ 'u-1': { name: 'Alice Martin', email: 'alice@example.com', role: 'admin', status: 'active', createdAt: 1710000000000 },
4
+ 'u-2': { name: 'Bob Chen', email: 'bob@example.com', role: 'editor', status: 'active', createdAt: 1712000000000 },
5
+ 'u-3': { name: 'Clara Rossi', email: 'clara@example.com', role: 'viewer', status: 'inactive', createdAt: 1714000000000 },
6
+ 'u-4': { name: 'David Park', email: 'david@example.com', role: 'editor', status: 'active', createdAt: 1716000000000 },
7
+ },
8
+ '/roles': {
9
+ 'r-1': { name: 'admin', label: 'Administrator', description: 'Full access to all resources' },
10
+ 'r-2': { name: 'editor', label: 'Editor', description: 'Can create and edit content' },
11
+ 'r-3': { name: 'viewer', label: 'Viewer', description: 'Read-only access' },
12
+ },
13
+ '/settings': {
14
+ 'app': {
15
+ siteName: '[projectname]',
16
+ supportEmail: 'support@example.com',
17
+ timezone: 'UTC',
18
+ maintenanceMode: false,
19
+ },
20
+ },
21
+ };
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import { Card } from '@llmnative/react';
3
+ import { mockData } from '../../data/mockData';
4
+
5
+ function StatCard({ label, value, sub }: { label: string; value: number | string; sub?: string }) {
6
+ return (
7
+ <Card title={label}>
8
+ <p className="text-2xl font-semibold">{value}</p>
9
+ {sub && <p className="text-xs text-muted-foreground">{sub}</p>}
10
+ </Card>
11
+ );
12
+ }
13
+
14
+ export default function HomePage() {
15
+ const users = Object.values(mockData['/users']);
16
+ const roles = Object.values(mockData['/roles']);
17
+
18
+ return (
19
+ <div className="mx-auto max-w-5xl space-y-8 px-2 py-4">
20
+ <div className="space-y-1">
21
+ <h1 className="text-2xl font-semibold">Admin panel</h1>
22
+ <p className="text-sm text-muted-foreground">System overview and management.</p>
23
+ </div>
24
+
25
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
26
+ <StatCard label="Total users" value={users.length} />
27
+ <StatCard label="Active users" value={users.filter(u => u.status === 'active').length} />
28
+ <StatCard label="Roles defined" value={roles.length} />
29
+ </div>
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { Grid, Input } from '@llmnative/react';
3
+
4
+ export default function RolesPage() {
5
+ return (
6
+ <Grid
7
+ path="/roles"
8
+ columns={[
9
+ { key: 'name', label: 'Key', sortable: true },
10
+ { key: 'label', label: 'Display name' },
11
+ { key: 'description', label: 'Description' },
12
+ ]}
13
+ actions={['add', 'edit', 'delete']}
14
+ view="table"
15
+ sortable
16
+ form={
17
+ <>
18
+ <Input name="name" label="Key (e.g. editor)" required />
19
+ <Input name="label" label="Display name" required />
20
+ <Input name="description" label="Description" />
21
+ </>
22
+ }
23
+ />
24
+ );
25
+ }
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { Form, Input, Select } from '@llmnative/react';
3
+
4
+ const timezoneOptions = [
5
+ { label: 'UTC', value: 'UTC' },
6
+ { label: 'Europe/Rome', value: 'Europe/Rome' },
7
+ { label: 'America/New_York', value: 'America/New_York' },
8
+ { label: 'Asia/Tokyo', value: 'Asia/Tokyo' },
9
+ ];
10
+
11
+ export default function SettingsPage() {
12
+ return (
13
+ <div className="mx-auto max-w-2xl px-2 py-4">
14
+ <Form
15
+ path="/settings/app"
16
+ appearance="card"
17
+ >
18
+ <Input name="siteName" label="Site name" required />
19
+ <Input name="supportEmail" label="Support email" type="email" />
20
+ <Select name="timezone" label="Timezone" options={timezoneOptions} />
21
+ </Form>
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import { Grid, Badge, Input, Select, type BadgeType } from '@llmnative/react';
3
+
4
+ const roleOptions = [
5
+ { label: 'Administrator', value: 'admin' },
6
+ { label: 'Editor', value: 'editor' },
7
+ { label: 'Viewer', value: 'viewer' },
8
+ ];
9
+
10
+ const statusOptions = [
11
+ { label: 'Active', value: 'active' },
12
+ { label: 'Inactive', value: 'inactive' },
13
+ ];
14
+
15
+ const roleVariant: Record<string, BadgeType> = {
16
+ admin: 'danger',
17
+ editor: 'primary',
18
+ viewer: 'secondary',
19
+ };
20
+
21
+ export default function UsersPage() {
22
+ return (
23
+ <Grid
24
+ path="/users"
25
+ columns={[
26
+ { key: 'name', label: 'Name', sortable: true },
27
+ { key: 'email', label: 'Email' },
28
+ { key: 'role', label: 'Role',
29
+ render: ({ value }) => (
30
+ <Badge variant={roleVariant[String(value)] ?? 'secondary'}>{String(value)}</Badge>
31
+ ),
32
+ },
33
+ { key: 'status', label: 'Status',
34
+ render: ({ value }) => (
35
+ <Badge variant={value === 'active' ? 'success' : 'secondary'}>{String(value)}</Badge>
36
+ ),
37
+ },
38
+ { key: 'createdAt', label: 'Created', render: 'date' },
39
+ ]}
40
+ actions={['add', 'edit', 'delete']}
41
+ view="table"
42
+ sortable
43
+ pagination={{ limit: 25 }}
44
+ form={
45
+ <>
46
+ <Input name="name" label="Full name" required />
47
+ <Input name="email" label="Email" type="email" required />
48
+ <Select name="role" label="Role" options={roleOptions} />
49
+ <Select name="status" label="Status" options={statusOptions} />
50
+ </>
51
+ }
52
+ />
53
+ );
54
+ }
@@ -0,0 +1,8 @@
1
+ import type { MenuConfig } from '@llmnative/react';
2
+ import HomePage from '../pages/home/HomePage';
3
+
4
+ export const menu: MenuConfig = {
5
+ main: [
6
+ { path: '/', title: 'Home', icon: 'home', page: HomePage, end: true },
7
+ ],
8
+ };
@@ -0,0 +1,6 @@
1
+ export const mockData = {
2
+ '/tasks': {
3
+ 'task-1': { title: 'Review scaffold structure', status: 'done' },
4
+ 'task-2': { title: 'Connect your first provider', status: 'next' },
5
+ },
6
+ };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { Grid } from '@llmnative/react';
3
+
4
+ export default function HomePage() {
5
+ return (
6
+ <div className="mx-auto max-w-5xl space-y-8 px-2 py-4">
7
+ <div className="space-y-1">
8
+ <h1 className="text-2xl font-semibold">Welcome to [projectname]</h1>
9
+ <p className="text-sm text-muted-foreground">
10
+ Scaffolded with LLM Native. Edit this page at <code>src/pages/home/HomePage.tsx</code>.
11
+ </p>
12
+ </div>
13
+
14
+ <Grid
15
+ path="/tasks"
16
+ columns={[
17
+ { key: 'title', label: 'Task' },
18
+ { key: 'status', label: 'Status' },
19
+ ]}
20
+ actions={['add', 'edit', 'delete']}
21
+ view="table"
22
+ />
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,14 @@
1
+ import type { MenuConfig } from '@llmnative/react';
2
+ import HomePage from '../pages/home/HomePage';
3
+ import ContactsPage from '../pages/contacts/ContactsPage';
4
+ import CompaniesPage from '../pages/companies/CompaniesPage';
5
+ import DealsPage from '../pages/deals/DealsPage';
6
+
7
+ export const menu: MenuConfig = {
8
+ main: [
9
+ { path: '/', title: 'Dashboard', icon: 'layout-dashboard', page: HomePage, end: true },
10
+ { path: '/contacts', title: 'Contacts', icon: 'users', page: ContactsPage, group: 'Sales' },
11
+ { path: '/companies', title: 'Companies', icon: 'building-2', page: CompaniesPage, group: 'Sales' },
12
+ { path: '/deals', title: 'Deals', icon: 'handshake', page: DealsPage, group: 'Sales' },
13
+ ],
14
+ };
@@ -0,0 +1,24 @@
1
+ export const mockData = {
2
+ '/contacts': {
3
+ 'c-1': { name: 'Alice Martin', email: 'alice@acme.com', company: 'Acme Corp', status: 'active', phone: '+1 555 0101' },
4
+ 'c-2': { name: 'Bob Chen', email: 'bob@globex.com', company: 'Globex', status: 'active', phone: '+1 555 0102' },
5
+ 'c-3': { name: 'Clara Rossi', email: 'clara@initech.it', company: 'Initech', status: 'inactive', phone: '+39 02 0103' },
6
+ 'c-4': { name: 'David Park', email: 'd.park@umbrella.co', company: 'Umbrella', status: 'active', phone: '+1 555 0104' },
7
+ 'c-5': { name: 'Eva Müller', email: 'eva@hooli.de', company: 'Hooli', status: 'lead', phone: '+49 89 0105' },
8
+ },
9
+ '/companies': {
10
+ 'co-1': { name: 'Acme Corp', industry: 'Manufacturing', website: 'acme.com', employees: 320 },
11
+ 'co-2': { name: 'Globex', industry: 'Technology', website: 'globex.com', employees: 85 },
12
+ 'co-3': { name: 'Initech', industry: 'IT Services', website: 'initech.it', employees: 50 },
13
+ 'co-4': { name: 'Umbrella', industry: 'Pharma', website: 'umbrella.co', employees: 1200 },
14
+ 'co-5': { name: 'Hooli', industry: 'Technology', website: 'hooli.de', employees: 600 },
15
+ },
16
+ '/deals': {
17
+ 'd-1': { title: 'Enterprise license', contact: 'Alice Martin', value: 12000, stage: 'qualified', closedAt: '' },
18
+ 'd-2': { title: 'SaaS subscription', contact: 'Bob Chen', value: 4800, stage: 'prospect', closedAt: '' },
19
+ 'd-3': { title: 'Consulting package', contact: 'Clara Rossi', value: 7500, stage: 'proposal', closedAt: '' },
20
+ 'd-4': { title: 'Support renewal', contact: 'David Park', value: 2400, stage: 'won', closedAt: '2025-03-15' },
21
+ 'd-5': { title: 'Migration project', contact: 'Eva Müller', value: 18000, stage: 'prospect', closedAt: '' },
22
+ 'd-6': { title: 'Hardware bundle', contact: 'Alice Martin', value: 5600, stage: 'lost', closedAt: '2025-02-28' },
23
+ },
24
+ };