@ramme-io/create-app 1.2.1 → 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 +62 -31
- 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/{generated/hooks.ts → engine/runtime/useSignalStore.ts} +35 -8
- package/template/src/{hooks → engine/runtime}/useWorkflowEngine.ts +34 -13
- package/template/src/{core → engine/types}/manifest-types.ts +35 -3
- package/template/src/{types → engine/validation}/schema.ts +17 -0
- 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/{blocks → features/datagrid}/SmartTable.tsx +4 -6
- package/template/src/{pages → features/onboarding/pages}/Welcome.tsx +0 -1
- 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 +26 -22
- 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/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/hooks/useSignal.ts +0 -83
- 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ramme-io/create-app",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-ramme-app": "./index.js"
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"zip:builder": "cd template && zip -r ../base.zip . -x \"node_modules/*\" \"dist/*\" \".DS_Store\" && mv ../base.zip ../../ramme-app-builder/public/base.zip && echo '✅ base.zip updated in Builder!'"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@ramme-io/ui": "^1.1.5",
|
|
22
21
|
"ag-grid-community": "^34.1.2",
|
|
23
22
|
"ag-grid-enterprise": "^34.1.2",
|
|
24
23
|
"ag-grid-react": "^34.1.2",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ramme-app",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@ai-sdk/google": "^0.0.10",
|
|
13
|
+
"@ramme-io/ui": "^1.2.2",
|
|
14
|
+
"@types/uuid": "^10.0.0",
|
|
15
|
+
"ag-grid-community": "^31.3.1",
|
|
16
|
+
"ag-grid-enterprise": "^31.3.1",
|
|
17
|
+
"ag-grid-react": "^31.3.1",
|
|
18
|
+
"ai": "^3.0.0",
|
|
19
|
+
"framer-motion": "^11.0.0",
|
|
20
|
+
"fs-extra": "^11.2.0",
|
|
21
|
+
"mqtt": "^5.3.5",
|
|
22
|
+
"react": "^18.2.0",
|
|
23
|
+
"react-dom": "^18.2.0",
|
|
24
|
+
"react-resizable-panels": "^2.0.9",
|
|
25
|
+
"react-router-dom": "^6.22.0",
|
|
26
|
+
"uuid": "^13.0.0",
|
|
27
|
+
"zod": "^3.22.0",
|
|
28
|
+
"zustand": "^4.5.7"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.11.0",
|
|
32
|
+
"@types/react": "^18.2.0",
|
|
33
|
+
"@types/react-dom": "^18.2.0",
|
|
34
|
+
"@vitejs/plugin-react": "^4.2.0",
|
|
35
|
+
"autoprefixer": "^10.4.19",
|
|
36
|
+
"postcss": "^8.4.38",
|
|
37
|
+
"tailwindcss": "^3.4.3",
|
|
38
|
+
"typescript": "^5.2.0",
|
|
39
|
+
"vite": "^5.2.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/template/pkg.json
CHANGED
package/template/src/App.tsx
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import { Routes, Route, Navigate } from 'react-router-dom';
|
|
2
|
-
import { generateRoutes } from './core/route-generator';
|
|
3
1
|
import { useEffect } from 'react';
|
|
2
|
+
import { Routes, Route, Navigate } from 'react-router-dom';
|
|
3
|
+
import { generateRoutes } from './engine/renderers/route-generator';
|
|
4
|
+
import { ThemeProvider } from '@ramme-io/ui';
|
|
5
|
+
import { AuthProvider } from './features/auth/AuthContext';
|
|
6
|
+
import { MqttProvider } from './engine/runtime/MqttContext';
|
|
7
|
+
|
|
8
|
+
// --- 1. IMPORT AUTH CLUSTER ---
|
|
9
|
+
import { AuthLayout } from './features/auth/pages/AuthLayout';
|
|
10
|
+
import LoginPage from './features/auth/pages/LoginPage';
|
|
11
|
+
import SignupPage from './features/auth/pages/SignupPage';
|
|
4
12
|
|
|
5
|
-
// ---
|
|
13
|
+
// --- 2. IMPORT TEMPLATES ---
|
|
6
14
|
import DashboardLayout from './templates/dashboard/DashboardLayout';
|
|
7
15
|
import { dashboardSitemap } from './templates/dashboard/dashboard.sitemap';
|
|
8
16
|
import DocsLayout from './templates/docs/DocsLayout';
|
|
@@ -12,43 +20,66 @@ import { settingsSitemap } from './templates/settings/settings.sitemap';
|
|
|
12
20
|
|
|
13
21
|
// Other Imports
|
|
14
22
|
import ProtectedRoute from './components/ProtectedRoute';
|
|
15
|
-
import
|
|
16
|
-
import NotFound from './pages/styleguide/NotFound';
|
|
23
|
+
import NotFound from './components/NotFound';
|
|
17
24
|
|
|
18
|
-
// ---
|
|
19
|
-
import { initializeDataLake } from './
|
|
25
|
+
// --- 3. DATA SEEDER ---
|
|
26
|
+
import { initializeDataLake } from './engine/runtime/data-seeder';
|
|
27
|
+
|
|
28
|
+
import ScrollToTop from './components/ScrollToTop';
|
|
29
|
+
import HashLinkScroll from './components/HashLinkScroll';
|
|
20
30
|
|
|
21
31
|
function App() {
|
|
22
32
|
|
|
23
|
-
//
|
|
33
|
+
// Trigger data seeding on mount
|
|
24
34
|
useEffect(() => {
|
|
25
35
|
initializeDataLake();
|
|
26
36
|
}, []);
|
|
27
37
|
|
|
28
38
|
return (
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
<ThemeProvider>
|
|
40
|
+
<AuthProvider>
|
|
41
|
+
<MqttProvider>
|
|
42
|
+
<ScrollToTop />
|
|
43
|
+
<HashLinkScroll />
|
|
44
|
+
<Routes>
|
|
45
|
+
{/* --- NEW AUTH CLUSTER --- */}
|
|
46
|
+
{/* This handles the layout and background for all auth pages */}
|
|
47
|
+
<Route path="/auth" element={<AuthLayout />}>
|
|
48
|
+
<Route index element={<Navigate to="/auth/login" replace />} />
|
|
49
|
+
<Route path="login" element={<LoginPage />} />
|
|
50
|
+
<Route path="signup" element={<SignupPage />} />
|
|
51
|
+
</Route>
|
|
52
|
+
|
|
53
|
+
{/* Fallback for legacy /login access */}
|
|
54
|
+
<Route path="/login" element={<Navigate to="/auth/login" replace />} />
|
|
55
|
+
|
|
56
|
+
{/* --- PROTECTED APP ROUTES --- */}
|
|
57
|
+
<Route element={<ProtectedRoute />}>
|
|
58
|
+
<Route path="/" element={<Navigate to="/dashboard/welcome" replace />} />
|
|
59
|
+
|
|
60
|
+
{/* Dashboard Template */}
|
|
61
|
+
<Route path="/dashboard/*" element={<DashboardLayout />}>
|
|
62
|
+
<Route index element={<Navigate to="welcome" replace />} />
|
|
63
|
+
{generateRoutes(dashboardSitemap)}
|
|
64
|
+
</Route>
|
|
65
|
+
|
|
66
|
+
{/* Docs Template */}
|
|
67
|
+
<Route path="/docs/*" element={<DocsLayout />}>
|
|
68
|
+
{generateRoutes(docsSitemap)}
|
|
69
|
+
</Route>
|
|
70
|
+
|
|
71
|
+
{/* Settings Template */}
|
|
72
|
+
<Route path="/settings/*" element={<SettingsLayout />}>
|
|
73
|
+
{generateRoutes(settingsSitemap)}
|
|
74
|
+
</Route>
|
|
75
|
+
</Route>
|
|
76
|
+
|
|
77
|
+
{/* 404 Handler */}
|
|
78
|
+
<Route path="*" element={<NotFound />} />
|
|
79
|
+
</Routes>
|
|
80
|
+
</MqttProvider>
|
|
81
|
+
</AuthProvider>
|
|
82
|
+
</ThemeProvider>
|
|
52
83
|
);
|
|
53
84
|
}
|
|
54
85
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
Button,
|
|
8
8
|
Icon,
|
|
9
9
|
} from '@ramme-io/ui';
|
|
10
|
-
import { useMockChat } from '../
|
|
10
|
+
import { useMockChat } from '../features/assistant/useMockChat';
|
|
11
11
|
|
|
12
12
|
interface AIChatWidgetProps {
|
|
13
13
|
onClose: () => void;
|
|
@@ -31,7 +31,7 @@ export const AIChatWidget: React.FC<AIChatWidgetProps> = ({ onClose }) => {
|
|
|
31
31
|
<div className="p-4 border-b flex justify-between items-center bg-muted/50 rounded-t-lg">
|
|
32
32
|
<div className="flex items-center gap-2">
|
|
33
33
|
<div className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
|
34
|
-
<span className="font-semibold text-sm">
|
|
34
|
+
<span className="font-semibold text-sm">AI Chat</span>
|
|
35
35
|
</div>
|
|
36
36
|
<Button variant="ghost" size="icon" onClick={onClose} className="h-6 w-6">
|
|
37
37
|
<Icon name="x" size={16} />
|
|
@@ -19,11 +19,11 @@ import {
|
|
|
19
19
|
useTheme,
|
|
20
20
|
type ThemeName,
|
|
21
21
|
} from '@ramme-io/ui';
|
|
22
|
-
import { useAuth } from '../
|
|
22
|
+
import { useAuth } from '../features/auth/AuthContext';
|
|
23
23
|
|
|
24
24
|
// --- STRATEGIC IMPORTS ---
|
|
25
25
|
import { appManifest } from '../config/navigation';
|
|
26
|
-
import type { ManifestLink } from '../
|
|
26
|
+
import type { ManifestLink } from '../engine/types/manifest-types';
|
|
27
27
|
import TemplateSwitcher from './TemplateSwitcher';
|
|
28
28
|
import rammeLogo from '../assets/orange.png'; // <-- 1. IMPORT THE LOGO
|
|
29
29
|
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { Drawer, FormTemplate, Button, type FormField } from '@ramme-io/ui';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @file AutoForm.tsx
|
|
6
|
+
* @description The "Zero-Boilerplate" Form Engine.
|
|
7
|
+
* * ARCHITECTURAL ROLE:
|
|
8
|
+
* This component acts as the bridge between your data schema (JSON) and the UI.
|
|
9
|
+
* Instead of writing manual form code for every resource (Users, Products, Orders),
|
|
10
|
+
* this component dynamically generates the correct inputs based on the field type.
|
|
11
|
+
* * KEY FEATURES:
|
|
12
|
+
* 1. **Smart Mapping:** Automatically converts 'boolean' -> Toggle, 'date' -> DatePicker, etc.
|
|
13
|
+
* 2. **Context Aware:** Auto-detects "Create" vs "Edit" mode based on `initialData`.
|
|
14
|
+
* 3. **Safety Logic:** Automatically hides primary keys during creation and locks them during editing.
|
|
15
|
+
*/
|
|
16
|
+
|
|
4
17
|
interface AutoFormProps {
|
|
5
18
|
isOpen: boolean;
|
|
6
19
|
onClose: () => void;
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import React, { useEffect } from 'react';
|
|
8
8
|
import { useLocation } from 'react-router-dom';
|
|
9
|
-
import { useSitemap } from '../
|
|
10
|
-
import type { SitemapEntry } from '../
|
|
9
|
+
import { useSitemap } from '../engine/runtime/SitemapContext'; // Import our context hook
|
|
10
|
+
import type { SitemapEntry } from '../engine/types/sitemap-entry';
|
|
11
11
|
|
|
12
12
|
// Helper function to recursively find a route by path
|
|
13
13
|
const findRouteByPath = (
|
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Navigate, Outlet } from 'react-router-dom';
|
|
3
|
-
import { useAuth } from '../
|
|
3
|
+
import { useAuth } from '../features/auth/AuthContext';
|
|
4
4
|
import { Icon } from '@ramme-io/ui';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @file ProtectedRoute.tsx
|
|
8
|
+
* @description The "Security Gatekeeper" for the application.
|
|
9
|
+
*
|
|
10
|
+
* ARCHITECTURAL ROLE:
|
|
11
|
+
* This component wraps all private routes (Dashboard, Settings, Docs) in `App.tsx`.
|
|
12
|
+
* It acts as a middleware that checks the user's authentication status before
|
|
13
|
+
* allowing access to the child components (Outlet).
|
|
14
|
+
*
|
|
15
|
+
* KEY FEATURES:
|
|
16
|
+
* 1. **Auth Verification:** Automatically redirects unauthenticated users to `/login`.
|
|
17
|
+
* 2. **Loading State:** Displays a spinner while the Auth Provider is initializing,
|
|
18
|
+
* preventing "flash of login screen" bugs.
|
|
19
|
+
* 3. **History Management:** Uses `replace` on redirect to ensure the browser
|
|
20
|
+
* back button works correctly (skips the restricted page).
|
|
21
|
+
*/
|
|
22
|
+
|
|
6
23
|
const ProtectedRoute: React.FC = () => {
|
|
7
24
|
const { user, isLoading } = useAuth();
|
|
8
25
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useLocation } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
const ScrollToTop = () => {
|
|
5
|
+
const { pathname } = useLocation();
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
// "instant" prevents the user from seeing the page scroll up
|
|
9
|
+
window.scrollTo({
|
|
10
|
+
top: 0,
|
|
11
|
+
left: 0,
|
|
12
|
+
behavior: 'instant',
|
|
13
|
+
});
|
|
14
|
+
}, [pathname]);
|
|
15
|
+
|
|
16
|
+
return null;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default ScrollToTop;
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '@ramme-io/ui';
|
|
14
14
|
|
|
15
15
|
// ✅ IMPORT YOUR CUSTOM COMPONENT
|
|
16
|
-
import { SmartTable } from '../
|
|
16
|
+
import { SmartTable } from '../features/datagrid/SmartTable';
|
|
17
17
|
|
|
18
18
|
export const COMPONENT_REGISTRY: Record<string, React.FC<any>> = {
|
|
19
19
|
// IoT Primitives
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* This is the central manifest for all GLOBAL, shared navigation elements.
|
|
6
6
|
* Corrected paths to point within the /settings layout.
|
|
7
7
|
*/
|
|
8
|
-
import type { ManifestLink } from '../
|
|
8
|
+
import type { ManifestLink } from '../engine/types/manifest-types';
|
|
9
9
|
|
|
10
10
|
interface AppManifest {
|
|
11
11
|
userMenu: ManifestLink[];
|
|
@@ -1,37 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @file mock-charts.ts
|
|
3
|
+
* @description The "Chart Data Lake".
|
|
4
|
+
*
|
|
5
|
+
* ARCHITECTURAL ROLE:
|
|
6
|
+
* This file stores the structured data required by Recharts components.
|
|
7
|
+
* Separating this from the tabular `mockData.ts` prevents bloat and allows
|
|
8
|
+
* for specific optimization of visualization data.
|
|
9
|
+
*/
|
|
3
10
|
|
|
4
|
-
//
|
|
5
|
-
|
|
11
|
+
// 1. Define a generic shape for Recharts data
|
|
12
|
+
// Recharts expects an array of objects: [{ name: 'A', value: 10 }, ...]
|
|
13
|
+
export type RechartsData = Record<string, any>[];
|
|
14
|
+
|
|
15
|
+
export const MOCK_CHART_DATA: Record<string, RechartsData> = {
|
|
6
16
|
|
|
7
17
|
// ID: energy_history
|
|
8
|
-
energy_history:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
tension: 0.4
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
},
|
|
18
|
+
energy_history: [
|
|
19
|
+
{ time: "12am", value: 12 },
|
|
20
|
+
{ time: "4am", value: 19 },
|
|
21
|
+
{ time: "8am", value: 3 },
|
|
22
|
+
{ time: "12pm", value: 5 },
|
|
23
|
+
{ time: "4pm", value: 2 },
|
|
24
|
+
{ time: "8pm", value: 3 }
|
|
25
|
+
],
|
|
20
26
|
|
|
21
27
|
// ID: server_load
|
|
22
|
-
server_load:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
]
|
|
32
|
-
}
|
|
28
|
+
server_load: [
|
|
29
|
+
{ day: "Mon", load: 45 },
|
|
30
|
+
{ day: "Tue", load: 52 },
|
|
31
|
+
{ day: "Wed", load: 38 },
|
|
32
|
+
{ day: "Thu", load: 70 },
|
|
33
|
+
{ day: "Fri", load: 65 },
|
|
34
|
+
{ day: "Sat", load: 30 },
|
|
35
|
+
{ day: "Sun", load: 40 }
|
|
36
|
+
]
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
export const getChartData = (id: string) => {
|
|
36
|
-
return MOCK_CHART_DATA[id] ||
|
|
40
|
+
return MOCK_CHART_DATA[id] || [];
|
|
37
41
|
};
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { getComponent } from '
|
|
2
|
+
import { getComponent } from '../../config/component-registry';
|
|
3
3
|
// @ts-ignore
|
|
4
4
|
import { useGeneratedSignals } from '../generated/hooks';
|
|
5
|
-
import { getMockData } from '
|
|
5
|
+
import { getMockData } from '../../data/mockData';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @file DynamicBlock.tsx
|
|
9
|
+
* @description The "Runtime Hydrator" for the application.
|
|
10
|
+
*
|
|
11
|
+
* ARCHITECTURAL ROLE:
|
|
12
|
+
* This component acts as the bridge between the Abstract Syntax Tree (JSON Manifest)
|
|
13
|
+
* and the concrete React UI.
|
|
14
|
+
*
|
|
15
|
+
* KEY RESPONSIBILITIES:
|
|
16
|
+
* 1. **Component Lookup:** Resolves string types ('StatCard') to actual React components.
|
|
17
|
+
* 2. **Signal Injection:** Subscribes to the real-time Signal Engine and feeds live values to props.
|
|
18
|
+
* 3. **Data Hydration:** Fetches static or async data (users, logs) based on `dataId`.
|
|
19
|
+
* 4. **Status Normalization:** Translates system-level signal states into UI-friendly status colors.
|
|
20
|
+
*/
|
|
6
21
|
|
|
7
22
|
const mapSignalStatus = (status: string): string => {
|
|
8
23
|
switch (status) {
|
|
@@ -39,18 +54,23 @@ export const DynamicBlock: React.FC<any> = ({ block }) => {
|
|
|
39
54
|
const signalState = signals[signalId];
|
|
40
55
|
|
|
41
56
|
if (signalState) {
|
|
42
|
-
// ✅ FIX:
|
|
43
|
-
|
|
44
|
-
|
|
57
|
+
// ✅ FIX: Check for null and handle objects vs raw values
|
|
58
|
+
const isSignalObject = typeof signalState === 'object' && signalState !== null;
|
|
59
|
+
|
|
60
|
+
// Extract Value
|
|
61
|
+
const rawValue = (isSignalObject && 'value' in signalState)
|
|
45
62
|
? signalState.value
|
|
46
63
|
: signalState;
|
|
47
64
|
|
|
48
|
-
|
|
65
|
+
// Extract Status
|
|
66
|
+
const rawStatus = (isSignalObject && 'status' in signalState)
|
|
49
67
|
? signalState.status
|
|
50
68
|
: 'fresh'; // Default for raw values
|
|
51
69
|
|
|
52
70
|
dynamicProps.value = typeof rawValue === 'number' ? rawValue : String(rawValue);
|
|
53
|
-
|
|
71
|
+
|
|
72
|
+
// ✅ FIX: Explicitly cast to String to satisfy TypeScript
|
|
73
|
+
dynamicProps.status = mapSignalStatus(String(rawStatus));
|
|
54
74
|
} else {
|
|
55
75
|
dynamicProps.status = mapSignalStatus('disconnected');
|
|
56
76
|
}
|
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { PageHeader, Alert, Button, Icon } from '@ramme-io/ui';
|
|
3
|
-
import { appManifest } from '
|
|
3
|
+
import { appManifest } from '../../config/app.manifest';
|
|
4
4
|
// Reuse the GhostOverlay for structure visualization
|
|
5
|
-
import { GhostOverlay } from '
|
|
5
|
+
import { GhostOverlay } from '../../features/developer/GhostOverlay';
|
|
6
6
|
// Import the Worker Bee
|
|
7
|
-
import { DynamicBlock } from '
|
|
7
|
+
import { DynamicBlock } from './DynamicBlock';
|
|
8
8
|
// Import the Hook to control the overlay
|
|
9
|
-
import { useDevTools } from '
|
|
9
|
+
import { useDevTools } from '../../features/developer/useDevTools';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @file DynamicPage.tsx
|
|
13
|
+
* @description The "Layout Engine" of the application.
|
|
14
|
+
*
|
|
15
|
+
* ARCHITECTURAL ROLE:
|
|
16
|
+
* This component is the bridge between the Abstract JSON Manifest and the Concrete DOM.
|
|
17
|
+
* It acts as the "General Contractor," taking the blueprint (Manifest) and instructing
|
|
18
|
+
* the workers (DynamicBlock) where to build.
|
|
19
|
+
*
|
|
20
|
+
* HIERARCHY:
|
|
21
|
+
* App -> Routes -> DynamicPage -> (Iterates Sections) -> DynamicBlock -> (Actual Component)
|
|
22
|
+
*
|
|
23
|
+
* KEY FEATURES:
|
|
24
|
+
* 1. **Manifest Lookup:** Uses the `pageId` prop to find the correct configuration in `app.manifest.ts`.
|
|
25
|
+
* 2. **Grid Composition:** Translates abstract layout numbers (e.g., `columns: 3`) into real CSS Grid styles.
|
|
26
|
+
* 3. **DevTools Integration:** Wraps blocks in `GhostOverlay` (when enabled) to visualize the
|
|
27
|
+
* hidden boundaries and signal connections during development.
|
|
28
|
+
*/
|
|
10
29
|
|
|
11
30
|
interface DynamicPageProps {
|
|
12
31
|
pageId: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import mqtt, { type MqttClient } from 'mqtt';
|
|
3
|
+
import { appManifest } from '../../config/app.manifest'; // ✅ Connect to the Single Source of Truth
|
|
3
4
|
|
|
4
5
|
interface MqttContextType {
|
|
5
6
|
isConnected: boolean;
|
|
@@ -11,22 +12,33 @@ interface MqttContextType {
|
|
|
11
12
|
|
|
12
13
|
const MqttContext = createContext<MqttContextType | null>(null);
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* @file MqttContext.tsx
|
|
17
|
+
* @description The Real-Time Connectivity Layer.
|
|
18
|
+
*
|
|
19
|
+
* ARCHITECTURAL ROLE:
|
|
20
|
+
* This provider establishes a persistent WebSocket connection to the MQTT Broker
|
|
21
|
+
* defined in `app.manifest.ts`.
|
|
22
|
+
*
|
|
23
|
+
* KEY FEATURES:
|
|
24
|
+
* 1. **Global Connection:** Maintains one connection for the whole app (Singleton pattern).
|
|
25
|
+
* 2. **Topic Management:** specific components (like DeviceCard) can subscribe to
|
|
26
|
+
* specific topics on demand using `subscribe()`.
|
|
27
|
+
* 3. **State Distribution:** Broadcasts the latest messages to any component using `useMqtt()`.
|
|
28
|
+
*/
|
|
17
29
|
|
|
18
30
|
export const MqttProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
19
31
|
const [isConnected, setIsConnected] = useState(false);
|
|
20
32
|
const [lastMessage, setLastMessage] = useState<Record<string, string>>({});
|
|
21
33
|
const clientRef = useRef<MqttClient | null>(null);
|
|
22
|
-
|
|
23
|
-
// Track active subscriptions to avoid double-subscribing
|
|
24
34
|
const subscriptions = useRef<Set<string>>(new Set());
|
|
25
35
|
|
|
26
36
|
useEffect(() => {
|
|
27
|
-
|
|
37
|
+
// ✅ Load Broker URL from the Manifest Config
|
|
38
|
+
const brokerUrl = appManifest.config.brokerUrl || 'wss://test.mosquitto.org:8081';
|
|
39
|
+
console.log(`[MQTT] Connecting to ${brokerUrl}...`);
|
|
28
40
|
|
|
29
|
-
const client = mqtt.connect(
|
|
41
|
+
const client = mqtt.connect(brokerUrl);
|
|
30
42
|
clientRef.current = client;
|
|
31
43
|
|
|
32
44
|
client.on('connect', () => {
|
|
@@ -34,12 +46,14 @@ export const MqttProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|
|
34
46
|
setIsConnected(true);
|
|
35
47
|
});
|
|
36
48
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
// ✅ FIX: Proper typing for MQTT payload (Buffer)
|
|
50
|
+
client.on('message', (topic: string, payload: Buffer) => {
|
|
51
|
+
const messageStr = payload.toString();
|
|
52
|
+
// console.log(`[MQTT] Msg: ${topic} -> ${messageStr}`); // Optional debug
|
|
53
|
+
setLastMessage((prev) => ({ ...prev, [topic]: messageStr }));
|
|
40
54
|
});
|
|
41
55
|
|
|
42
|
-
client.on('error', (err
|
|
56
|
+
client.on('error', (err) => {
|
|
43
57
|
console.error('[MQTT] Connection error: ', err);
|
|
44
58
|
client.end();
|
|
45
59
|
});
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import { createContext, useContext } from 'react';
|
|
22
22
|
// Import the canonical "schema" for a sitemap entry
|
|
23
|
-
import type { SitemapEntry } from '../
|
|
23
|
+
import type { SitemapEntry } from '../types/sitemap-entry';
|
|
24
24
|
|
|
25
25
|
// 1. Create the context
|
|
26
26
|
// We initialize it with an empty array as a safe default.
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
// src/core/data-seeder.ts
|
|
2
|
+
|
|
1
3
|
// ✅ Match the export from your new mockData.ts
|
|
2
|
-
import { DATA_REGISTRY } from '
|
|
4
|
+
import { DATA_REGISTRY } from '../../data/mockData';
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
/**
|
|
7
|
+
* @file data-seeder.ts
|
|
8
|
+
* @description The "Big Bang" for the client-side database.
|
|
9
|
+
* * ARCHITECTURAL ROLE:
|
|
10
|
+
* This utility ensures that the LocalStorage "Data Lake" is never empty.
|
|
11
|
+
* On app launch, it checks if data exists. If not, it writes the seed data
|
|
12
|
+
* from `mockData.ts` into the browser's storage.
|
|
13
|
+
* * This allows the app to feel "alive" with data immediately after installation.
|
|
14
|
+
*/
|
|
5
15
|
|
|
6
16
|
export const initializeDataLake = () => {
|
|
7
17
|
if (typeof window === 'undefined') return;
|
|
@@ -9,7 +19,8 @@ export const initializeDataLake = () => {
|
|
|
9
19
|
console.groupCollapsed('🌊 [Data Lake] Initialization');
|
|
10
20
|
|
|
11
21
|
Object.entries(DATA_REGISTRY).forEach(([key, seedData]) => {
|
|
12
|
-
|
|
22
|
+
// ⚠️ REMOVED PREFIX ('ramme_db_') so it matches AuthContext
|
|
23
|
+
const storageKey = key;
|
|
13
24
|
const existing = localStorage.getItem(storageKey);
|
|
14
25
|
|
|
15
26
|
if (!existing) {
|
|
@@ -27,9 +38,8 @@ export const initializeDataLake = () => {
|
|
|
27
38
|
* Utility to clear the lake (useful for a "Reset Data" button)
|
|
28
39
|
*/
|
|
29
40
|
export const resetDataLake = () => {
|
|
30
|
-
// ✅ FIX: Use DATA_REGISTRY (the new name)
|
|
31
41
|
Object.keys(DATA_REGISTRY).forEach((key) => {
|
|
32
|
-
localStorage.removeItem(
|
|
42
|
+
localStorage.removeItem(key); // Matches the storageKey above
|
|
33
43
|
});
|
|
34
44
|
window.location.reload();
|
|
35
45
|
};
|