@ramme-io/create-app 1.1.9 → 1.2.1

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.
@@ -1,49 +1,11 @@
1
- /**
2
- * @file GhostOverlay.tsx
3
- * @repository ramme-app-starter
4
- * @description
5
- * A development-mode wrapper that provides "X-Ray vision" into the
6
- * application's structure.
7
- *
8
- * INTENT & PHILOSOPHY:
9
- * This component embodies the "Glass Box" doctrine of the Ramme Framework.
10
- * It allows creators to peer behind the "Visual Fidelity" of the prototype
11
- * and inspect the "Functional Fidelity" underneath.
12
- *
13
- * STRATEGIC VALUE:
14
- * 1. Traceability: It visually links a UI element (e.g., a Gauge) back to
15
- * its source definition in the manifest (e.g., Signal ID: 'temp_01').
16
- * 2. Debugging: It helps creators understand layout boundaries (Grid Cells)
17
- * and data flow without needing browser DevTools.
18
- * 3. Education: It reinforces the "Architect" mindset by exposing the
19
- * component hierarchy.
20
- *
21
- * USAGE:
22
- * Wrap any dynamic component in the `Dashboard.tsx` loop with this overlay.
23
- * It listens to a global `debugMode` state (likely from a store or context)
24
- * to toggle its visibility.
25
- */
26
-
27
1
  import React from 'react';
28
- import { Badge, Icon } from '@ramme-io/ui';
29
- // We'll need a way to check if we are in "Debug/Ghost" mode.
30
- // For now, we can pass it as a prop or assume a context hook exists.
31
- // import { useDevTools } from '../../contexts/DevToolsContext';
2
+ import { Icon } from '@ramme-io/ui';
32
3
 
33
4
  interface GhostOverlayProps {
34
- /** The actual UI component being wrapped (e.g., DeviceCard) */
35
5
  children: React.ReactNode;
36
-
37
- /** The unique ID of the component from the manifest */
38
6
  componentId: string;
39
-
40
- /** The name of the React component being rendered (e.g., "StatCard") */
41
7
  componentType: string;
42
-
43
- /** (Optional) The ID of the data signal driving this component */
44
8
  signalId?: string;
45
-
46
- /** Whether the overlay is currently active (X-Ray Mode on) */
47
9
  isActive?: boolean;
48
10
  }
49
11
 
