@ramme-io/create-app 1.2.0 → 1.2.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 (92) hide show
  1. package/package.json +1 -2
  2. package/template/package.json +41 -0
  3. package/template/pkg.json +1 -1
  4. package/template/src/App.tsx +65 -35
  5. package/template/src/components/AIChatWidget.tsx +2 -2
  6. package/template/src/components/AppHeader.tsx +2 -2
  7. package/template/src/components/AutoForm.tsx +13 -0
  8. package/template/src/{pages/styleguide → components}/NotFound.tsx +1 -1
  9. package/template/src/components/PageTitleUpdater.tsx +2 -2
  10. package/template/src/components/ProtectedRoute.tsx +18 -1
  11. package/template/src/components/ScrollToTop.tsx +19 -0
  12. package/template/src/config/app.manifest.ts +3 -1
  13. package/template/src/{core → config}/component-registry.tsx +1 -1
  14. package/template/src/config/navigation.ts +1 -1
  15. package/template/src/data/mock-charts.ts +32 -28
  16. package/template/src/{components → engine/renderers}/DynamicBlock.tsx +27 -7
  17. package/template/src/{pages → engine/renderers}/DynamicPage.tsx +23 -4
  18. package/template/src/{contexts → engine/runtime}/MqttContext.tsx +25 -11
  19. package/template/src/{contexts → engine/runtime}/SitemapContext.tsx +1 -1
  20. package/template/src/{core → engine/runtime}/data-seeder.ts +15 -5
  21. package/template/src/{hooks → engine/runtime}/useAction.ts +19 -8
  22. package/template/src/{hooks → engine/runtime}/useCrudLocalStorage.ts +27 -8
  23. package/template/src/{hooks → engine/runtime}/useDataQuery.ts +15 -1
  24. package/template/src/engine/runtime/useSignal.ts +51 -0
  25. package/template/src/engine/runtime/useSignalStore.ts +94 -0
  26. package/template/src/engine/runtime/useWorkflowEngine.ts +144 -0
  27. package/template/src/{core → engine/types}/manifest-types.ts +35 -3
  28. package/template/src/{types → engine/validation}/schema.ts +53 -2
  29. package/template/src/{pages → features/ai/pages}/AiChat.tsx +1 -1
  30. package/template/src/features/auth/AuthContext.tsx +118 -0
  31. package/template/src/features/auth/pages/AuthLayout.tsx +55 -0
  32. package/template/src/features/auth/pages/LoginPage.tsx +106 -0
  33. package/template/src/features/auth/pages/SignupPage.tsx +96 -0
  34. package/template/src/features/datagrid/SmartTable.tsx +222 -0
  35. package/template/src/features/onboarding/pages/Welcome.tsx +161 -0
  36. package/template/src/features/overview/index.ts +1 -0
  37. package/template/src/features/overview/pages/OverviewPage.tsx +127 -0
  38. package/template/src/{pages → features/playground/pages}/AccountingLedgerPage.tsx +1 -1
  39. package/template/src/{pages/prototypes → features/playground/pages}/ItemSelectorPage.tsx +1 -1
  40. package/template/src/{pages/settings → features/settings/pages}/BillingPage.tsx +1 -1
  41. package/template/src/features/settings/pages/ProfilePage.tsx +153 -0
  42. package/template/src/{pages/settings → features/settings/pages}/TeamPage.tsx +1 -1
  43. package/template/src/features/styleguide/Styleguide.tsx +75 -0
  44. package/template/src/features/users/components/UserDrawer.tsx +138 -0
  45. package/template/src/features/users/index.ts +2 -0
  46. package/template/src/features/users/pages/UsersPage.tsx +151 -0
  47. package/template/src/index.css +1 -1
  48. package/template/src/main.tsx +3 -3
  49. package/template/src/templates/dashboard/DashboardLayout.tsx +75 -106
  50. package/template/src/templates/dashboard/dashboard.sitemap.ts +34 -19
  51. package/template/src/templates/docs/DocsLayout.tsx +49 -38
  52. package/template/src/templates/docs/docs.sitemap.ts +22 -34
  53. package/template/src/templates/settings/SettingsLayout.tsx +83 -143
  54. package/template/src/templates/settings/settings.sitemap.ts +6 -6
  55. package/template/vite.config.ts +12 -9
  56. package/template/src/adaptors/.gitkeep +0 -0
  57. package/template/src/blocks/SmartTable.tsx +0 -191
  58. package/template/src/components/LocalSideNav.tsx +0 -120
  59. package/template/src/components/PageWithSideNav.tsx +0 -69
  60. package/template/src/config/dashboard.layout.ts +0 -110
  61. package/template/src/contexts/AuthContext.tsx +0 -64
  62. package/template/src/data/mockUsers.ts +0 -18
  63. package/template/src/generated/hooks.ts +0 -40
  64. package/template/src/hooks/useSignal.ts +0 -83
  65. package/template/src/hooks/useWorkflowEngine.ts +0 -6
  66. package/template/src/layouts/DataLayout.tsx +0 -37
  67. package/template/src/layouts/SideNavLayout.tsx +0 -28
  68. package/template/src/pages/Dashboard.tsx +0 -60
  69. package/template/src/pages/DataGridPage.tsx +0 -184
  70. package/template/src/pages/LoginPage.tsx +0 -58
  71. package/template/src/pages/settings/ProfilePage.tsx +0 -10
  72. package/template/src/pages/styleguide/Styleguide.tsx +0 -40
  73. package/template/src/templates/docs/pages/Introduction.tsx +0 -13
  74. package/template/src/types/signal.ts +0 -23
  75. /package/template/src/{core → engine/renderers}/route-generator.tsx +0 -0
  76. /package/template/src/{core → engine/types}/sitemap-entry.ts +0 -0
  77. /package/template/src/{pages → features}/GenericContentPage.tsx +0 -0
  78. /package/template/src/{hooks → features/assistant}/useMockChat.ts +0 -0
  79. /package/template/src/{components/dev → features/developer}/GhostOverlay.tsx +0 -0
  80. /package/template/src/{hooks → features/developer}/useDevTools.ts +0 -0
  81. /package/template/src/{pages → features}/styleguide/sections/charts/ChartsSection.tsx +0 -0
  82. /package/template/src/{pages → features}/styleguide/sections/colors/ColorsSection.tsx +0 -0
  83. /package/template/src/{pages → features}/styleguide/sections/elements/ElementsSection.tsx +0 -0
  84. /package/template/src/{pages → features}/styleguide/sections/feedback/FeedbackSection.tsx +0 -0
  85. /package/template/src/{pages → features}/styleguide/sections/forms/FormsSection.tsx +0 -0
  86. /package/template/src/{pages → features}/styleguide/sections/icons/IconsSection.tsx +0 -0
  87. /package/template/src/{pages → features}/styleguide/sections/layout/LayoutSection.tsx +0 -0
  88. /package/template/src/{pages → features}/styleguide/sections/navigation/NavigationSection.tsx +0 -0
  89. /package/template/src/{pages → features}/styleguide/sections/tables/TablesSection.tsx +0 -0
  90. /package/template/src/{pages → features}/styleguide/sections/templates/TemplatesSection.tsx +0 -0
  91. /package/template/src/{pages → features}/styleguide/sections/theming/ThemingSection.tsx +0 -0
  92. /package/template/src/{pages → features}/styleguide/sections/utilities/UtilitiesSection.tsx +0 -0
