@ramme-io/create-app 1.1.8 → 1.1.9

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.8",
3
+ "version": "1.1.9",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "create-ramme-app": "./index.js"
@@ -17,7 +17,7 @@
17
17
  "bundle:ai": "repomix --ignore '**/*.lock,**/dist/**,**/template/dist/**,**/template/node_modules/**' --output 'ramme-starter-context.txt'"
18
18
  },
19
19
  "dependencies": {
20
- "@ramme-io/ui": "^1.1.1",
20
+ "@ramme-io/ui": "^1.1.3",
21
21
  "ag-grid-community": "^34.1.2",
22
22
  "ag-grid-enterprise": "^34.1.2",
23
23
  "ag-grid-react": "^34.1.2",
package/template/pkg.json CHANGED
@@ -9,8 +9,9 @@
9
9
  "preview": "vite preview"
10
10
  },
11
11
  "dependencies": {
12
- "@ramme-io/ui": "^1.1.2",
12
+ "@ramme-io/ui": "^1.1.3",
13
13
  "mqtt": "^5.3.5",
14
+ "zustand": "^4.5.0",
14
15
  "ag-grid-community": "^31.3.1",
15
16
  "ag-grid-enterprise": "^31.3.1",
16
17
  "ag-grid-react": "^31.3.1",
@@ -3,12 +3,11 @@ import { getComponent } from '../core/component-registry';
3
3
  import { useSignal } from '../hooks/useSignal';
4
4
  import { getMockData } from '../data/mockData';
5
5
 
6
- // 1. THE TRANSLATOR: Maps Data Status ('fresh') to UI Status ('online')
7
6
  const mapSignalStatus = (status: string): string => {
8
7
  switch (status) {
9
- case 'fresh': return 'online'; // Active/Good
10
- case 'stale': return 'warning'; // Old data
11
- case 'disconnected': return 'offline';
8
+ case 'fresh': return 'online';
9
+ case 'stale': return 'warning';
10
+ case 'disconnected': return 'offline';
12
11
  case 'error': return 'error';
13
12
  default: return 'offline';
14
13
  }
@@ -24,35 +23,29 @@ interface DynamicBlockProps {
24
23
 
25
24
  export const DynamicBlock: React.FC<DynamicBlockProps> = ({ block }) => {
26
25
  const Component = getComponent(block.type);
27
-
28
26
  const { signalId, dataId, ...staticProps } = block.props;
29
27
 
30
- // 2. Resolve Data (Crucial Step)
28
+ // 1. Resolve Data
31
29
  const resolvedData = dataId ? getMockData(dataId) : staticProps.data;
32
30
 
33
- // 3. Signal Wiring (Hooks must run unconditionally)
34
- // We pass an empty string if undefined, the hook handles the rest.
31
+ // 2. Signal Wiring
35
32
  const signalState = useSignal(signalId || '');
36
33
 
37
- // 4. Merge Props
38
- // We explicitly type this as Record<string, any> so we can inject new props without TS errors
34
+ // 3. Merge Props
39
35
  const dynamicProps: Record<string, any> = {
40
36
  ...staticProps,
37
+ // FIX: Pass data as BOTH 'data' (Charts) and 'rowData' (Tables)
41
38
  data: resolvedData || [],
39
+ rowData: resolvedData || [],
42
40
  };
43
41
 
44
- // 5. Inject Signal Data (If valid)
42
+ // 4. Inject Signal Data
45
43
  if (signalId && signalState) {
46
- // Inject the value (formatted with unit)
47
44
  dynamicProps.value = `${signalState.value}${signalState.unit || ''}`;
48
-
49
- // Inject the translated status
50
45
  if (signalState.status) {
51
46
  dynamicProps.status = mapSignalStatus(signalState.status);
52
-
53
- // Optional: Auto-map 'variant' for components like Badge that use it
54
47
  if (signalState.status === 'error') {
55
- dynamicProps.variant = 'destructive'; // UI terminology
48
+ dynamicProps.variant = 'destructive';
56
49
  }
57
50
  }
58
51
  }
@@ -12,33 +12,42 @@ import {
12
12
  } from '@ramme-io/ui';
13
13
 
14
14
  /**
15
- * The Registry maps the "string" name of a component (from the AI Manifest)
15
+ * The Registry maps the "string" name of a component (from the JSON manifest)
16
16
  * to the actual React component implementation.
17
17
  */
18
18
  export const COMPONENT_REGISTRY: Record<string, React.FC<any>> = {
19
- // --- Data Display ---
19
+ // --- Core Components ---
20
+ DeviceCard,
20
21
  StatCard,
21
22
  BarChart,
22
23
  LineChart,
23
24
  PieChart,
24
25
  DataTable,
25
-
26
- // --- IoT & Controls ---
27
- DeviceCard,
28
-
29
- // MAPPING STRATEGY:
30
- // The Wizard generates specific UI intents ("Gauge", "Toggle").
31
- // For now, we map these to our versatile 'DeviceCard' primitive.
32
- // In the future, we will build dedicated components for each.
33
- GaugeCard: DeviceCard,
34
- SparklineCard: DeviceCard,
35
- ToggleCard: DeviceCard,
36
- SliderCard: DeviceCard,
37
-
38
- // --- Layout & Feedback ---
39
26
  Card,
40
27
  Alert,
41
28
  EmptyState,
29
+
30
+ // --- 🛡️ ROBUSTNESS ALIASES ---
31
+ // These mappings allow the manifest to use lowercase or alternate names
32
+ // without crashing the application.
33
+
34
+ // Tables
35
+ 'table': DataTable, // Fixes your specific error
36
+ 'Table': DataTable,
37
+ 'grid': DataTable,
38
+
39
+ // Charts
40
+ 'chart': BarChart, // Default generic chart to BarChart
41
+ 'line': LineChart,
42
+ 'bar': BarChart,
43
+ 'pie': PieChart,
44
+
45
+ // IoT Fallbacks
46
+ 'DataCard': DeviceCard,
47
+ 'GaugeCard': DeviceCard,
48
+ 'ToggleCard': DeviceCard,
49
+ 'SliderCard': DeviceCard,
50
+ 'SparklineCard': DeviceCard,
42
51
  };
43
52
 
44
53
  /**
@@ -46,12 +55,19 @@ export const COMPONENT_REGISTRY: Record<string, React.FC<any>> = {
46
55
  * Returns a fallback if the component name is unknown.
47
56
  */
48
57
  export const getComponent = (name: string) => {
49
- const Component = COMPONENT_REGISTRY[name];
58
+ // 1. Try direct lookup
59
+ let Component = COMPONENT_REGISTRY[name];
60
+
61
+ // 2. If not found, try PascalCase (e.g. "deviceCard" -> "DeviceCard")
62
+ if (!Component) {
63
+ const pascalName = name.charAt(0).toUpperCase() + name.slice(1);
64
+ Component = COMPONENT_REGISTRY[pascalName];
65
+ }
50
66
 
51
67
  if (!Component) {
52
68
  console.warn(`[Registry] Unknown component type: "${name}"`);
53
69
  return () => (
54
- <Alert variant="warning" title="Unknown Component">
70
+ <Alert variant="danger" title="Unknown Component">
55
71
  The system tried to render <code>{name}</code> but it was not found in the registry.
56
72
  </Alert>
57
73
  );
@@ -0,0 +1,63 @@
1
+ import React from 'react';
2
+ import { PageHeader, Alert } from '@ramme-io/ui';
3
+ import { appManifest } from '../config/app.manifest';
4
+ import { getComponent } from '../core/component-registry';
5
+ // Reuse the GhostOverlay for structure visualization
6
+ import { GhostOverlay } from '../components/dev/GhostOverlay';
7
+ // Import the component that renders a single block (you likely have this extracted from Dashboard.tsx)
8
+ import { DynamicBlock } from '../components/DynamicBlock';
9
+
10
+ interface DynamicPageProps {
11
+ pageId: string;
12
+ }
13
+
14
+ const DynamicPage: React.FC<DynamicPageProps> = ({ pageId }) => {
15
+ // 1. Look up the page definition in the manifest
16
+ const pageConfig = appManifest.pages?.find((p: { id: string; }) => p.id === pageId);
17
+
18
+ if (!pageConfig) {
19
+ return (
20
+ <div className="p-8">
21
+ <Alert variant="danger" title="404: Page Definition Not Found">
22
+ The manifest does not contain a page with ID: <code>{pageId}</code>.
23
+ </Alert>
24
+ </div>
25
+ );
26
+ }
27
+
28
+ return (
29
+ <div className="space-y-8 fade-in">
30
+ <PageHeader
31
+ title={pageConfig.title}
32
+ description={pageConfig.description}
33
+ />
34
+
35
+ {/* Render Sections */}
36
+ {pageConfig.sections.map((section: { id: React.Key | null | undefined; title: string | number | boolean | React.ReactElement<any, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | null | undefined; layout: { columns: any; }; blocks: any[]; }) => (
37
+ <div key={section.id} className="space-y-4">
38
+ {section.title && <h3 className="text-xl font-semibold">{section.title}</h3>}
39
+
40
+ <div
41
+ className="grid gap-6"
42
+ style={{
43
+ gridTemplateColumns: `repeat(${section.layout?.columns || 3}, minmax(300px, 1fr))`
44
+ }}
45
+ >
46
+ {section.blocks.map(block => (
47
+ <GhostOverlay
48
+ key={block.id}
49
+ componentId={block.id}
50
+ componentType={block.type}
51
+ signalId={block.props.signalId}
52
+ >
53
+ <DynamicBlock block={block} />
54
+ </GhostOverlay>
55
+ ))}
56
+ </div>
57
+ </div>
58
+ ))}
59
+ </div>
60
+ );
61
+ };
62
+
63
+ export default DynamicPage;