@@ -52,48 +14,53 @@ export const GhostOverlay: React.FC<GhostOverlayProps> = ({
52
14
  componentId,
53
15
  componentType,
54
16
  signalId,
55
- isActive = false, // Default to off
17
+ isActive = false,
56
18
  }) => {
57
19
 
58
20
  if (!isActive) {
59
21
  return <>{children}</>;
60
22
  }
61
23
 
24
+ const handleClick = (e: React.MouseEvent) => {
25
+ // 🛑 Stop the click from triggering app logic (like navigation or toggles)
26
+ e.preventDefault();
27
+ e.stopPropagation();
28
+
29
+ // 🚀 THE GHOST BRIDGE: Signal the Parent (The Builder)
30
+ window.parent.postMessage({
31
+ type: 'RAMME_SELECT_BLOCK',
32
+ payload: { blockId: componentId }
33
+ }, '*');
34
+ };
35
+
62
36
  return (
63
- <div className="relative group">
64
- {/* The "Ghost" Border
65
- - dashed: indicates scaffolding/structure
66
- - accent color: differentiates dev tools from UI
67
- */}
68
- <div className="absolute inset-0 z-50 pointer-events-none border-2 border-dashed border-accent/50 rounded-lg bg-accent/5 group-hover:bg-accent/10 transition-colors" />
37
+ <div
38
+ className="relative group cursor-pointer"
39
+ onClick={handleClick} // Add Click Handler
40
+ >
41
+ {/* The "Ghost" Border */}
42
+ <div className="absolute inset-0 z-50 border-2 border-dashed border-accent/50 rounded-lg bg-accent/5 group-hover:bg-accent/10 transition-colors pointer-events-none" />
69
43
 
70
- {/* The "Info Tag"
71
- - Floats top-left
72
- - Displays the technical 'bones' of the component
73
- */}
74
- <div className="absolute top-0 left-0 z-50 p-2 transform -translate-y-1/2 translate-x-2">
44
+ {/* The "Info Tag" */}
45
+ <div className="absolute top-0 left-0 z-50 p-2 transform -translate-y-1/2 translate-x-2 pointer-events-none">
75
46
  <div className="flex items-center gap-2 bg-accent text-accent-foreground text-xs font-mono py-1 px-2 rounded shadow-sm">
76
47
  <Icon name="box" size={12} />
77
48
  <span className="font-bold">{componentType}</span>
78
- <span className="opacity-75">#{componentId}</span>
79
49
  </div>
80
50
  </div>
81
51
 
82
- {/* The "Signal Wire"
83
- - Floats bottom-right if a signal is connected
84
- - Shows the data source wiring
85
- */}
52
+ {/* The "Signal Wire" */}
86
53
  {signalId && (
87
- <div className="absolute bottom-0 right-0 z-50 p-2 transform translate-y-1/2 -translate-x-2">
54
+ <div className="absolute bottom-0 right-0 z-50 p-2 transform translate-y-1/2 -translate-x-2 pointer-events-none">
88
55
  <div className="flex items-center gap-1.5 bg-blue-600 text-white text-xs font-mono py-1 px-2 rounded shadow-sm">
89
56
  <Icon name="activity" size={12} />
90
- <span>Signal: {signalId}</span>
57
+ <span>{signalId}</span>
91
58
  </div>
92
59
  </div>
93
60
  )}
94
61
 
95
62
  {/* Render the actual component underneath */}
96
- <div className="opacity-50 grayscale transition-all duration-200 group-hover:opacity-75 group-hover:grayscale-0">
63
+ <div className="opacity-50 grayscale transition-all duration-200 group-hover:opacity-75 group-hover:grayscale-0 pointer-events-none">
97
64
  {children}
98
65
  </div>
99
66
  </div>
@@ -2,55 +2,55 @@ import type { AppSpecification } from '../types/schema';
2
2
 
3
3
  export const appManifest: AppSpecification = {
4
4
  meta: {
5
- name: "Acme Corp Admin",
6
- version: "2.0.0",
7
- description: "Executive overview of KPI metrics.",
8
- author: "Ramme Builder"
5
+ name: "Ramme System Check",
6
+ version: "1.0.0",
7
+ description: "Verifying the Smart Runtime.",
8
+ author: "Ramme Builder",
9
9
  },
10
10
  config: {
11
- theme: 'corporate', // <--- Switches the visual theme automatically
12
- mockMode: false // <--- Critical: Tells the Adapter Factory to use REAL code (HTTP/MQTT)
11
+ theme: 'system',
12
+ mockMode: false,
13
+ brokerUrl: 'wss://test.mosquitto.org:8081',
13
14
  },
14
- domain: {
15
- signals: [
16
- {
17
- id: "mrr_stripe",
18
- label: "Monthly Revenue",
19
- kind: "metric",
20
- source: "http", // <--- This triggers your new generateHttpImplementation()
21
- endpoint: "/api_mock.json",
22
- unit: "USD",
23
- defaultValue: 0,
24
- refreshRate: 0
25
- },
26
- {
27
- id: "active_users",
28
- label: "Active Users",
29
- kind: "metric",
30
- source: "http",
31
- endpoint: "/api_mock.json",
32
- unit: "",
33
- defaultValue: 0,
34
- refreshRate: 0
35
- }
36
- ],
37
- entities: [
38
- {
39
- id: "widget_mrr",
40
- name: "Current MRR",
41
- type: "kpi",
42
- signals: ["mrr_stripe"],
43
- ui: { dashboardComponent: "StatCard", icon: "dollar-sign" },
44
- category: ''
45
- },
46
- {
47
- id: "widget_users",
48
- name: "Total Users",
49
- type: "kpi",
50
- signals: ["active_users"],
51
- ui: { dashboardComponent: "StatCard", icon: "users" },
52
- category: ''
53
- }
54
- ]
55
- }
15
+ domain: { signals: [], entities: [] },
16
+ pages: [
17
+ {
18
+ id: "dashboard",
19
+ slug: "dashboard",
20
+ title: "System Status",
21
+ description: "Verifying Week 1 Logic Engine",
22
+ sections: [
23
+ {
24
+ id: "sect-users",
25
+ title: "User Management (CRUD Test)",
26
+ layout: { columns: 1 },
27
+ blocks: [
28
+ {
29
+ id: "table_users",
30
+ type: "SmartTable",
31
+ props: {
32
+ title: "Active Users",
33
+ dataId: "users" // ✅ Points to SEED_USERS
34
+ }
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ id: "sect-invoices",
40
+ title: "Invoices (Relational Test)",
41
+ layout: { columns: 1 },
42
+ blocks: [
43
+ {
44
+ id: "table_invoices",
45
+ type: "SmartTable",
46
+ props: {
47
+ title: "Recent Invoices",
48
+ dataId: "invoices" // ✅ Points to SEED_INVOICES
49
+ }
50
+ }
51
+ ]
52
+ }
53
+ ]
54
+ }
55
+ ]
56
56
  };
@@ -5,65 +5,45 @@ import {
5
5
  BarChart,
6
6
  LineChart,
7
7
  PieChart,
8
- DataTable,
8
+ DataTable,
9
9
  Card,
10
10
  Alert,
11
11
  EmptyState,
12
+ ToggleSwitch
12
13
  } from '@ramme-io/ui';
13
14
 
14
- /**
15
- * The Registry maps the "string" name of a component (from the JSON manifest)
16
- * to the actual React component implementation.
17
- */
15
+ // ✅ IMPORT YOUR CUSTOM COMPONENT
16
+ import { SmartTable } from '../blocks/SmartTable';
17
+
18
18
  export const COMPONENT_REGISTRY: Record<string, React.FC<any>> = {
19
- // --- Core Components ---
19
+ // IoT Primitives
20
20
  DeviceCard,
21
+
22
+ // Data Display
21
23
  StatCard,
22
24
  BarChart,
23
25
  LineChart,
24
26
  PieChart,
25
- DataTable,
27
+
28
+ // Tables
29
+ DataTable, // The raw grid
30
+
31
+ // ✅ FIX: Map "SmartTable" to the actual SmartTable component
32
+ // (Previously it was aliased to DataTable, which broke the UI)
33
+ SmartTable: SmartTable,
34
+
35
+ // Layout & Feedback
26
36
  Card,
27
37
  Alert,
28
38
  EmptyState,
29
-
30
- // --- 🛡️ ROBUSTNESS ALIASES ---
31
- // These mappings allow the manifest to use lowercase or alternate names
32
- // without crashing the application.
33
39
 
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,
40
+ // Forms/Controls
41
+ ToggleSwitch
51
42
  };
52
43
 
53
- /**
54
- * Helper to safely resolve a component.
55
- * Returns a fallback if the component name is unknown.
56
- */
57
44
  export const getComponent = (name: string) => {
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
- }
66
-
45
+ const Component = COMPONENT_REGISTRY[name];
46
+
67
47
  if (!Component) {
68
48
  console.warn(`[Registry] Unknown component type: "${name}"`);
69
49
  return () => (
@@ -0,0 +1,35 @@
1
+ // ✅ Match the export from your new mockData.ts
2
+ import { DATA_REGISTRY } from '../data/mockData';
3
+
4
+ const DB_PREFIX = 'ramme_db_';
5
+
6
+ export const initializeDataLake = () => {
7
+ if (typeof window === 'undefined') return;
8
+
9
+ console.groupCollapsed('🌊 [Data Lake] Initialization');
10
+
11
+ Object.entries(DATA_REGISTRY).forEach(([key, seedData]) => {
12
+ const storageKey = `${DB_PREFIX}${key}`;
13
+ const existing = localStorage.getItem(storageKey);
14
+
15
+ if (!existing) {
16
+ console.log(`✨ Seeding collection: ${key} (${seedData.length} records)`);
17
+ localStorage.setItem(storageKey, JSON.stringify(seedData));
18
+ } else {
19
+ console.log(`✅ Collection exists: ${key}`);
20
+ }
21
+ });
22
+
23
+ console.groupEnd();
24
+ };
25
+
26
+ /**
27
+ * Utility to clear the lake (useful for a "Reset Data" button)
28
+ */
29
+ export const resetDataLake = () => {
30
+ // ✅ FIX: Use DATA_REGISTRY (the new name)
31
+ Object.keys(DATA_REGISTRY).forEach((key) => {
32
+ localStorage.removeItem(`${DB_PREFIX}${key}`);
33
+ });
34
+ window.location.reload();
35
+ };
@@ -1,43 +1,172 @@
1
- // src/data/mockData.ts
2
-
3
- // --- 1. Your Existing Examples ---
4
- export const mockChartData = [
5
- { name: 'Jan', uv: 400, pv: 240 },
6
- { name: 'Feb', uv: 300, pv: 139 },
7
- { name: 'Mar', uv: 200, pv: 980 },
8
- { name: 'Apr', uv: 278, pv: 390 },
9
- { name: 'May', uv: 189, pv: 480 },
10
- { name: 'Jun', uv: 239, pv: 380 },
11
- { name: 'Jul', uv: 349, pv: 430 },
1
+ /**
2
+ * @file src/data/mockData.ts
3
+ * @description The "Golden Copy" of seed data.
4
+ * These records are injected into localStorage when the app first launches.
5
+ */
6
+
7
+ // 1. Interface for the Metadata (Schema)
8
+ export interface ResourceMeta {
9
+ name: string;
10
+ fields: {
11
+ key: string;
12
+ label: string;
13
+ type: string;
14
+ required?: boolean;
15
+ defaultValue?: any;
16
+ description?: string;
17
+ }[];
18
+ }
19
+
20
+ // --- DATA INTERFACES ---
21
+
22
+ export interface User {
23
+ id: string;
24
+ name: string;
25
+ email: string;
26
+ role: 'admin' | 'editor' | 'viewer';
27
+ status: 'active' | 'pending' | 'banned';
28
+ avatar?: string;
29
+ joinedAt: string;
30
+ }
31
+
32
+ export interface Product {
33
+ id: string;
34
+ name: string;
35
+ category: string;
36
+ price: number;
37
+ stock: number;
38
+ status: 'in_stock' | 'low_stock' | 'out_of_stock';
39
+ }
40
+
41
+ export interface Invoice {
42
+ id: string;
43
+ userId: string; // Foreign Key -> User
44
+ amount: number;
45
+ status: 'paid' | 'pending' | 'overdue';
46
+ date: string;
47
+ }
48
+
49
+ export interface Review {
50
+ id: string;
51
+ productId: string; // Foreign Key -> Product
52
+ rating: number;
53
+ comment: string;
54
+ author: string;
55
+ date: string;
56
+ }
57
+
58
+ export interface ActivityLog {
59
+ id: string;
60
+ action: string;
61
+ entity: string;
62
+ timestamp: string;
63
+ user: string;
64
+ }
65
+
66
+ // --- SEED DATA ---
67
+
68
+ export const SEED_USERS: User[] = [
69
+ { id: 'usr_1', name: 'Alex Carter', email: 'alex@example.com', role: 'admin', status: 'active', joinedAt: '2023-01-15' },
70
+ { id: 'usr_2', name: 'Sarah Jenkins', email: 'sarah@example.com', role: 'editor', status: 'active', joinedAt: '2023-03-22' },
71
+ { id: 'usr_3', name: 'Mike Ross', email: 'mike@example.com', role: 'viewer', status: 'pending', joinedAt: '2023-05-10' },
72
+ { id: 'usr_4', name: 'Emily Blunt', email: 'emily@example.com', role: 'editor', status: 'banned', joinedAt: '2023-06-01' },
12
73
  ];
13
74
 
14
- export const mockTableData = [
15
- { make: 'Tesla', model: 'Model Y', price: 64950, electric: true },
16
- { make: 'Ford', model: 'F-Series', price: 33850, electric: false },
17
- { make: 'Toyota', model: 'Corolla', price: 29600, electric: false },
18
- { make: 'Mercedes', model: 'EQS', price: 102310, electric: true },
19
- { make: 'BMW', model: 'i4', price: 51400, electric: true },
75
+ export const SEED_PRODUCTS: Product[] = [
76
+ { id: 'prod_1', name: 'ErgoChair Pro', category: 'Furniture', price: 450, stock: 12, status: 'in_stock' },
77
+ { id: 'prod_2', name: 'Standing Desk', category: 'Furniture', price: 600, stock: 3, status: 'low_stock' },
78
+ { id: 'prod_3', name: 'Monitor Arm', category: 'Accessories', price: 120, stock: 0, status: 'out_of_stock' },
79
+ { id: 'prod_4', name: 'Mechanical Keyboard', category: 'Electronics', price: 180, stock: 25, status: 'in_stock' },
20
80
  ];
21
81
 
22
- // 2. FIXED: Energy Data (Recharts Format)
23
- // Recharts expects an Array of Objects, NOT { labels, datasets }
24
- export const energyHistoryData = [
25
- { time: "12am", value: 12 },
26
- { time: "4am", value: 19 },
27
- { time: "8am", value: 3 },
28
- { time: "12pm", value: 5 },
29
- { time: "4pm", value: 2 },
30
- { time: "8pm", value: 3 }
82
+ export const SEED_INVOICES: Invoice[] = [
83
+ { id: 'inv_001', userId: 'usr_2', amount: 450.00, status: 'paid', date: '2023-10-01' },
84
+ { id: 'inv_002', userId: 'usr_1', amount: 1200.50, status: 'pending', date: '2023-10-05' },
85
+ { id: 'inv_003', userId: 'usr_3', amount: 180.00, status: 'overdue', date: '2023-09-15' },
86
+ { id: 'inv_004', userId: 'usr_2', amount: 600.00, status: 'paid', date: '2023-10-10' },
31
87
  ];
32
88
 
33
- // --- 3. The Lookup Registry ---
34
- // This allows the Manifest to refer to data by string ID.
35
- export const MOCK_DATA_REGISTRY: Record<string, any> = {
36
- 'energy_history': energyHistoryData,
37
- 'demo_chart': mockChartData,
38
- 'demo_cars': mockTableData
89
+ export const SEED_REVIEWS: Review[] = [
90
+ { id: 'rev_1', productId: 'prod_1', rating: 5, comment: 'Life changing comfort!', author: 'Alex Carter', date: '2023-09-01' },
91
+ { id: 'rev_2', productId: 'prod_4', rating: 4, comment: 'Clicky but loud.', author: 'Mike Ross', date: '2023-09-10' },
92
+ { id: 'rev_3', productId: 'prod_2', rating: 2, comment: 'Wobbles at max height.', author: 'Sarah Jenkins', date: '2023-09-12' },
93
+ ];
94
+
95
+ export const SEED_LOGS: ActivityLog[] = [
96
+ { id: 'log_1', action: 'User Created', entity: 'User', user: 'System', timestamp: '2023-10-01T10:00:00Z' },
97
+ { id: 'log_2', action: 'Invoice Paid', entity: 'Invoice', user: 'Alex Carter', timestamp: '2023-10-02T14:30:00Z' },
98
+ { id: 'log_3', action: 'Stock Updated', entity: 'Product', user: 'Sarah Jenkins', timestamp: '2023-10-03T09:15:00Z' },
99
+ ];
100
+
101
+ // --- REGISTRIES ---
102
+
103
+ // 1. Data Registry (The Records)
104
+ export const DATA_REGISTRY: Record<string, any[]> = {
105
+ users: SEED_USERS,
106
+ products: SEED_PRODUCTS,
107
+ invoices: SEED_INVOICES,
108
+ reviews: SEED_REVIEWS,
109
+ logs: SEED_LOGS,
39
110
  };
40
111
 
41
- export const getMockData = (id: string) => {
42
- return MOCK_DATA_REGISTRY[id] || null;
112
+ // 2. Metadata Registry (The Schema/Definitions)
113
+ export const RESOURCE_METADATA: Record<string, ResourceMeta> = {
114
+ users: {
115
+ name: 'Users',
116
+ fields: [
117
+ { key: 'name', label: 'Name', type: 'text', required: true },
118
+ { key: 'email', label: 'Email', type: 'email', required: true },
119
+ { key: 'role', label: 'Role', type: 'status' },
120
+ { key: 'status', label: 'Status', type: 'status' },
121
+ { key: 'joinedAt', label: 'Joined', type: 'date' },
122
+ ]
123
+ },
124
+ products: {
125
+ name: 'Products',
126
+ fields: [
127
+ { key: 'name', label: 'Product Name', type: 'text', required: true },
128
+ { key: 'category', label: 'Category', type: 'text' },
129
+ { key: 'price', label: 'Price', type: 'currency' },
130
+ { key: 'stock', label: 'Stock', type: 'number' },
131
+ { key: 'status', label: 'Availability', type: 'status' },
132
+ ]
133
+ },
134
+ invoices: {
135
+ name: 'Invoices',
136
+ fields: [
137
+ // ✅ FIX: Changed label from 'User ID' to 'User'
138
+ { key: 'userId', label: 'User', type: 'text', required: true },
139
+ { key: 'amount', label: 'Amount', type: 'currency' },
140
+ { key: 'status', label: 'Status', type: 'status' },
141
+ { key: 'date', label: 'Date', type: 'date' },
142
+ ]
143
+ },
144
+ reviews: {
145
+ name: 'Reviews',
146
+ fields: [
147
+ // ✅ FIX: Changed label from 'Product ID' to 'Product'
148
+ { key: 'productId', label: 'Product', type: 'text' },
149
+ { key: 'rating', label: 'Rating', type: 'number' },
150
+ { key: 'comment', label: 'Comment', type: 'text' },
151
+ { key: 'author', label: 'Author', type: 'text' },
152
+ ]
153
+ },
154
+ logs: {
155
+ name: 'Activity Logs',
156
+ fields: [
157
+ { key: 'action', label: 'Action', type: 'text' },
158
+ { key: 'entity', label: 'Entity', type: 'text' },
159
+ { key: 'user', label: 'User', type: 'text' },
160
+ { key: 'timestamp', label: 'Time', type: 'date' },
161
+ ]
162
+ }
163
+ };
164
+
165
+ // --- HELPERS ---
166
+
167
+ export const getMockData = (id: string) => DATA_REGISTRY[id] || [];
168
+
169
+ // ✅ Updated to return ResourceMeta or null (instead of never/null)
170
+ export const getResourceMeta = (id: string): ResourceMeta | null => {
171
+ return RESOURCE_METADATA[id] || null;
43
172
  };