@@ -0,0 +1,138 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Drawer, Button, Input, Select, type SelectOption } from '@ramme-io/ui';
3
+ import type { User } from '../../../data/mockData';
4
+
5
+ interface UserDrawerProps {
6
+ isOpen: boolean;
7
+ onClose: () => void;
8
+ user: User | null;
9
+ onSave: (id: string, data: Partial<User>) => void;
10
+ }
11
+
12
+ const UserDrawer: React.FC<UserDrawerProps> = ({ isOpen, onClose, user, onSave }) => {
13
+ const [formData, setFormData] = useState<Partial<User>>({
14
+ name: '',
15
+ email: '',
16
+ role: 'viewer',
17
+ status: 'active',
18
+ });
19
+
20
+ useEffect(() => {
21
+ if (user) {
22
+ setFormData(user);
23
+ } else {
24
+ setFormData({
25
+ name: '',
26
+ email: '',
27
+ role: 'viewer',
28
+ status: 'active',
29
+ });
30
+ }
31
+ }, [user, isOpen]);
32
+
33
+ // Options Configurations
34
+ const roleOptions: SelectOption[] = [
35
+ { value: 'admin', label: 'Admin' },
36
+ { value: 'editor', label: 'Editor' },
37
+ { value: 'viewer', label: 'Viewer' },
38
+ ];
39
+
40
+ const statusOptions: SelectOption[] = [
41
+ { value: 'active', label: 'Active' },
42
+ { value: 'pending', label: 'Pending' },
43
+ { value: 'banned', label: 'Banned' },
44
+ ];
45
+
46
+ // ✅ HELPER: Find the full object based on the string value
47
+ // This satisfies the TypeScript requirement: value={SelectOption}
48
+ const getOption = (options: SelectOption[], value: string | undefined) => {
49
+ return options.find((opt) => opt.value === value) || null;
50
+ };
51
+
52
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
53
+ setFormData({ ...formData, [e.target.name]: e.target.value });
54
+ };
55
+
56
+ // ✅ HELPER: Handle change for both Native Events and Custom Selects
57
+ const handleSelectChange = (name: keyof User, newValue: any) => {
58
+ // If the component returns a SelectOption object (Custom Select)
59
+ if (newValue && typeof newValue === 'object' && 'value' in newValue) {
60
+ setFormData({ ...formData, [name]: newValue.value });
61
+ }
62
+ // If the component returns a standard Event (Native Select)
63
+ else if (newValue?.target) {
64
+ setFormData({ ...formData, [name]: newValue.target.value });
65
+ }
66
+ // Fallback
67
+ else {
68
+ setFormData({ ...formData, [name]: newValue });
69
+ }
70
+ };
71
+
72
+ const handleSubmit = (e: React.FormEvent) => {
73
+ e.preventDefault();
74
+ const idToSave = user?.id || '';
75
+ onSave(idToSave, formData);
76
+ };
77
+
78
+ return (
79
+ <Drawer
80
+ isOpen={isOpen}
81
+ onClose={onClose}
82
+ title={user ? 'Edit User' : 'Add New User'}
83
+ size="md"
84
+ >
85
+ <form onSubmit={handleSubmit} className="flex flex-col h-full">
86
+ <div className="flex-1 space-y-6 p-1">
87
+ <Input
88
+ label="Full Name"
89
+ name="name"
90
+ placeholder="Jane Doe"
91
+ value={formData.name || ''}
92
+ onChange={handleChange}
93
+ required
94
+ />
95
+
96
+ <Input
97
+ label="Email Address"
98
+ name="email"
99
+ type="email"
100
+ placeholder="jane@example.com"
101
+ value={formData.email || ''}
102
+ onChange={handleChange}
103
+ required
104
+ />
105
+
106
+ <Select
107
+ label="Role"
108
+ options={roleOptions}
109
+ // ✅ FIX: Pass the object, not the string
110
+ value={getOption(roleOptions, formData.role)}
111
+ // ✅ FIX: Handle the update safely
112
+ onChange={(val) => handleSelectChange('role', val)}
113
+ />
114
+
115
+ <Select
116
+ label="Status"
117
+ options={statusOptions}
118
+ // ✅ FIX: Pass the object, not the string
119
+ value={getOption(statusOptions, formData.status)}
120
+ // ✅ FIX: Handle the update safely
121
+ onChange={(val) => handleSelectChange('status', val)}
122
+ />
123
+ </div>
124
+
125
+ <div className="flex justify-end gap-3 mt-8 pt-4 border-t border-border">
126
+ <Button variant="ghost" onClick={onClose} type="button">
127
+ Cancel
128
+ </Button>
129
+ <Button variant="primary" type="submit">
130
+ {user ? 'Save Changes' : 'Create User'}
131
+ </Button>
132
+ </div>
133
+ </form>
134
+ </Drawer>
135
+ );
136
+ };
137
+
138
+ export default UserDrawer;
@@ -0,0 +1,2 @@
1
+ // ✅ Export Page for Routing
2
+ export { default as UsersPage } from './pages/UsersPage';
@@ -0,0 +1,151 @@
1
+ import React, { useState, useMemo } from 'react';
2
+ import {
3
+ PageHeader,
4
+ DataTable,
5
+ Button,
6
+ useToast,
7
+ Badge,
8
+ Input,
9
+ type ColDef,
10
+ type ICellRendererParams,
11
+ } from '@ramme-io/ui';
12
+
13
+ // 1. REMOVE: userService and old User types
14
+ // import { userService } from '../api/user.service';
15
+ // import type { User } from '../api/user.types';
16
+
17
+ // 2. ADD: The Engine Hook & Shared Data
18
+ import { useCrudLocalStorage } from '../../../engine/runtime/useCrudLocalStorage';
19
+ import { SEED_USERS, type User } from '../../../data/mockData';
20
+
21
+ import UserDrawer from '../components/UserDrawer';
22
+
23
+ const UsersPage: React.FC = () => {
24
+ const { addToast } = useToast();
25
+
26
+ // 3. REPLACE: Manual state fetching with the Reactive Engine
27
+ // This automatically loads 'ramme_db_users' and keeps it in sync.
28
+ const {
29
+ data: users,
30
+ createItem,
31
+ updateItem,
32
+ deleteItem
33
+ } = useCrudLocalStorage<User>('ramme_db_users', SEED_USERS);
34
+
35
+ const [isDrawerOpen, setIsDrawerOpen] = useState(false);
36
+ const [editingUser, setEditingUser] = useState<User | null>(null);
37
+ const [searchTerm, setSearchTerm] = useState('');
38
+
39
+ // (Removed refreshData & useEffect - the hook handles this automatically)
40
+
41
+ const handleOpenDrawer = (user: User | null = null) => {
42
+ setEditingUser(user);
43
+ setIsDrawerOpen(true);
44
+ };
45
+
46
+ const handleDelete = (user: User) => {
47
+ if (confirm(`Delete ${user.name}?`)) {
48
+ deleteItem(user.id);
49
+ addToast('User deleted', 'success');
50
+ }
51
+ };
52
+
53
+ const handleSave = (_id: string, data: Partial<User>) => {
54
+ try {
55
+ if (editingUser) {
56
+ // Update existing user
57
+ updateItem({ ...editingUser, ...data } as User);
58
+ addToast('User updated', 'success');
59
+ } else {
60
+ // Create new user (Engine handles ID generation)
61
+ createItem({
62
+ ...data,
63
+ role: data.role || 'viewer', // Ensure defaults
64
+ status: data.status || 'active',
65
+ joinedAt: new Date().toISOString()
66
+ } as User);
67
+ addToast('User created', 'success');
68
+ }
69
+ // Close drawer
70
+ setIsDrawerOpen(false);
71
+ } catch (error) {
72
+ addToast('Error saving user', 'error');
73
+ }
74
+ };
75
+
76
+ const columnDefs = useMemo<ColDef[]>(() => [
77
+ { field: 'name', headerName: 'Name', flex: 1, filter: true },
78
+ { field: 'email', headerName: 'Email', flex: 1, filter: true },
79
+ {
80
+ field: 'role',
81
+ headerName: 'Role',
82
+ width: 120,
83
+ cellRenderer: (params: any) => (
84
+ <Badge variant={params.value === 'admin' ? 'primary' : 'secondary'}>
85
+ {params.value}
86
+ </Badge>
87
+ )
88
+ },
89
+ {
90
+ field: 'status',
91
+ width: 120,
92
+ cellRenderer: (params: any) => (
93
+ <Badge variant={params.value === 'active' ? 'success' : 'danger'}>
94
+ {params.value}
95
+ </Badge>
96
+ )
97
+ },
98
+ {
99
+ headerName: 'Actions',
100
+ width: 120,
101
+ pinned: 'right',
102
+ cellRenderer: (params: ICellRendererParams) => (
103
+ <div className="flex gap-2">
104
+ <Button size="sm" variant="ghost" iconLeft="edit" onClick={() => handleOpenDrawer(params.data)} />
105
+ <Button size="sm" variant="ghost" className="text-red-500 hover:text-red-600" iconLeft="trash-2" onClick={() => handleDelete(params.data)} />
106
+ </div>
107
+ ),
108
+ },
109
+ ], [deleteItem]); // Added dependency
110
+
111
+ return (
112
+ <div className="space-y-6 h-[calc(100vh-140px)] flex flex-col">
113
+ <PageHeader
114
+ title="User Management"
115
+ description="Manage system access and permissions."
116
+ actions={
117
+ <Button variant="primary" iconLeft="plus" onClick={() => handleOpenDrawer()}>
118
+ Add User
119
+ </Button>
120
+ }
121
+ />
122
+
123
+ <div className="w-full max-w-sm">
124
+ <Input
125
+ placeholder="Search users..."
126
+ value={searchTerm}
127
+ onChange={(e) => setSearchTerm(e.target.value)}
128
+ />
129
+ </div>
130
+
131
+ <DataTable
132
+ rowData={users}
133
+ columnDefs={columnDefs}
134
+ height="100%"
135
+ quickFilterText={searchTerm}
136
+ pagination
137
+ paginationPageSize={10}
138
+ paginationPageSizeSelector={[10, 25, 50]}
139
+ />
140
+
141
+ <UserDrawer
142
+ isOpen={isDrawerOpen}
143
+ onClose={() => setIsDrawerOpen(false)}
144
+ user={editingUser}
145
+ onSave={handleSave}
146
+ />
147
+ </div>
148
+ );
149
+ };
150
+
151
+ export default UsersPage;
@@ -1,4 +1,4 @@
1
- @import '@ramme-io/ui/style.css';
1
+
2
2
 
