@ramme-io/create-app 1.1.3 → 1.1.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramme-io/create-app",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "create-ramme-app": "./index.js"
package/template/pkg.json CHANGED
@@ -10,6 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@ramme-io/ui": "^1.1.1",
13
+ "mqtt": "^5.3.5",
13
14
  "ag-grid-community": "^31.3.1",
14
15
  "ag-grid-enterprise": "^31.3.1",
15
16
  "ag-grid-react": "^31.3.1",
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @file dashboard.layout.ts
3
+ * Defines the schema and data for the dynamic dashboard.
4
+ */
5
+
6
+ // 1. Define the Shape of our "Brain"
7
+ export interface DashboardItem {
8
+ id: string;
9
+ component: string;
10
+ props: Record<string, any>;
11
+ signalId?: string; // <-- Mark as Optional (?)
12
+ }
13
+
14
+ export interface DashboardSection {
15
+ id: string;
16
+ title: string;
17
+ type: string;
18
+ columns: number;
19
+ items: DashboardItem[]; // <-- Enforce the type here
20
+ }
21
+
22
+ // 2. The Data (Typed)
23
+ export const dashboardLayout: DashboardSection[] = [
24
+ {
25
+ id: "section_iot",
26
+ title: "Live Device Status",
27
+ type: "grid",
28
+ columns: 3,
29
+ items: [
30
+ {
31
+ id: "dev_1",
32
+ component: "DeviceCard",
33
+ props: {
34
+ title: "Living Room AC",
35
+ description: "Zone A • Floor 1",
36
+ icon: "thermometer",
37
+ status: "online",
38
+ trend: "Cooling to 70°"
39
+ },
40
+ signalId: "living_room_ac"
41
+ },
42
+ {
43
+ id: "dev_2",
44
+ component: "DeviceCard",
45
+ props: {
46
+ title: "Air Quality",
47
+ description: "Sensor ID: #8842",
48
+ icon: "droplets",
49
+ status: "active",
50
+ trend: "Stable"
51
+ },
52
+ signalId: "living_room_hum"
53
+ },
54
+ {
55
+ id: "dev_3",
56
+ component: "DeviceCard",
57
+ props: {
58
+ title: "Main Server",
59
+ description: "192.168.1.42",
60
+ icon: "server",
61
+ status: "online",
62
+ trend: "CPU Load"
63
+ },
64
+ signalId: "server_01"
65
+ }
66
+ ]
67
+ },
68
+ {
69
+ id: "section_metrics",
70
+ title: "Business Overview",
71
+ type: "grid",
72
+ columns: 4,
73
+ items: [
74
+ {
75
+ id: "stat_1",
76
+ component: "StatCard",
77
+ props: {
78
+ title: "Total Users",
79
+ value: "1,234",
80
+ icon: "users",
81
+ changeText: "+10% from last month",
82
+ changeDirection: "positive"
83
+ }
84
+ },
85
+ {
86
+ id: "stat_2",
87
+ component: "StatCard",
88
+ props: {
89
+ title: "Sales Today",
90
+ value: "$5,678",
91
+ icon: "dollar-sign",
92
+ changeText: "+5% from yesterday",
93
+ changeDirection: "positive"
94
+ }
95
+ }
96
+ ]
97
+ }
98
+ ];
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import {
3
+ DeviceCard,
4
+ StatCard,
5
+ BarChart,
6
+ LineChart,
7
+ PieChart,
8
+ DataTable,
9
+ Card,
10
+ Alert,
11
+ EmptyState,
12
+ } from '@ramme-io/ui';
13
+
14
+ /**
15
+ * The Registry maps the "string" name of a component (from the JSON manifest)
16
+ * to the actual React component implementation.
17
+ */
18
+ export const COMPONENT_REGISTRY: Record<string, React.FC<any>> = {
19
+ // IoT Primitives
20
+ DeviceCard,
21
+
22
+ // Data Display
23
+ StatCard,
24
+ BarChart,
25
+ LineChart,
26
+ PieChart,
27
+ DataTable,
28
+
29
+ // Layout & Feedback
30
+ Card,
31
+ Alert,
32
+ EmptyState,
33
+ };
34
+
35
+ /**
36
+ * Helper to safely resolve a component.
37
+ * Returns a fallback if the component name is unknown.
38
+ */
39
+ export const getComponent = (name: string) => {
40
+ const Component = COMPONENT_REGISTRY[name];
41
+ if (!Component) {
42
+ console.warn(`[Registry] Unknown component type: "${name}"`);
43
+ return () => (
44
+ <Alert variant="warning" title="Unknown Component">
45
+ The system tried to render <code>{name}</code> but it was not found in the registry.
46
+ </Alert>
47
+ );
48
+ }
49
+ return Component;
50
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @file hooks.ts
3
+ * @generated by @ramme-io/builder
4
+ * @description
5
+ * This file is AUTO-GENERATED. Do not edit it directly.
6
+ * It defines the "State Machine" for the application, initializing
7
+ * all necessary signals based on the project manifest.
8
+ */
9
+
10
+ import { useSignal } from '../hooks/useSignal';
11
+
12
+ /**
13
+ * Initializes all global signals defined in the project.
14
+ * This hook is called once by the Dashboard (or App root).
15
+ */
16
+ export const useGeneratedSignals = () => {
17
+ // 1. Living Room AC (Thermostat)
18
+ const living_room_ac = useSignal('living_room_ac', {
19
+ initialValue: 72,
20
+ min: 68,
21
+ max: 76,
22
+ unit: '°F'
23
+ });
24
+
25
+ // 2. Air Quality Sensor
26
+ const living_room_hum = useSignal('living_room_hum', {
27
+ initialValue: 45,
28
+ min: 40,
29
+ max: 60,
30
+ unit: '%'
31
+ });
32
+
33
+ // 3. Main Server CPU Load
34
+ const server_01 = useSignal('server_01', {
35
+ initialValue: 42,
36
+ min: 10,
37
+ max: 95,
38
+ unit: '%'
39
+ });
40
+
41
+ // Return a dictionary for easy O(1) lookup by signalId
42
+ return {
43
+ living_room_ac,
44
+ living_room_hum,
45
+ server_01,
46
+ };
47
+ };
@@ -1,43 +1,15 @@
1
1
  import React from 'react';
2
- import {
3
- BarChart,
4
- LineChart,
5
- Card,
6
- PageHeader,
7
- StatCard,
8
- DataTable,
9
- useDataFetch,
10
- DeviceCard, // <-- NEW: Import DeviceCard
11
- type ColDef,
12
- type ValueFormatterParams,
13
- } from '@ramme-io/ui';
14
- import { useSignal } from '../hooks/useSignal'; // <-- NEW: Import the Engine
15
- import { mockTableData, mockChartData } from '../data/mockData';
2
+ import { PageHeader } from '@ramme-io/ui';
16
3
 
17
- const Dashboard: React.FC = () => {
18
- // 1. Existing Logic (SaaS Data)
19
- const { data: fetchedTableData } = useDataFetch(null, mockTableData);
20
-
21
- // 2. New Logic (IoT Engine)
22
- const temp = useSignal('living_room_ac', { initialValue: 72, min: 68, max: 76, unit: '°F' });
23
- const hum = useSignal('living_room_hum', { initialValue: 45, min: 40, max: 60, unit: '%' });
24
- const cpu = useSignal('server_01', { initialValue: 42, min: 10, max: 95, unit: '%' });
4
+ // --- IMPORTS ---
5
+ import { getComponent } from '../core/component-registry';
6
+ import { dashboardLayout } from '../config/dashboard.layout';
7
+ import { useGeneratedSignals } from '../generated/hooks'; // <-- The New Engine
25
8
 
26
- // 3. Table Config
27
- const columnDefs: ColDef[] = [
28
- { field: 'make', headerName: 'Make', sortable: true, filter: true },
29
- { field: 'model', headerName: 'Model', sortable: true, filter: true },
30
- {
31
- field: 'price',
32
- headerName: 'Price',
33
- valueFormatter: (p: ValueFormatterParams) => '$' + p.value.toLocaleString(),
34
- },
35
- {
36
- field: 'electric',
37
- headerName: 'Electric',
38
- cellRenderer: (params: any) => (params.value ? '⚡' : '⛽'),
39
- },
40
- ];
9
+ const Dashboard: React.FC = () => {
10
+ // 1. Initialize the "Brain"
11
+ // This single line replaces all the hardcoded hooks.
12
+ const signals = useGeneratedSignals();
41
13
 
42
14
  return (
43
15
  <div className="space-y-8">
@@ -46,106 +18,48 @@ const Dashboard: React.FC = () => {
46
18
  description="Real-time device monitoring and business analytics."
47
19
  />
48
20
 
49
- {/* --- SECTION 1: LIVE IOT STATUS (The New Stuff) --- */}
50
- <div>
51
- <h3 className="text-lg font-semibold mb-4 text-foreground">Live Device Status</h3>
52
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
53
- <DeviceCard
54
- title="Living Room AC"
55
- description="Zone A • Floor 1"
56
- icon="thermometer"
57
- status="online"
58
- value={`${temp.value}${temp.unit}`}
59
- trend="Cooling to 70°"
60
- />
61
- <DeviceCard
62
- title="Air Quality"
63
- description="Sensor ID: #8842"
64
- icon="droplets"
65
- status="active"
66
- value={`${hum.value}${hum.unit}`}
67
- trend="Stable"
68
- />
69
- <DeviceCard
70
- title="Main Server"
71
- description="192.168.1.42"
72
- icon="server"
73
- status={cpu.value > 90 ? 'error' : 'online'}
74
- value={`${cpu.value}${cpu.unit}`}
75
- trend="CPU Load"
76
- />
77
- </div>
78
- </div>
21
+ {/* --- DYNAMIC RUNTIME ENGINE --- */}
22
+ {dashboardLayout.map((section) => (
23
+ <div key={section.id}>
24
+ <h3 className="text-lg font-semibold mb-4 text-foreground">
25
+ {section.title}
26
+ </h3>
27
+
28
+ <div
29
+ className="grid gap-6"
30
+ // Use the 'columns' prop from the manifest to control density
31
+ style={{
32
+ gridTemplateColumns: `repeat(${section.columns || 3}, minmax(300px, 1fr))`
33
+ }}
34
+ >
35
+ {section.items.map((item) => {
36
+ const Component = getComponent(item.component);
37
+
38
+ // Prepare Props
39
+ let dynamicProps = { ...item.props };
79
40
 
80
- {/* --- SECTION 2: BUSINESS METRICS (The Old Stuff) --- */}
81
- <div>
82
- <h3 className="text-lg font-semibold mb-4 text-foreground">Business Overview</h3>
83
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
84
- <StatCard
85
- title="Total Users"
86
- value="1,234"
87
- icon="users"
88
- changeText="+10% from last month"
89
- changeDirection="positive"
90
- />
91
- <StatCard
92
- title="Sales Today"
93
- value="$5,678"
94
- icon="dollar-sign"
95
- changeText="+5% from yesterday"
96
- changeDirection="positive"
97
- />
98
- <StatCard
99
- title="New Orders"
100
- value="89"
101
- icon="shopping-cart"
102
- changeText="-2 since last hour"
103
- changeDirection="negative"
104
- />
105
- <StatCard
106
- title="Active Projects"
107
- value="12"
108
- icon="briefcase"
109
- footerText="3 nearing completion"
110
- />
111
- </div>
112
- </div>
41
+ // Inject Signal Data
42
+ if (item.signalId && signals[item.signalId as keyof typeof signals]) {
43
+ const sig = signals[item.signalId as keyof typeof signals];
44
+
45
+ // Standard Value Injection
46
+ dynamicProps.value = `${sig.value}${sig.unit}`;
47
+
48
+ // Auto-Status Logic (The "Smart" Layer)
49
+ // If a signal exceeds its defined max, auto-flag it as error
50
+ if (sig.value > (sig.max || 100)) {
51
+ dynamicProps.status = 'error';
52
+ dynamicProps.trend = 'CRITICAL';
53
+ }
54
+ }
113
55
 
114
- {/* --- SECTION 3: ANALYTICS CHARTS --- */}
115
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
116
- <Card className="p-6">
117
- <h3 className="text-lg font-semibold mb-4 text-foreground">Monthly Revenue</h3>
118
- <div className="h-[350px] w-full">
119
- <BarChart
120
- data={mockChartData}
121
- dataKeyX="name"
122
- barKeys={['pv', 'uv']}
123
- />
124
- </div>
125
- </Card>
126
- <Card className="p-6">
127
- <h3 className="text-lg font-semibold mb-4 text-foreground">User Growth</h3>
128
- <div className="h-[350px] w-full">
129
- <LineChart
130
- data={mockChartData}
131
- dataKeyX="name"
132
- lineKeys={['uv', 'pv']}
133
- />
56
+ return (
57
+ <Component key={item.id} {...dynamicProps} />
58
+ );
59
+ })}
134
60
  </div>
135
- </Card>
136
- </div>
137
-
138
- {/* --- SECTION 4: DATA TABLE --- */}
139
- <Card className="p-6">
140
- <h3 className="text-2xl font-semibold mb-4 text-foreground">Recent Vehicle Data</h3>
141
- {fetchedTableData && (
142
- <DataTable
143
- rowData={fetchedTableData}
144
- columnDefs={columnDefs}
145
- height="400px"
146
- />
147
- )}
148
- </Card>
61
+ </div>
62
+ ))}
149
63
  </div>
150
64
  );
151
65
  };
@@ -10,6 +10,7 @@
10
10
  export type SignalStatus = 'fresh' | 'stale' | 'disconnected' | 'error';
11
11
 
12
12
  export interface Signal<T = any> {
13
+ max: number;
13
14
  id: string; // The unique ID (e.g., "temp_01")
14
15
  value: T; // The actual data (e.g., 24.5)
15
16
  unit?: string; // Optional unit (e.g., "°C")
@@ -1,78 +0,0 @@
1
- {
2
- "name": "template",
3
- "version": "0.0.0",
4
- "lockfileVersion": 3,
5
- "requires": true,
6
- "packages": {
7
- "": {
8
- "dependencies": {
9
- "zod": "^4.1.13"
10
- }
11
- },
12
- "../../ramme-ui": {
13
- "name": "@ramme-io/ui",
14
- "version": "1.1.0",
15
- "extraneous": true,
16
- "dependencies": {
17
- "@radix-ui/react-slot": "^1.2.3",
18
- "@tippyjs/react": "^4.2.6",
19
- "ag-grid-community": "^31.3.1",
20
- "ag-grid-enterprise": "^31.3.1",
21
- "ag-grid-react": "^31.3.1",
22
- "clsx": "^2.1.1",
23
- "date-fns": "^3.6.0",
24
- "lucide-react": "^0.537.0",
25
- "react-datepicker": "^7.3.0",
26
- "react-select": "^5.8.0",
27
- "react-syntax-highlighter": "^15.6.6",
28
- "recharts": "^2.12.7",
29
- "tailwind-merge": "^3.3.1",
30
- "tippy.js": "^6.3.7",
31
- "zod": "^4.1.13"
32
- },
33
- "devDependencies": {
34
- "@storybook/addon-essentials": "8.2.2",
35
- "@storybook/addon-interactions": "8.2.2",
36
- "@storybook/addon-links": "8.2.2",
37
- "@storybook/addon-themes": "8.2.2",
38
- "@storybook/blocks": "8.2.2",
39
- "@storybook/react": "8.2.2",
40
- "@storybook/react-vite": "8.2.2",
41
- "@storybook/testing-library": "0.2.2",
42
- "@types/doctrine": "^0.0.9",
43
- "@types/node": "^24.2.1",
44
- "@types/react": "^18.3.23",
45
- "@types/react-dom": "^18.3.7",
46
- "@types/react-syntax-highlighter": "^15.5.13",
47
- "@vitejs/plugin-react": "^4.3.1",
48
- "autoprefixer": "^10.4.19",
49
- "class-variance-authority": "^0.7.1",
50
- "doctrine": "^3.0.0",
51
- "postcss": "^8.4.38",
52
- "react": "^18.2.0",
53
- "react-dom": "^18.2.0",
54
- "sass": "^1.77.8",
55
- "storybook": "8.2.2",
56
- "tailwindcss": "^3.4.4",
57
- "typescript": "^5.9.2",
58
- "vite": "^5.3.1",
59
- "vite-plugin-dts": "^3.9.1"
60
- },
61
- "peerDependencies": {
62
- "react": ">=18.2.0",
63
- "react-dom": ">=18.2.0",
64
- "react-router-dom": "^6.25.1",
65
- "tailwindcss": ">=3.0.0"
66
- }
67
- },
68
- "node_modules/zod": {
69
- "version": "4.1.13",
70
- "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz",
71
- "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
72
- "license": "MIT",
73
- "funding": {
74
- "url": "https://github.com/sponsors/colinhacks"
75
- }
76
- }
77
- }
78
- }