@ramme-io/create-app 1.2.8 → 1.2.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/package.json +14 -9
- package/template/package-lock.json +5474 -0
- package/template/src/components/layout/StandardPageLayout.tsx +41 -0
- package/template/src/engine/renderers/DynamicPage.tsx +25 -35
- package/template/src/features/GenericContentPage.tsx +19 -14
- package/template/src/features/auth/pages/LoginPage.tsx +1 -1
- package/template/src/features/datagrid/SmartTable.tsx +117 -43
- package/template/src/features/overview/pages/OverviewPage.tsx +57 -69
- package/template/src/features/settings/pages/ProfilePage.tsx +70 -80
- package/template/src/features/styleguide/Styleguide.tsx +8 -9
- package/template/src/features/users/pages/UsersPage.tsx +41 -52
- package/template/src/templates/dashboard/DashboardLayout.tsx +8 -11
- package/template/DESIGNER_GUIDE.md +0 -43
|
@@ -3,26 +3,20 @@ import {
|
|
|
3
3
|
Button,
|
|
4
4
|
Input,
|
|
5
5
|
Card,
|
|
6
|
-
SectionHeader,
|
|
7
6
|
Icon,
|
|
8
7
|
Alert
|
|
9
8
|
} from '@ramme-io/ui';
|
|
10
9
|
import { useAuth } from '../../auth/AuthContext';
|
|
11
|
-
|
|
12
|
-
// 1. REMOVE Zombie Service
|
|
13
|
-
// import { userService } from '../../users/api/user.service';
|
|
14
|
-
|
|
15
|
-
// 2. ADD Engine Hooks & Data
|
|
16
10
|
import { useCrudLocalStorage } from '../../../engine/runtime/useCrudLocalStorage';
|
|
17
11
|
import { SEED_USERS, type User } from '../../../data/mockData';
|
|
12
|
+
// ✅ Import Core Layout
|
|
13
|
+
import { StandardPageLayout } from '../../../components/layout/StandardPageLayout';
|
|
18
14
|
|
|
19
15
|
const ProfilePage: React.FC = () => {
|
|
20
16
|
const { user } = useAuth();
|
|
21
17
|
const [isLoading, setIsLoading] = useState(false);
|
|
22
18
|
const [success, setSuccess] = useState(false);
|
|
23
19
|
|
|
24
|
-
// 3. CONNECT to Data Lake
|
|
25
|
-
// We only need 'updateItem' here to modify the existing profile
|
|
26
20
|
const { updateItem } = useCrudLocalStorage<User>('ramme_db_users', SEED_USERS);
|
|
27
21
|
|
|
28
22
|
const [formData, setFormData] = useState({
|
|
@@ -32,7 +26,6 @@ const ProfilePage: React.FC = () => {
|
|
|
32
26
|
bio: 'Product Designer based in San Francisco.'
|
|
33
27
|
});
|
|
34
28
|
|
|
35
|
-
// Load current user data into form
|
|
36
29
|
useEffect(() => {
|
|
37
30
|
if (user) {
|
|
38
31
|
setFormData({
|
|
@@ -55,14 +48,8 @@ const ProfilePage: React.FC = () => {
|
|
|
55
48
|
|
|
56
49
|
try {
|
|
57
50
|
if (user?.id) {
|
|
58
|
-
// 4. USE ENGINE to update the record in the Data Lake
|
|
59
|
-
// We merge the existing user object with the new form data
|
|
60
51
|
updateItem({ ...user, ...formData } as User);
|
|
61
|
-
|
|
62
|
-
// 5. REFRESH SESSION (Local hack to update UI header immediately)
|
|
63
|
-
// In a real app, AuthContext would listen to storage changes, but this is fine for a prototype.
|
|
64
52
|
localStorage.setItem('ramme_session', JSON.stringify({ ...user, ...formData }));
|
|
65
|
-
|
|
66
53
|
setSuccess(true);
|
|
67
54
|
setTimeout(() => setSuccess(false), 3000);
|
|
68
55
|
}
|
|
@@ -74,79 +61,82 @@ const ProfilePage: React.FC = () => {
|
|
|
74
61
|
};
|
|
75
62
|
|
|
76
63
|
return (
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
64
|
+
<StandardPageLayout
|
|
65
|
+
title="My Profile"
|
|
66
|
+
description="Manage your personal information and account settings."
|
|
67
|
+
>
|
|
68
|
+
<div className="max-w-4xl space-y-6">
|
|
69
|
+
{success && (
|
|
70
|
+
<Alert variant="info" title="Changes Saved">
|
|
71
|
+
Your profile has been updated successfully.
|
|
72
|
+
</Alert>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
76
|
+
{/* Left Column - Avatar Card */}
|
|
77
|
+
<Card className="p-6 flex flex-col items-center text-center space-y-4 h-fit">
|
|
78
|
+
<div className="relative group cursor-pointer">
|
|
79
|
+
<div className="h-24 w-24 rounded-full bg-primary/10 flex items-center justify-center text-2xl font-bold text-primary border-4 border-background shadow-sm">
|
|
80
|
+
{formData.name.charAt(0)}
|
|
81
|
+
</div>
|
|
82
|
+
<div className="absolute inset-0 bg-black/50 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity">
|
|
83
|
+
<Icon name="camera" className="text-white" />
|
|
84
|
+
</div>
|
|
92
85
|
</div>
|
|
93
|
-
<div
|
|
94
|
-
|
|
86
|
+
<div>
|
|
87
|
+
<h3 className="font-semibold text-lg">{formData.name}</h3>
|
|
88
|
+
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent bg-primary text-primary-foreground hover:bg-primary/80 uppercase">
|
|
89
|
+
{formData.role}
|
|
90
|
+
</span>
|
|
95
91
|
</div>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<span className="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent bg-primary text-primary-foreground hover:bg-primary/80 uppercase">
|
|
100
|
-
{formData.role}
|
|
101
|
-
</span>
|
|
102
|
-
</div>
|
|
103
|
-
<div className="w-full pt-4 border-t">
|
|
104
|
-
<p className="text-xs text-muted-foreground mb-4">Joined December 2024</p>
|
|
105
|
-
<Button variant="outline" className="w-full" size="sm">Change Avatar</Button>
|
|
106
|
-
</div>
|
|
107
|
-
</Card>
|
|
108
|
-
|
|
109
|
-
{/* Right Column - Form */}
|
|
110
|
-
<Card className="md:col-span-2 p-6">
|
|
111
|
-
<form onSubmit={handleSave} className="space-y-4">
|
|
112
|
-
<div className="grid gap-2">
|
|
113
|
-
<h3 className="font-semibold text-lg">Personal Information</h3>
|
|
114
|
-
<p className="text-sm text-muted-foreground">Update your personal details here.</p>
|
|
92
|
+
<div className="w-full pt-4 border-t">
|
|
93
|
+
<p className="text-xs text-muted-foreground mb-4">Joined December 2024</p>
|
|
94
|
+
<Button variant="outline" className="w-full" size="sm">Change Avatar</Button>
|
|
115
95
|
</div>
|
|
96
|
+
</Card>
|
|
97
|
+
|
|
98
|
+
{/* Right Column - Form */}
|
|
99
|
+
<Card className="md:col-span-2 p-6">
|
|
100
|
+
<form onSubmit={handleSave} className="space-y-4">
|
|
101
|
+
<div className="grid gap-2">
|
|
102
|
+
<h3 className="font-semibold text-lg">Personal Information</h3>
|
|
103
|
+
<p className="text-sm text-muted-foreground">Update your personal details here.</p>
|
|
104
|
+
</div>
|
|
116
105
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
/>
|
|
130
|
-
<div className="space-y-2">
|
|
131
|
-
<label className="text-sm font-medium leading-none">Bio</label>
|
|
132
|
-
<textarea
|
|
133
|
-
className="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
134
|
-
value={formData.bio}
|
|
135
|
-
disabled
|
|
106
|
+
<div className="grid gap-4 py-4">
|
|
107
|
+
<Input
|
|
108
|
+
label="Full Name"
|
|
109
|
+
name="name"
|
|
110
|
+
value={formData.name}
|
|
111
|
+
onChange={handleChange}
|
|
112
|
+
/>
|
|
113
|
+
<Input
|
|
114
|
+
label="Email Address"
|
|
115
|
+
name="email"
|
|
116
|
+
value={formData.email}
|
|
117
|
+
onChange={handleChange}
|
|
136
118
|
/>
|
|
137
|
-
<
|
|
119
|
+
<div className="space-y-2">
|
|
120
|
+
<label className="text-sm font-medium leading-none">Bio</label>
|
|
121
|
+
<textarea
|
|
122
|
+
className="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
123
|
+
value={formData.bio}
|
|
124
|
+
disabled
|
|
125
|
+
/>
|
|
126
|
+
<p className="text-[0.8rem] text-muted-foreground">Bio editing is disabled in this demo.</p>
|
|
127
|
+
</div>
|
|
138
128
|
</div>
|
|
139
|
-
</div>
|
|
140
129
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
130
|
+
<div className="flex justify-end gap-4">
|
|
131
|
+
<Button type="submit" disabled={isLoading}>
|
|
132
|
+
{isLoading ? 'Saving...' : 'Save Changes'}
|
|
133
|
+
</Button>
|
|
134
|
+
</div>
|
|
135
|
+
</form>
|
|
136
|
+
</Card>
|
|
137
|
+
</div>
|
|
148
138
|
</div>
|
|
149
|
-
</
|
|
139
|
+
</StandardPageLayout>
|
|
150
140
|
);
|
|
151
141
|
};
|
|
152
142
|
|
|
@@ -14,15 +14,12 @@ const Styleguide: React.FC = () => {
|
|
|
14
14
|
}, [sitemap]);
|
|
15
15
|
|
|
16
16
|
// 2. Transform to Sidebar Items
|
|
17
|
-
const navItems = useMemo<SidebarItem[]>(() => {
|
|
17
|
+
const navItems = useMemo<SidebarItem[]>(() => {
|
|
18
18
|
if (!styleguideSection?.children) return [];
|
|
19
19
|
|
|
20
|
-
// ✅ FIX: Remove the explicit ": { ... }" type. Let TS infer 'child' automatically.
|
|
21
20
|
return styleguideSection.children.map((child) => ({
|
|
22
21
|
id: child.id,
|
|
23
22
|
label: child.title,
|
|
24
|
-
// We cast to IconName because we know the string is a valid icon,
|
|
25
|
-
// and we provide a fallback 'hash' if it's undefined.
|
|
26
23
|
icon: (child.icon as IconName) || 'hash',
|
|
27
24
|
href: child.path ? `/docs/styleguide/${child.path}` : '/docs/styleguide',
|
|
28
25
|
}));
|
|
@@ -44,8 +41,10 @@ const navItems = useMemo<SidebarItem[]>(() => {
|
|
|
44
41
|
return (
|
|
45
42
|
<div className="flex w-full min-h-[calc(100vh-7rem)]">
|
|
46
43
|
{/* SIDEBAR */}
|
|
47
|
-
<div className="sticky top-[7rem] h-[calc(100vh-7rem)] z-20">
|
|
44
|
+
<div className="sticky top-[7rem] h-[calc(100vh-7rem)] z-20 hidden md:block">
|
|
48
45
|
<Sidebar
|
|
46
|
+
// ✅ FIX 1: Removed 'w-64'. Let the Sidebar component manage its own width.
|
|
47
|
+
// ✅ FIX 2: Added 'h-full' so it fills the sticky container perfectly.
|
|
49
48
|
className="h-full border-r border-border bg-card/50 backdrop-blur-sm"
|
|
50
49
|
items={navItems}
|
|
51
50
|
activeItemId={activeItemId}
|
|
@@ -62,10 +61,10 @@ const navItems = useMemo<SidebarItem[]>(() => {
|
|
|
62
61
|
</div>
|
|
63
62
|
|
|
64
63
|
{/* MAIN CONTENT */}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<div className="
|
|
68
|
-
|
|
64
|
+
{/* ✅ FIX 3: Re-added padding (p-6) so content doesn't hit the edges. */}
|
|
65
|
+
<main className="flex-1 min-w-0 bg-background overflow-x-hidden p-6 md:p-10">
|
|
66
|
+
<div className="max-w-7xl mx-auto">
|
|
67
|
+
<Outlet />
|
|
69
68
|
</div>
|
|
70
69
|
</main>
|
|
71
70
|
</div>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useState, useMemo } from 'react';
|
|
2
2
|
import {
|
|
3
|
-
PageHeader,
|
|
4
3
|
DataTable,
|
|
5
4
|
Button,
|
|
6
5
|
useToast,
|
|
@@ -9,22 +8,15 @@ import {
|
|
|
9
8
|
type ColDef,
|
|
10
9
|
type ICellRendererParams,
|
|
11
10
|
} 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
11
|
import { useCrudLocalStorage } from '../../../engine/runtime/useCrudLocalStorage';
|
|
19
12
|
import { SEED_USERS, type User } from '../../../data/mockData';
|
|
20
|
-
|
|
21
13
|
import UserDrawer from '../components/UserDrawer';
|
|
14
|
+
// ✅ Import Core Layout
|
|
15
|
+
import { StandardPageLayout } from '../../../components/layout/StandardPageLayout';
|
|
22
16
|
|
|
23
17
|
const UsersPage: React.FC = () => {
|
|
24
18
|
const { addToast } = useToast();
|
|
25
19
|
|
|
26
|
-
// 3. REPLACE: Manual state fetching with the Reactive Engine
|
|
27
|
-
// This automatically loads 'ramme_db_users' and keeps it in sync.
|
|
28
20
|
const {
|
|
29
21
|
data: users,
|
|
30
22
|
createItem,
|
|
@@ -36,8 +28,6 @@ const UsersPage: React.FC = () => {
|
|
|
36
28
|
const [editingUser, setEditingUser] = useState<User | null>(null);
|
|
37
29
|
const [searchTerm, setSearchTerm] = useState('');
|
|
38
30
|
|
|
39
|
-
// (Removed refreshData & useEffect - the hook handles this automatically)
|
|
40
|
-
|
|
41
31
|
const handleOpenDrawer = (user: User | null = null) => {
|
|
42
32
|
setEditingUser(user);
|
|
43
33
|
setIsDrawerOpen(true);
|
|
@@ -53,20 +43,17 @@ const UsersPage: React.FC = () => {
|
|
|
53
43
|
const handleSave = (_id: string, data: Partial<User>) => {
|
|
54
44
|
try {
|
|
55
45
|
if (editingUser) {
|
|
56
|
-
// Update existing user
|
|
57
46
|
updateItem({ ...editingUser, ...data } as User);
|
|
58
47
|
addToast('User updated', 'success');
|
|
59
48
|
} else {
|
|
60
|
-
// Create new user (Engine handles ID generation)
|
|
61
49
|
createItem({
|
|
62
50
|
...data,
|
|
63
|
-
role: data.role || 'viewer',
|
|
51
|
+
role: data.role || 'viewer',
|
|
64
52
|
status: data.status || 'active',
|
|
65
53
|
joinedAt: new Date().toISOString()
|
|
66
54
|
} as User);
|
|
67
55
|
addToast('User created', 'success');
|
|
68
56
|
}
|
|
69
|
-
// Close drawer
|
|
70
57
|
setIsDrawerOpen(false);
|
|
71
58
|
} catch (error) {
|
|
72
59
|
addToast('Error saving user', 'error');
|
|
@@ -106,45 +93,47 @@ const UsersPage: React.FC = () => {
|
|
|
106
93
|
</div>
|
|
107
94
|
),
|
|
108
95
|
},
|
|
109
|
-
], [deleteItem]);
|
|
96
|
+
], [deleteItem]);
|
|
110
97
|
|
|
111
98
|
return (
|
|
112
|
-
<
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
99
|
+
<StandardPageLayout
|
|
100
|
+
title="User Management"
|
|
101
|
+
description="Manage system access and permissions."
|
|
102
|
+
actions={
|
|
103
|
+
<Button variant="primary" iconLeft="plus" onClick={() => handleOpenDrawer()}>
|
|
104
|
+
Add User
|
|
105
|
+
</Button>
|
|
106
|
+
}
|
|
107
|
+
>
|
|
108
|
+
<div className="space-y-6 h-full flex flex-col">
|
|
109
|
+
<div className="w-full max-w-sm">
|
|
110
|
+
<Input
|
|
111
|
+
placeholder="Search users..."
|
|
112
|
+
value={searchTerm}
|
|
113
|
+
onChange={(e) => setSearchTerm(e.target.value)}
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div className="flex-1 min-h-[500px]">
|
|
118
|
+
<DataTable
|
|
119
|
+
rowData={users}
|
|
120
|
+
columnDefs={columnDefs}
|
|
121
|
+
height="100%"
|
|
122
|
+
quickFilterText={searchTerm}
|
|
123
|
+
pagination
|
|
124
|
+
paginationPageSize={10}
|
|
125
|
+
paginationPageSizeSelector={[10, 25, 50]}
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
140
128
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
129
|
+
<UserDrawer
|
|
130
|
+
isOpen={isDrawerOpen}
|
|
131
|
+
onClose={() => setIsDrawerOpen(false)}
|
|
132
|
+
user={editingUser}
|
|
133
|
+
onSave={handleSave}
|
|
134
|
+
/>
|
|
135
|
+
</div>
|
|
136
|
+
</StandardPageLayout>
|
|
148
137
|
);
|
|
149
138
|
};
|
|
150
139
|
|
|
@@ -13,8 +13,6 @@ import PageTitleUpdater from '../../components/PageTitleUpdater';
|
|
|
13
13
|
import AppHeader from '../../components/AppHeader';
|
|
14
14
|
import { AIChatWidget } from '../../components/AIChatWidget';
|
|
15
15
|
import { useWorkflowEngine } from '../../engine/runtime/useWorkflowEngine';
|
|
16
|
-
|
|
17
|
-
// ✅ 1. IMPORT THE HOOK
|
|
18
16
|
import { useDynamicSitemap } from '../../engine/runtime/useDynamicSitemap';
|
|
19
17
|
|
|
20
18
|
const DashboardLayout: React.FC = () => {
|
|
@@ -24,22 +22,19 @@ const DashboardLayout: React.FC = () => {
|
|
|
24
22
|
|
|
25
23
|
useWorkflowEngine();
|
|
26
24
|
|
|
27
|
-
//
|
|
25
|
+
// 2. GET LIVE DATA (Merges Static + Builder Data)
|
|
28
26
|
const liveSitemap = useDynamicSitemap(dashboardSitemap);
|
|
29
27
|
|
|
30
|
-
//
|
|
28
|
+
// 3. BUILD SIDEBAR FROM LIVE DATA
|
|
31
29
|
const sidebarItems: SidebarItem[] = useMemo(() => {
|
|
32
|
-
// We map over 'liveSitemap' instead of 'dashboardSitemap'
|
|
33
30
|
return liveSitemap.map((route) => ({
|
|
34
31
|
id: route.id,
|
|
35
32
|
label: route.title,
|
|
36
33
|
icon: route.icon as IconName,
|
|
37
|
-
// Handle the path logic safely
|
|
38
34
|
href: route.path.startsWith('/') ? route.path : `/dashboard/${route.path}`,
|
|
39
35
|
}));
|
|
40
|
-
}, [liveSitemap]);
|
|
36
|
+
}, [liveSitemap]);
|
|
41
37
|
|
|
42
|
-
// 4. Determine Active Item
|
|
43
38
|
const activeItemId = useMemo(() => {
|
|
44
39
|
const active = sidebarItems.find(item =>
|
|
45
40
|
item.href !== '/dashboard' && location.pathname.startsWith(item.href!)
|
|
@@ -48,7 +43,6 @@ const DashboardLayout: React.FC = () => {
|
|
|
48
43
|
}, [location.pathname, sidebarItems]);
|
|
49
44
|
|
|
50
45
|
return (
|
|
51
|
-
// ✅ 5. PASS LIVE SITEMAP TO CONTEXT (For Breadcrumbs/Titles)
|
|
52
46
|
<SitemapProvider value={liveSitemap}>
|
|
53
47
|
<PageTitleUpdater />
|
|
54
48
|
|
|
@@ -56,7 +50,7 @@ const DashboardLayout: React.FC = () => {
|
|
|
56
50
|
|
|
57
51
|
<Sidebar
|
|
58
52
|
className="relative border-border"
|
|
59
|
-
items={sidebarItems}
|
|
53
|
+
items={sidebarItems}
|
|
60
54
|
activeItemId={activeItemId}
|
|
61
55
|
onNavigate={(item) => {
|
|
62
56
|
if (item.href) navigate(item.href);
|
|
@@ -75,7 +69,10 @@ const DashboardLayout: React.FC = () => {
|
|
|
75
69
|
|
|
76
70
|
<div className="flex flex-col flex-1 overflow-hidden">
|
|
77
71
|
<AppHeader />
|
|
78
|
-
|
|
72
|
+
{/* ✅ FIX: Removed 'p-8'. Changed to 'p-0' (implied) or just removed class.
|
|
73
|
+
We keep 'bg-muted/20' for the app background.
|
|
74
|
+
The StandardPageLayout inside Outlet now controls the margins. */}
|
|
75
|
+
<main className="flex-1 overflow-y-auto bg-muted/20">
|
|
79
76
|
<Outlet />
|
|
80
77
|
</main>
|
|
81
78
|
</div>
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
## 🚀 Quick Start for Designers
|
|
2
|
-
|
|
3
|
-
You don't need to be a coding wizard to use this. Just follow these 3 steps:
|
|
4
|
-
|
|
5
|
-
### 1. Setup (Do this once)
|
|
6
|
-
You need **Node.js** installed on your computer.
|
|
7
|
-
- Open your terminal (Command Prompt or Terminal app).
|
|
8
|
-
- Type `node -v` to see if you have it. If not, download it from [nodejs.org](https://nodejs.org).
|
|
9
|
-
|
|
10
|
-
### 2. Create your Project
|
|
11
|
-
Run this magic command in your terminal:
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm create @ramme-io/app@latest
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
It will ask you for a project name (e.g., my-smart-home). Type it and hit Enter.
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Run it!
|
|
22
|
-
|
|
23
|
-
You will be prompted to name your project.
|
|
24
|
-
|
|
25
|
-
**1. Enter the project directory**
|
|
26
|
-
```bash
|
|
27
|
-
cd my-new-project
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
**2. Install dependencies**
|
|
31
|
-
```bash
|
|
32
|
-
pnpm install
|
|
33
|
-
# or npm install
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
**3. Start the dev server**
|
|
37
|
-
```bash
|
|
38
|
-
pnpm run dev
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Your prototype is now running locally at `http://localhost:5173`.
|
|
42
|
-
|
|
43
|
-
---
|