3
3
  @tailwind base;
4
4
  @tailwind components;
@@ -3,9 +3,9 @@ import ReactDOM from 'react-dom/client';
3
3
  import { BrowserRouter } from 'react-router-dom';
4
4
  import App from './App.tsx';
5
5
  import { ThemeProvider, ToastProvider } from '@ramme-io/ui';
6
- import { AuthProvider } from './contexts/AuthContext';
7
- import { MqttProvider } from './contexts/MqttContext';
8
- import '@ramme-io/ui/style.css';
6
+ import { AuthProvider } from './features/auth/AuthContext.tsx';
7
+ import { MqttProvider } from './engine/runtime/MqttContext';
8
+ import "@ramme-io/ui/index.css";
9
9
  import './index.css';
10
10
 
11
11
  // This import activates all AG Grid Enterprise features
@@ -1,127 +1,96 @@
1
- import React, { useState } from 'react'; // <-- Added useState
2
- import { Outlet, NavLink } from 'react-router-dom';
1
+ import React, { useState, useMemo } from 'react';
2
+ import { Outlet, useNavigate, useLocation } from 'react-router-dom';
3
3
  import {
4
4
  Sidebar,
5
- SidebarProvider,
6
- SidebarHeader,
7
- SidebarContent,
8
- SidebarFooter,
9
- SidebarMenu,
10
- SidebarMenuItem,
11
- useSidebar,
12
- Button,
13
- Icon,
14
- ChatFAB, // <-- NEW: Import FAB
15
- } from '@ramme-io/ui';
5
+ type SidebarItem,
6
+ ChatFAB,
7
+ type IconName, // Type for safety
8
+ } from '@ramme-io/ui'; // ✅ Import only what exists in v1.2.0
9
+
16
10
  import { dashboardSitemap } from './dashboard.sitemap';
