@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.
- package/package.json +1 -2
- package/template/package.json +41 -0
- package/template/pkg.json +1 -1
- package/template/src/App.tsx +65 -35
- package/template/src/components/AIChatWidget.tsx +2 -2
- package/template/src/components/AppHeader.tsx +2 -2
- package/template/src/components/AutoForm.tsx +13 -0
- package/template/src/{pages/styleguide → components}/NotFound.tsx +1 -1
- package/template/src/components/PageTitleUpdater.tsx +2 -2
- package/template/src/components/ProtectedRoute.tsx +18 -1
- package/template/src/components/ScrollToTop.tsx +19 -0
- package/template/src/config/app.manifest.ts +3 -1
- package/template/src/{core → config}/component-registry.tsx +1 -1
- package/template/src/config/navigation.ts +1 -1
- package/template/src/data/mock-charts.ts +32 -28
- package/template/src/{components → engine/renderers}/DynamicBlock.tsx +27 -7
- package/template/src/{pages → engine/renderers}/DynamicPage.tsx +23 -4
- package/template/src/{contexts → engine/runtime}/MqttContext.tsx +25 -11
- package/template/src/{contexts → engine/runtime}/SitemapContext.tsx +1 -1
- package/template/src/{core → engine/runtime}/data-seeder.ts +15 -5
- package/template/src/{hooks → engine/runtime}/useAction.ts +19 -8
- package/template/src/{hooks → engine/runtime}/useCrudLocalStorage.ts +27 -8
- package/template/src/{hooks → engine/runtime}/useDataQuery.ts +15 -1
- package/template/src/engine/runtime/useSignal.ts +51 -0
- package/template/src/engine/runtime/useSignalStore.ts +94 -0
- package/template/src/engine/runtime/useWorkflowEngine.ts +144 -0
- package/template/src/{core → engine/types}/manifest-types.ts +35 -3
- package/template/src/{types → engine/validation}/schema.ts +53 -2
- package/template/src/{pages → features/ai/pages}/AiChat.tsx +1 -1
- package/template/src/features/auth/AuthContext.tsx +118 -0
- package/template/src/features/auth/pages/AuthLayout.tsx +55 -0
- package/template/src/features/auth/pages/LoginPage.tsx +106 -0
- package/template/src/features/auth/pages/SignupPage.tsx +96 -0
- package/template/src/features/datagrid/SmartTable.tsx +222 -0
- package/template/src/features/onboarding/pages/Welcome.tsx +161 -0
- package/template/src/features/overview/index.ts +1 -0
- package/template/src/features/overview/pages/OverviewPage.tsx +127 -0
- package/template/src/{pages → features/playground/pages}/AccountingLedgerPage.tsx +1 -1
- package/template/src/{pages/prototypes → features/playground/pages}/ItemSelectorPage.tsx +1 -1
- package/template/src/{pages/settings → features/settings/pages}/BillingPage.tsx +1 -1
- package/template/src/features/settings/pages/ProfilePage.tsx +153 -0
- package/template/src/{pages/settings → features/settings/pages}/TeamPage.tsx +1 -1
- package/template/src/features/styleguide/Styleguide.tsx +75 -0
- package/template/src/features/users/components/UserDrawer.tsx +138 -0
- package/template/src/features/users/index.ts +2 -0
- package/template/src/features/users/pages/UsersPage.tsx +151 -0
- package/template/src/index.css +1 -1
- package/template/src/main.tsx +3 -3
- package/template/src/templates/dashboard/DashboardLayout.tsx +75 -106
- package/template/src/templates/dashboard/dashboard.sitemap.ts +34 -19
- package/template/src/templates/docs/DocsLayout.tsx +49 -38
- package/template/src/templates/docs/docs.sitemap.ts +22 -34
- package/template/src/templates/settings/SettingsLayout.tsx +83 -143
- package/template/src/templates/settings/settings.sitemap.ts +6 -6
- package/template/vite.config.ts +12 -9
- package/template/src/adaptors/.gitkeep +0 -0
- package/template/src/blocks/SmartTable.tsx +0 -191
- package/template/src/components/LocalSideNav.tsx +0 -120
- package/template/src/components/PageWithSideNav.tsx +0 -69
- package/template/src/config/dashboard.layout.ts +0 -110
- package/template/src/contexts/AuthContext.tsx +0 -64
- package/template/src/data/mockUsers.ts +0 -18
- package/template/src/generated/hooks.ts +0 -40
- package/template/src/hooks/useSignal.ts +0 -83
- package/template/src/hooks/useWorkflowEngine.ts +0 -6
- package/template/src/layouts/DataLayout.tsx +0 -37
- package/template/src/layouts/SideNavLayout.tsx +0 -28
- package/template/src/pages/Dashboard.tsx +0 -60
- package/template/src/pages/DataGridPage.tsx +0 -184
- package/template/src/pages/LoginPage.tsx +0 -58
- package/template/src/pages/settings/ProfilePage.tsx +0 -10
- package/template/src/pages/styleguide/Styleguide.tsx +0 -40
- package/template/src/templates/docs/pages/Introduction.tsx +0 -13
- package/template/src/types/signal.ts +0 -23
- /package/template/src/{core → engine/renderers}/route-generator.tsx +0 -0
- /package/template/src/{core → engine/types}/sitemap-entry.ts +0 -0
- /package/template/src/{pages → features}/GenericContentPage.tsx +0 -0
- /package/template/src/{hooks → features/assistant}/useMockChat.ts +0 -0
- /package/template/src/{components/dev → features/developer}/GhostOverlay.tsx +0 -0
- /package/template/src/{hooks → features/developer}/useDevTools.ts +0 -0
- /package/template/src/{pages → features}/styleguide/sections/charts/ChartsSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/colors/ColorsSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/elements/ElementsSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/feedback/FeedbackSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/forms/FormsSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/icons/IconsSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/layout/LayoutSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/navigation/NavigationSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/tables/TablesSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/templates/TemplatesSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/theming/ThemingSection.tsx +0 -0
- /package/template/src/{pages → features}/styleguide/sections/utilities/UtilitiesSection.tsx +0 -0
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Outlet, useLocation } from 'react-router-dom';
|
|
3
|
-
import { PageWithSideNav } from '../components/PageWithSideNav';
|
|
4
|
-
import { useSitemap } from '../contexts/SitemapContext'; // Import the custom hook
|
|
5
|
-
|
|
6
|
-
const DataLayout: React.FC = () => {
|
|
7
|
-
const location = useLocation();
|
|
8
|
-
const sitemap = useSitemap(); // Consume the sitemap from context
|
|
9
|
-
const isDocsTemplate = location.pathname.startsWith('/docs');
|
|
10
|
-
|
|
11
|
-
if (isDocsTemplate) {
|
|
12
|
-
const dataSitemapSection = sitemap.find(item => item.id === 'data');
|
|
13
|
-
|
|
14
|
-
const navItems = (dataSitemapSection?.children || []).map(child => ({
|
|
15
|
-
label: child.title,
|
|
16
|
-
href: child.path,
|
|
17
|
-
icon: child.icon,
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<PageWithSideNav
|
|
22
|
-
sideNavHeader={
|
|
23
|
-
<h2 className="text-lg font-semibold tracking-tight mb-2">
|
|
24
|
-
{dataSitemapSection?.title || 'Data'}
|
|
25
|
-
</h2>
|
|
26
|
-
}
|
|
27
|
-
navItems={navItems}
|
|
28
|
-
>
|
|
29
|
-
<Outlet />
|
|
30
|
-
</PageWithSideNav>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return <Outlet />;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export default DataLayout;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Outlet } from 'react-router-dom';
|
|
3
|
-
import { PageWithSideNav } from '../components/PageWithSideNav';
|
|
4
|
-
import type { NavItem } from '../components/LocalSideNav';
|
|
5
|
-
|
|
6
|
-
interface SideNavLayoutProps {
|
|
7
|
-
navItems: NavItem[];
|
|
8
|
-
sideNavHeader?: React.ReactNode;
|
|
9
|
-
contentWidth?: 'fixed' | 'full';
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const SideNavLayout: React.FC<SideNavLayoutProps> = ({
|
|
13
|
-
navItems,
|
|
14
|
-
sideNavHeader,
|
|
15
|
-
contentWidth = 'fixed',
|
|
16
|
-
}) => {
|
|
17
|
-
return (
|
|
18
|
-
<PageWithSideNav
|
|
19
|
-
navItems={navItems}
|
|
20
|
-
sideNavHeader={sideNavHeader}
|
|
21
|
-
contentWidth={contentWidth}
|
|
22
|
-
>
|
|
23
|
-
<Outlet />
|
|
24
|
-
</PageWithSideNav>
|
|
25
|
-
);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export default SideNavLayout;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { useLocation } from 'react-router-dom';
|
|
3
|
-
import { PageHeader, Alert } from '@ramme-io/ui';
|
|
4
|
-
import { appManifest } from '../config/app.manifest';
|
|
5
|
-
import { DynamicBlock } from '../components/DynamicBlock';
|
|
6
|
-
|
|
7
|
-
const Dashboard: React.FC = () => {
|
|
8
|
-
const location = useLocation();
|
|
9
|
-
|
|
10
|
-
// 1. Determine current slug from URL
|
|
11
|
-
const pathParts = location.pathname.split('/').filter(Boolean);
|
|
12
|
-
// If path is root or /dashboard, slug is 'dashboard', else take the last part
|
|
13
|
-
const currentSlug = pathParts.length > 1 ? pathParts[pathParts.length - 1] : 'dashboard';
|
|
14
|
-
|
|
15
|
-
// 2. Find the matching Page Definition
|
|
16
|
-
const pageDef = appManifest.pages?.find(p => p.slug === currentSlug)
|
|
17
|
-
|| appManifest.pages?.[0];
|
|
18
|
-
|
|
19
|
-
if (!pageDef) {
|
|
20
|
-
return (
|
|
21
|
-
<div className="p-8">
|
|
22
|
-
<Alert variant="warning" title="Page Not Found">
|
|
23
|
-
Could not find a definition for slug: <code>{currentSlug}</code>
|
|
24
|
-
</Alert>
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<div className="space-y-8 relative">
|
|
31
|
-
<PageHeader
|
|
32
|
-
title={pageDef.title}
|
|
33
|
-
description={pageDef.description}
|
|
34
|
-
/>
|
|
35
|
-
|
|
36
|
-
{pageDef.sections.map((section) => (
|
|
37
|
-
<div key={section.id} className="relative">
|
|
38
|
-
{section.title && (
|
|
39
|
-
<h3 className="text-lg font-semibold mb-4 text-foreground">
|
|
40
|
-
{section.title}
|
|
41
|
-
</h3>
|
|
42
|
-
)}
|
|
43
|
-
|
|
44
|
-
<div
|
|
45
|
-
className="grid gap-6"
|
|
46
|
-
style={{
|
|
47
|
-
gridTemplateColumns: `repeat(${section.layout?.columns || 3}, minmax(300px, 1fr))`
|
|
48
|
-
}}
|
|
49
|
-
>
|
|
50
|
-
{section.blocks.map((block) => (
|
|
51
|
-
<DynamicBlock key={block.id} block={block} />
|
|
52
|
-
))}
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
))}
|
|
56
|
-
</div>
|
|
57
|
-
);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export default Dashboard;
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import React, { useState, useMemo } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
PageHeader, // <-- Add PageHeader back
|
|
4
|
-
DataTable,
|
|
5
|
-
Button,
|
|
6
|
-
Icon,
|
|
7
|
-
Drawer,
|
|
8
|
-
FormTemplate,
|
|
9
|
-
useToast,
|
|
10
|
-
Badge,
|
|
11
|
-
type ColDef,
|
|
12
|
-
type FormField,
|
|
13
|
-
type ICellRendererParams,
|
|
14
|
-
} from '@ramme-io/ui';
|
|
15
|
-
import { useCrudLocalStorage } from '../hooks/useCrudLocalStorage';
|
|
16
|
-
import { mockUsers, type User } from '../data/mockUsers';
|
|
17
|
-
|
|
18
|
-
// --- Custom Cell Renderers (No changes needed here) ---
|
|
19
|
-
const ActionsRenderer: React.FC<ICellRendererParams & { onEdit: (data: User) => void; onDelete: (data: User) => void; }> = ({ data, onEdit, onDelete }) => (
|
|
20
|
-
<div className="flex items-center justify-center gap-2 h-full">
|
|
21
|
-
<Button size="sm" variant="outline" onClick={() => onEdit(data)} aria-label="Edit">
|
|
22
|
-
<Icon name="edit" className="h-4 w-4" />
|
|
23
|
-
</Button>
|
|
24
|
-
<Button size="sm" variant="danger" onClick={() => onDelete(data)} aria-label="Delete">
|
|
25
|
-
<Icon name="trash-2" className="h-4 w-4" />
|
|
26
|
-
</Button>
|
|
27
|
-
</div>
|
|
28
|
-
);
|
|
29
|
-
interface StatusRendererProps extends ICellRendererParams {
|
|
30
|
-
value: User['status'];
|
|
31
|
-
}
|
|
32
|
-
const StatusRenderer: React.FC<StatusRendererProps> = ({ value }) => {
|
|
33
|
-
const variantMap: Record<User['status'], 'success' | 'warning' | 'danger'> = {
|
|
34
|
-
Active: 'success',
|
|
35
|
-
Pending: 'warning',
|
|
36
|
-
Banned: 'danger',
|
|
37
|
-
};
|
|
38
|
-
const variant = variantMap[value] || 'secondary';
|
|
39
|
-
return <Badge variant={variant}>{value}</Badge>;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const DataGridPage: React.FC = () => {
|
|
44
|
-
const { addToast } = useToast();
|
|
45
|
-
const { data: users, createItem, updateItem, deleteItem } = useCrudLocalStorage<User>('users', mockUsers);
|
|
46
|
-
|
|
47
|
-
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
|
48
|
-
const [editingUser, setEditingUser] = useState<User | null>(null);
|
|
49
|
-
|
|
50
|
-
const handleOpenDrawer = (user: User | null = null) => {
|
|
51
|
-
setEditingUser(user);
|
|
52
|
-
setIsDrawerOpen(true);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// ... (handleDelete and handleFormSubmit functions remain the same)
|
|
56
|
-
const handleDelete = (user: User) => {
|
|
57
|
-
if (window.confirm(`Are you sure you want to delete ${user.name}?`)) {
|
|
58
|
-
deleteItem(user.id);
|
|
59
|
-
addToast('User deleted successfully', 'success');
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const handleFormSubmit = (formData: Record<string, any>) => {
|
|
64
|
-
if (editingUser) {
|
|
65
|
-
updateItem({ ...editingUser, ...formData });
|
|
66
|
-
addToast('User updated successfully', 'success');
|
|
67
|
-
} else {
|
|
68
|
-
const newUser: Omit<User, 'id' | 'createdAt'> = {
|
|
69
|
-
...formData,
|
|
70
|
-
} as Omit<User, 'id' | 'createdAt'>;
|
|
71
|
-
createItem({ ...newUser, createdAt: new Date().toISOString() });
|
|
72
|
-
addToast('User created successfully', 'success');
|
|
73
|
-
}
|
|
74
|
-
setIsDrawerOpen(false);
|
|
75
|
-
setEditingUser(null);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// ... (columnDefs and formFields definitions remain the same)
|
|
79
|
-
const columnDefs = useMemo<ColDef[]>(() => [
|
|
80
|
-
{ field: 'id', headerName: 'ID', width: 80, sortable: true },
|
|
81
|
-
{ field: 'name', headerName: 'Name', flex: 2, sortable: true, filter: 'agTextColumnFilter' },
|
|
82
|
-
{ field: 'email', headerName: 'Email', flex: 3, sortable: true, filter: 'agTextColumnFilter' },
|
|
83
|
-
{ field: 'role', headerName: 'Role', flex: 1, filter: 'agTextColumnFilter' },
|
|
84
|
-
{
|
|
85
|
-
field: 'status',
|
|
86
|
-
headerName: 'Status',
|
|
87
|
-
flex: 1,
|
|
88
|
-
cellRenderer: StatusRenderer,
|
|
89
|
-
filter: 'agTextColumnFilter'
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
field: 'createdAt',
|
|
93
|
-
headerName: 'Created At',
|
|
94
|
-
flex: 2,
|
|
95
|
-
sortable: true,
|
|
96
|
-
filter: 'agDateColumnFilter',
|
|
97
|
-
filterParams: {
|
|
98
|
-
comparator: (filterLocalDateAtMidnight: Date, cellValue: string) => {
|
|
99
|
-
if (cellValue == null) return -1;
|
|
100
|
-
const cellDate = new Date(cellValue);
|
|
101
|
-
cellDate.setHours(0,0,0,0);
|
|
102
|
-
if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) return 0;
|
|
103
|
-
return cellDate < filterLocalDateAtMidnight ? -1 : 1;
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
valueFormatter: params => new Date(params.value).toLocaleDateString(),
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
headerName: 'Actions',
|
|
110
|
-
width: 120,
|
|
111
|
-
pinned: 'right',
|
|
112
|
-
cellRenderer: (props: ICellRendererParams) => (
|
|
113
|
-
<ActionsRenderer {...props} onEdit={handleOpenDrawer} onDelete={handleDelete} />
|
|
114
|
-
),
|
|
115
|
-
},
|
|
116
|
-
], [handleDelete]);
|
|
117
|
-
|
|
118
|
-
const formFields: FormField[] = useMemo(() => [
|
|
119
|
-
{ name: 'name', label: 'Full Name', type: 'text', placeholder: 'Jane Doe', required: true, value: editingUser?.name },
|
|
120
|
-
{ name: 'email', label: 'Email Address', type: 'email', placeholder: 'jane.doe@example.com', required: true, value: editingUser?.email },
|
|
121
|
-
{ name: 'username', label: 'Username', type: 'text', placeholder: 'jane.doe', required: true, value: editingUser?.username },
|
|
122
|
-
{
|
|
123
|
-
name: 'role',
|
|
124
|
-
label: 'Role',
|
|
125
|
-
type: 'select',
|
|
126
|
-
options: [{value: 'Admin', label: 'Admin'}, {value: 'Editor', label: 'Editor'}, {value: 'Viewer', label: 'Viewer'}],
|
|
127
|
-
value: editingUser?.role
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
name: 'status',
|
|
131
|
-
label: 'Status',
|
|
132
|
-
type: 'select',
|
|
133
|
-
options: [{value: 'Active', label: 'Active'}, {value: 'Pending', label: 'Pending'}, {value: 'Banned', label: 'Banned'}],
|
|
134
|
-
value: editingUser?.status
|
|
135
|
-
},
|
|
136
|
-
], [editingUser]);
|
|
137
|
-
|
|
138
|
-
return (
|
|
139
|
-
<div className="space-y-6">
|
|
140
|
-
<PageHeader
|
|
141
|
-
title="User Management"
|
|
142
|
-
description="A complete CRUD example with persisted data and an edit-in-drawer pattern."
|
|
143
|
-
actions={
|
|
144
|
-
<Button
|
|
145
|
-
variant="primary"
|
|
146
|
-
// FIX: Changed from iconBefore to iconLeft and passed the icon name string
|
|
147
|
-
iconLeft="plus"
|
|
148
|
-
onClick={() => handleOpenDrawer()}
|
|
149
|
-
>
|
|
150
|
-
Create User
|
|
151
|
-
</Button>
|
|
152
|
-
}
|
|
153
|
-
/>
|
|
154
|
-
|
|
155
|
-
<DataTable
|
|
156
|
-
rowData={users}
|
|
157
|
-
columnDefs={columnDefs}
|
|
158
|
-
height="calc(100vh - 280px)"
|
|
159
|
-
enableQuickSearch
|
|
160
|
-
rowSelection="multiple"
|
|
161
|
-
pagination
|
|
162
|
-
paginationPageSize={10}
|
|
163
|
-
// FIX: Add the page size selector to resolve the AG Grid warning
|
|
164
|
-
paginationPageSizeSelector={[10, 25, 50]}
|
|
165
|
-
/>
|
|
166
|
-
|
|
167
|
-
<Drawer isOpen={isDrawerOpen} onClose={() => setIsDrawerOpen(false)} title={editingUser ? 'Edit User' : 'Create New User'} size="lg">
|
|
168
|
-
<div className="p-6">
|
|
169
|
-
<FormTemplate
|
|
170
|
-
fields={formFields}
|
|
171
|
-
onSubmit={handleFormSubmit}
|
|
172
|
-
>
|
|
173
|
-
<div className="flex justify-end gap-2 mt-8">
|
|
174
|
-
<Button variant="outline" onClick={() => setIsDrawerOpen(false)}>Cancel</Button>
|
|
175
|
-
<Button type="submit">{editingUser ? 'Update User' : 'Create User'}</Button>
|
|
176
|
-
</div>
|
|
177
|
-
</FormTemplate>
|
|
178
|
-
</div>
|
|
179
|
-
</Drawer>
|
|
180
|
-
</div>
|
|
181
|
-
);
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
export default DataGridPage;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { useNavigate } from 'react-router-dom';
|
|
3
|
-
import { useAuth } from '../contexts/AuthContext';
|
|
4
|
-
import { Button, Card, FormTemplate, Alert, Icon, type FormField } from '@ramme-io/ui';
|
|
5
|
-
|
|
6
|
-
const LoginPage: React.FC = () => {
|
|
7
|
-
const navigate = useNavigate();
|
|
8
|
-
const { login } = useAuth();
|
|
9
|
-
const [error, setError] = useState<string | null>(null);
|
|
10
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
11
|
-
|
|
12
|
-
const handleLogin = async (formData: Record<string, any>) => {
|
|
13
|
-
setIsLoading(true);
|
|
14
|
-
setError(null);
|
|
15
|
-
try {
|
|
16
|
-
const user = await login(formData.username, formData.password);
|
|
17
|
-
if (user) {
|
|
18
|
-
navigate('/dashboard');
|
|
19
|
-
} else {
|
|
20
|
-
setError('Invalid username or password.');
|
|
21
|
-
}
|
|
22
|
-
} catch (err) {
|
|
23
|
-
setError('An unexpected error occurred.');
|
|
24
|
-
} finally {
|
|
25
|
-
setIsLoading(false);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const formFields: FormField[] = [
|
|
30
|
-
{ name: 'username', label: 'Username', type: 'text', placeholder: 'e.g., jane' },
|
|
31
|
-
{ name: 'password', label: 'Password', type: 'password', placeholder: 'e.g., password' },
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
|
36
|
-
<Card className="w-full max-w-md p-8">
|
|
37
|
-
<div className="text-center mb-8">
|
|
38
|
-
<Icon name="layout-template" size={48} className="text-primary mx-auto mb-4" />
|
|
39
|
-
<h1 className="text-3xl font-bold text-text">Welcome to Ramme</h1>
|
|
40
|
-
<p className="text-muted-foreground">Please sign in to continue</p>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
{error && <Alert variant="danger" title="Login Failed" className="mb-4">{error}</Alert>}
|
|
44
|
-
|
|
45
|
-
<FormTemplate
|
|
46
|
-
fields={formFields}
|
|
47
|
-
onSubmit={handleLogin}
|
|
48
|
-
>
|
|
49
|
-
<Button type="submit" loading={isLoading} className="w-full">
|
|
50
|
-
Sign In
|
|
51
|
-
</Button>
|
|
52
|
-
</FormTemplate>
|
|
53
|
-
</Card>
|
|
54
|
-
</div>
|
|
55
|
-
);
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export default LoginPage;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
// --- 1. Import the new generic page ---
|
|
3
|
-
import GenericContentPage from '../GenericContentPage';
|
|
4
|
-
|
|
5
|
-
const ProfilePage: React.FC = () => {
|
|
6
|
-
// --- 2. Use the generic page component ---
|
|
7
|
-
return <GenericContentPage pageTitle="Profile Settings" />;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default ProfilePage;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Outlet, useLocation } from 'react-router-dom';
|
|
3
|
-
import { PageWithSideNav } from '../../components/PageWithSideNav';
|
|
4
|
-
import { useSitemap } from '../../contexts/SitemapContext';
|
|
5
|
-
|
|
6
|
-
const Styleguide: React.FC = () => {
|
|
7
|
-
const location = useLocation();
|
|
8
|
-
const sitemap = useSitemap();
|
|
9
|
-
const isDocsTemplate = location.pathname.startsWith('/docs');
|
|
10
|
-
|
|
11
|
-
if (isDocsTemplate) {
|
|
12
|
-
// Find the 'styleguide' section from the sitemap to get its children
|
|
13
|
-
const styleguideSitemapSection = sitemap.find(item => item.id === 'styleguide');
|
|
14
|
-
|
|
15
|
-
// Transform the sitemap children into the shape the nav component expects
|
|
16
|
-
const navItems = (styleguideSitemapSection?.children || []).map(child => ({
|
|
17
|
-
label: child.title,
|
|
18
|
-
href: child.path,
|
|
19
|
-
icon: child.icon,
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<PageWithSideNav
|
|
24
|
-
sideNavHeader={
|
|
25
|
-
<h2 className="text-lg font-semibold tracking-tight mb-2">
|
|
26
|
-
{styleguideSitemapSection?.title || 'Style Guide'}
|
|
27
|
-
</h2>
|
|
28
|
-
}
|
|
29
|
-
navItems={navItems}
|
|
30
|
-
>
|
|
31
|
-
<Outlet />
|
|
32
|
-
</PageWithSideNav>
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// In the dashboard template, the main layout handles navigation.
|
|
37
|
-
return <Outlet />;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export default Styleguide;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Card } from '@ramme-io/ui';
|
|
3
|
-
|
|
4
|
-
const IntroductionPage: React.FC = () => {
|
|
5
|
-
return (
|
|
6
|
-
<Card className="p-4">
|
|
7
|
-
<h2 className="text-xl font-bold mb-2">Introduction</h2>
|
|
8
|
-
<p>This is the introduction page for the 'Docs' template.</p>
|
|
9
|
-
</Card>
|
|
10
|
-
);
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export default IntroductionPage;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// src/types/signal.ts
|
|
2
|
-
/**
|
|
3
|
-
* @file signal.d.ts
|
|
4
|
-
* @description Defines the universal contract for "Live Data" in the Ramme ecosystem.
|
|
5
|
-
*
|
|
6
|
-
* A "Signal" is a single data point that changes over time.
|
|
7
|
-
* unlike a "Record" (which is static database row), a Signal is ephemeral.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export type SignalStatus = 'fresh' | 'stale' | 'disconnected' | 'error';
|
|
11
|
-
|
|
12
|
-
export interface Signal<T = any> {
|
|
13
|
-
max: number;
|
|
14
|
-
id: string; // The unique ID (e.g., "temp_01")
|
|
15
|
-
value: T; // The actual data (e.g., 24.5)
|
|
16
|
-
unit?: string; // Optional unit (e.g., "°C")
|
|
17
|
-
timestamp: number; // Unix timestamp of last update
|
|
18
|
-
status: SignalStatus;
|
|
19
|
-
meta?: Record<string, any>; // Extra metadata (e.g., limits)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// A simple map for looking up signals by ID
|
|
23
|
-
export type SignalMap = Record<string, Signal>;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/template/src/{pages → features}/styleguide/sections/navigation/NavigationSection.tsx
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|