17
- import { SitemapProvider } from '../../contexts/SitemapContext';
11
+ import { SitemapProvider } from '../../engine/runtime/SitemapContext';
18
12
  import PageTitleUpdater from '../../components/PageTitleUpdater';
19
13
  import AppHeader from '../../components/AppHeader';
20
- import { AIChatWidget } from '../../components/AIChatWidget'; // <-- NEW: Import Widget
21
- import { useWorkflowEngine } from '../../hooks/useWorkflowEngine';
22
-
23
- // NavLink wrapper - Correct
24
- const SidebarNavLink = React.forwardRef<HTMLAnchorElement, any>(
25
- ({ end, href, ...props }, ref) => {
26
- return <NavLink ref={ref} to={href || ''} {...props} end={end} />;
27
- },
28
- );
29
- SidebarNavLink.displayName = 'SidebarNavLink';
30
-
31
- // Sidebar Content Component
32
- const AppSidebarContent: React.FC = () => {
33
- const { isOpen, toggle } = useSidebar();
34
- return (
35
- <>
36
- <SidebarHeader>
37
- <div className="flex items-center justify-end w-full h-full">
38
- <Button
39
- variant="ghost"
40
- size="icon"
41
- onClick={toggle}
42
- className="hidden md:flex mr-1"
43
- >
44
- <Icon name={isOpen ? 'panel-left-close' : 'panel-left-open'} />
45
- </Button>
46
- </div>
47
- </SidebarHeader>
48
-
49
- <SidebarContent>
50
- <SidebarMenu>
51
- {dashboardSitemap.map((item) => (
52
- <React.Fragment key={item.id}>
53
- <SidebarMenuItem
54
- as={SidebarNavLink}
55
- href={item.path ? `/dashboard/${item.path}` : '/dashboard'}
56
- end
57
- icon={item.icon ? <Icon name={item.icon} /> : undefined}
58
- tooltip={item.title}
59
- >
60
- {item.title}
61
- </SidebarMenuItem>
62
- {item.children &&
63
- isOpen &&
64
- item.children.map((child) => (
65
- <SidebarMenuItem
66
- key={child.id}
67
- as={SidebarNavLink}
68
- href={`/dashboard/${item.path}/${child.path}`}
69
- end
70
- icon={child.icon ? <Icon name={child.icon} /> : undefined}
71
- tooltip={child.title}
72
- className="pl-10"
73
- >
74
- {child.title}
75
- </SidebarMenuItem>
76
- ))}
77
- </React.Fragment>
78
- ))}
79
- </SidebarMenu>
80
- </SidebarContent>
81
-
82
- <SidebarFooter />
83
- </>
84
- );
85
- };
14
+ import { AIChatWidget } from '../../components/AIChatWidget';
15
+ import { useWorkflowEngine } from '../../engine/runtime/useWorkflowEngine';
86
16
 
87
17
  // Main Layout Component
88
18
  const DashboardLayout: React.FC = () => {
89
- // 1. STATE: Track if the chat window is open
19
+ const navigate = useNavigate();
20
+ const location = useLocation();
90
21
  const [isChatOpen, setIsChatOpen] = useState(false);
22
+
91
23
  useWorkflowEngine();
92
24
 
25
+ // 1. Transform Sitemap to Sidebar Items
26
+ // The new Sidebar expects a simple array of items.
27
+ const sidebarItems: SidebarItem[] = useMemo(() => {
28
+ return dashboardSitemap.map((route) => ({
29
+ id: route.id,
30
+ label: route.title,
31
+ icon: route.icon as IconName, // Ensure your sitemap strings match valid icon names
32
+ href: route.path ? `/dashboard/${route.path}` : '/dashboard',
33
+ // Note: v1.2.0 Sidebar currently handles top-level items.
34
+ // If you need nested items, we would flatten them here or update Sidebar.tsx later.
35
+ }));
36
+ }, []);
37
+
38
+ // 2. Determine Active Item based on URL
39
+ const activeItemId = useMemo(() => {
40
+ // Find the item whose href matches the start of the current path
41
+ const active = sidebarItems.find(item =>
42
+ item.href !== '/dashboard' && location.pathname.startsWith(item.href!)
43
+ );
44
+ // Default to dashboard (first item) if no specific match
45
+ return active?.id || sidebarItems[0]?.id;
46
+ }, [location.pathname, sidebarItems]);
47
+
93
48
  return (
94
49
  <SitemapProvider value={dashboardSitemap}>
95
50
  <PageTitleUpdater />
96
- <SidebarProvider>
97
- <div className="flex h-screen bg-background text-foreground relative">
98
- <Sidebar>
99
- <AppSidebarContent />
100
- </Sidebar>
101
- <div className="flex flex-col flex-1 overflow-hidden">
102
- <AppHeader />
103
- <main className="flex-1 overflow-y-auto p-8">
104
- <Outlet />
105
- </main>
106
- </div>
51
+
52
+ {/* 3. New Layout Structure (No Provider needed) */}
53
+ <div className="flex h-screen bg-background text-foreground relative">
54
+
55
+ <Sidebar
56
+ className="relative border-border"
57
+ items={sidebarItems}
58
+ activeItemId={activeItemId}
59
+ onNavigate={(item) => {
60
+ if (item.href) navigate(item.href);
61
+ }}
62
+ user={{
63
+ name: "Demo User",
64
+ email: "user@example.com",
65
+ avatarUrl: "https://i.pravatar.cc/150?u=ramme"
66
+ }}
67
+ logo={
68
+ <div className="flex items-center gap-2 px-2 font-bold text-xl tracking-tight">
69
+ <span className="text-primary">Ramme</span>App
70
+ </div>
71
+ }
72
+ />
107
73
 
108
- {/* --- AI COPILOT SECTION --- */}
109
-
110
- {/* 2. The Widget: Only renders when open */}
111
- {isChatOpen && (
112
- <AIChatWidget onClose={() => setIsChatOpen(false)} />
113
- )}
74
+ <div className="flex flex-col flex-1 overflow-hidden">
75
+ <AppHeader />
76
+ <main className="flex-1 overflow-y-auto p-8 bg-muted/20">
77
+ <Outlet />
78
+ </main>
79
+ </div>
114
80
 
115
- {/* 3. The Button: Fixed to bottom-right */}
116
- <div className="fixed bottom-6 right-6 z-50">
117
- <ChatFAB
118
- onClick={() => setIsChatOpen(!isChatOpen)}
119
- tooltipContent={isChatOpen ? "Close Assistant" : "Open Bodewell AI"}
120
- />
121
- </div>
81
+ {/* --- AI COPILOT SECTION --- */}
82
+ {isChatOpen && (
83
+ <AIChatWidget onClose={() => setIsChatOpen(false)} />
84
+ )}
122
85
 
86
+ <div className="fixed bottom-6 right-6 z-50">
87
+ <ChatFAB
88
+ onClick={() => setIsChatOpen(!isChatOpen)}
89
+ tooltipContent={isChatOpen ? "Close Assistant" : "Open Bodewell AI"}
90
+ />
123
91
  </div>
124
- </SidebarProvider>
92
+
93
+ </div>
125
94
  </SitemapProvider>
126
95
  );
127
96
  };
@@ -1,25 +1,40 @@
1
- import { type SitemapEntry } from '../../core/sitemap-entry';
1
+ import { type SitemapEntry } from '../../engine/types/sitemap-entry';
2
2
  import { appManifest } from '../../config/app.manifest';
3
- import Dashboard from '../../pages/Dashboard';
4
- import AiChat from '../../pages/AiChat';
5
3
 
6
- export const dashboardSitemap: SitemapEntry[] = [];
4
+ // Existing pages (Legacy location)
5
+ import Welcome from '../../features/onboarding/pages/Welcome';
6
+ import { OverviewPage } from '../../features/overview';
7
+ import AiChat from '../../features/ai/pages/AiChat';
7
8
 
8
- // A. Dynamic Pages from Manifest
9
- if (appManifest.pages) {
10
- appManifest.pages.forEach(page => {
11
- const isDashboard = page.slug === 'dashboard';
12
-
13
- dashboardSitemap.push({
14
- id: page.id,
15
- title: page.title,
16
- // Map root to Dashboard, others to their slug
17
- path: isDashboard ? '' : page.slug,
18
- icon: isDashboard ? 'layout-dashboard' : 'file-text',
19
- component: Dashboard, // Map everything to the Universal Renderer
20
- });
21
- });
22
- }
9
+ // NEW: Import from the Feature Domain (Clean API)
10
+ import { UsersPage } from '../../features/users';
11
+
12
+ export const dashboardSitemap: SitemapEntry[] = [
13
+ // 1. The New Landing Page
14
+ {
15
+ id: 'welcome',
16
+ path: 'welcome',
17
+ title: 'Start Here',
18
+ icon: 'rocket',
19
+ component: Welcome,
20
+ },
21
+ // 2. The "Mission Control" Charts Page
22
+ {
23
+ id: 'overview',
24
+ path: 'overview',
25
+ title: 'Overview',
26
+ icon: 'layout-dashboard',
27
+ component: OverviewPage,
28
+ },
29
+ // 3. ✅ The User Management Module
30
+ {
31
+ id: 'users',
32
+ path: 'users',
33
+ title: 'Users',
34
+ icon: 'users',
35
+ component: UsersPage,
36
+ }
37
+ ];
23
38
 
24
39
  // B. Dynamic Modules
25
40
  if (appManifest.modules?.includes('ai-chat')) {