diff --git a/Data/Engine/web-interface/src/Admin/Page_Template.jsx b/Data/Engine/web-interface/src/Admin/Page_Template.jsx new file mode 100644 index 00000000..fc2308e6 --- /dev/null +++ b/Data/Engine/web-interface/src/Admin/Page_Template.jsx @@ -0,0 +1,302 @@ +import React, { useMemo, useRef } from "react"; +import { + Paper, + Box, + Typography, + Button, + IconButton, + Stack, + Tooltip, +} from "@mui/material"; +import { + Dashboard as TemplateIcon, + Cached as RefreshIcon, + Add as AddIcon, + Tune as TuneIcon, +} from "@mui/icons-material"; +import { AgGridReact } from "ag-grid-react"; +import { ModuleRegistry, AllCommunityModule, themeQuartz } from "ag-grid-community"; + +/** + * ============================================================================ + * Borealis MagicUI — Page Template + * --------------------------------------------------------------------------- + * PURPOSE + * • A *visual-only* reference for building Borealis pages. + * • NO API calls, NO business logic. Everything is placeholder/demo. + * • Use this as a baseline for new pages to ensure perfect visual parity. + * + * WHAT THIS TEMPLATE SHOWS + * 1) Full-bleed gradient shell using the Borealis Aurora pattern. + * 2) Page header with Material icon to the LEFT of the title. + * 3) Subtitle directly beneath the title. + * 4) Top-right utility buttons (e.g., Refresh). + * 5) AG Grid using the Quartz theme with rounded corners, sorting, filtering, + * pagination, and example data/columns. + * 6) Gradient buttons consistent with MagicUI accents. + * + * DOs + * ✓ Keep pages full-bleed to their parent container (no gutters on the Paper). + * ✓ Use the same Aurora gradient shell across pages. + * ✓ Use IBM Plex Sans for a consistent typographic tone. + * ✓ Keep header icon SMALL (around 20–24px) and aligned with the title baseline. + * ✓ Put a concise subtitle under the page title to orient the user. + * ✓ Prefer rounded corners (8px) for grid chrome and panels. + * ✓ Use AG Grid Quartz theme and scope theme CSS vars on the wrapper. + * ✓ Use gradient-filled primary buttons for key actions. + * + * DON'Ts + * ✗ Do not introduce API/data fetching in this template (copy into real pages). + * ✗ Do not change global AG Grid CSS (keep theme-scoped). + * ✗ Do not mix multiple font families; stick to IBM Plex Sans. + * ✗ Do not leave empty margins around the page shell. + * ✗ Do not hardcode fixed heights unless absolutely necessary. + * + * HOW TO ADAPT THIS TEMPLATE + * • Replace the static sample rows/columns with your real grid model. + * • Wire the Refresh button to your data loader (keep the icon + placement). + * • Keep the theme params/vars to maintain cross-page visual cohesion. + * + * REFERENCES + * • See other pages for style parity (aurora shell, Quartz theme usage, + * rounded grid edges, and gradient buttons). + * ============================================================================ + */ + +// ----------------------------------------------------------------------------- +// AG Grid module registration (community only, consistent with other pages) +// ----------------------------------------------------------------------------- +ModuleRegistry.registerModules([AllCommunityModule]); + +// ----------------------------------------------------------------------------- +// MagicUI x Quartz Theme +// Keep these params consistent across pages for color/typography parity. +// ----------------------------------------------------------------------------- +const gridTheme = themeQuartz.withParams({ + accentColor: "#8b5cf6", // Indigo/Violet accent (matches MagicUI) + backgroundColor: "#070b1a", // Deep navy panel background + browserColorScheme: "dark", + fontFamily: { googleFont: "IBM Plex Sans" }, + foregroundColor: "#f4f7ff", + headerFontSize: 13, +}); +const themeClassName = gridTheme.themeName || "ag-theme-quartz"; + +// ----------------------------------------------------------------------------- +/** Aurora gradient shell colors — identical across pages for cohesion. */ +// ----------------------------------------------------------------------------- +const AURORA_SHELL = { + background: + "radial-gradient(120% 120% at 0% 0%, rgba(76, 186, 255, 0.16), transparent 55%), " + + "radial-gradient(120% 120% at 100% 0%, rgba(214, 130, 255, 0.18), transparent 60%), #040711", + text: "#e2e8f0", + subtext: "#9ba3b4", + accent: "#7dd3fc", +}; + +// ----------------------------------------------------------------------------- +// Gradient button style — use for primary CTAs to feel 'alive' and branded. +// ----------------------------------------------------------------------------- +const gradientButtonSx = { + backgroundImage: "linear-gradient(135deg,#7dd3fc,#c084fc)", + color: "#0b1220", + borderRadius: 999, + textTransform: "none", + boxShadow: "0 10px 26px rgba(124,58,237,0.28)", + "&:hover": { + backgroundImage: "linear-gradient(135deg,#86e1ff,#d1a6ff)", + boxShadow: "0 12px 34px rgba(124,58,237,0.38)", + filter: "none", + }, +}; + +// ----------------------------------------------------------------------------- +// Example grid data — placeholder only. Replace with real rows on real pages. +// ----------------------------------------------------------------------------- +const SAMPLE_ROWS = [ + { id: "ROW-001", category: "Example", name: "Gemini Borealis", owner: "alice", updated: "2025-06-12 10:32" }, + { id: "ROW-002", category: "Demo", name: "Aurora Runner", owner: "bob", updated: "2025-07-01 14:05" }, + { id: "ROW-003", category: "Sample", name: "Quartz Tables", owner: "carol", updated: "2025-08-20 09:18" }, + { id: "ROW-004", category: "Guide", name: "MagicUI Rules", owner: "dave", updated: "2025-09-03 16:41" }, + { id: "ROW-005", category: "Pattern", name: "Borealis Blue", owner: "erin", updated: "2025-10-11 08:27" }, +]; + +// ----------------------------------------------------------------------------- +// Column definitions — keep sorting/filtering enabled; rounded edges come from +// the theme vars on the wrapper element below. +// ----------------------------------------------------------------------------- +const sampleColumnDefs = [ + { headerName: "ID", field: "id", minWidth: 140, sortable: true, filter: "agTextColumnFilter" }, + { headerName: "Category", field: "category", minWidth: 140, sortable: true, filter: "agTextColumnFilter" }, + { headerName: "Name", field: "name", minWidth: 220, sortable: true, filter: "agTextColumnFilter" }, + { headerName: "Owner", field: "owner", minWidth: 140, sortable: true, filter: "agTextColumnFilter" }, + { headerName: "Updated", field: "updated", minWidth: 180, sortable: true, filter: "agTextColumnFilter" }, +]; + +const defaultColDef = { + sortable: true, + filter: "agTextColumnFilter", + resizable: true, + minWidth: 140, +}; + +const gridFontFamily = "'IBM Plex Sans','Helvetica Neue',Arial,sans-serif"; +const iconFontFamily = "'Quartz Regular'"; + +// ----------------------------------------------------------------------------- +// Page Template Component +// ----------------------------------------------------------------------------- +export default function PageTemplate() { + const gridRef = useRef(null); + + // NOTE: No data fetching, no side effects — this is a pure visual template. + const columnDefs = useMemo(() => sampleColumnDefs, []); + + const handleRefresh = () => { + // In real pages, call your data loader. Here we do nothing (visual only). + // Keeping the icon + placement ensures consistent muscle memory. + // eslint-disable-next-line no-console + console.log("Refresh clicked (template; no-op)."); + }; + + return ( + + {/* Page header */} + + + + + Page Template + + + {/* Top-right controls: keep order + sizes consistent project-wide */} + + + + + + + + + + + + + + + + + + + + + + {/* Subtitle directly under title */} + + Page Styling Guide and Template - Use as a baseline when designing new pages. + + + + {/* Content area — the grid lives in a chromeless card that still keeps rounded corners */} + + + + + + + ); +} diff --git a/Data/Engine/web-interface/src/App.jsx b/Data/Engine/web-interface/src/App.jsx index dceb4bdb..c677e6ce 100644 --- a/Data/Engine/web-interface/src/App.jsx +++ b/Data/Engine/web-interface/src/App.jsx @@ -46,6 +46,7 @@ import CredentialList from "./Access_Management/Credential_List.jsx"; import UserManagement from "./Access_Management/Users.jsx"; import GithubAPIToken from "./Access_Management/Github_API_Token.jsx"; import ServerInfo from "./Admin/Server_Info.jsx"; +import PageTemplate from "./Admin/Page_Template.jsx"; import EnrollmentCodes from "./Devices/Enrollment_Codes.jsx"; import DeviceApprovals from "./Devices/Device_Approvals.jsx"; @@ -224,6 +225,8 @@ const LOCAL_STORAGE_KEY = "borealis_persistent_state"; return "/access_management/users"; case "server_info": return "/admin/server_info"; + case "page_template": + return "/admin/page_template"; case "admin_enrollment_codes": return "/admin/enrollment-codes"; case "admin_device_approvals": @@ -279,6 +282,7 @@ const LOCAL_STORAGE_KEY = "borealis_persistent_state"; if (path === "/access_management/github_token") return { page: "access_github_token", options: {} }; if (path === "/access_management/credentials") return { page: "access_credentials", options: {} }; if (path === "/admin/server_info") return { page: "server_info", options: {} }; + if (path === "/admin/page_template") return { page: "page_template", options: {} }; if (path === "/admin/enrollment-codes") return { page: "admin_enrollment_codes", options: {} }; if (path === "/admin/device-approvals") return { page: "admin_device_approvals", options: {} }; return { page: "devices", options: {} }; @@ -458,6 +462,10 @@ const LOCAL_STORAGE_KEY = "borealis_persistent_state"; items.push({ label: "Admin Settings" }); items.push({ label: "Server Info", page: "server_info" }); break; + case "page_template": + items.push({ label: "Developer Tools" }); + items.push({ label: "Page Template", page: "page_template" }); + break; case "admin_enrollment_codes": items.push({ label: "Admin Settings", page: "server_info" }); items.push({ label: "Installer Codes", page: "admin_enrollment_codes" }); @@ -998,7 +1006,8 @@ const LOCAL_STORAGE_KEY = "borealis_persistent_state"; || currentPage === 'access_users' || currentPage === 'ssh_devices' || currentPage === 'winrm_devices' - || currentPage === 'agent_devices'; + || currentPage === 'agent_devices' + || currentPage === 'page_template'; if (!isAdmin && requiresAdmin) { setNotAuthorizedOpen(true); navigateTo('devices', { replace: true, suppressPending: true }); @@ -1124,6 +1133,9 @@ const LOCAL_STORAGE_KEY = "borealis_persistent_state"; case "server_info": return ; + case "page_template": + return ; + case "admin_enrollment_codes": return ; diff --git a/Data/Engine/web-interface/src/Navigation_Sidebar.jsx b/Data/Engine/web-interface/src/Navigation_Sidebar.jsx index 5103c6a5..7bde2d1b 100644 --- a/Data/Engine/web-interface/src/Navigation_Sidebar.jsx +++ b/Data/Engine/web-interface/src/Navigation_Sidebar.jsx @@ -25,6 +25,7 @@ import { PersonOutline as UserIcon, GitHub as GitHubIcon, Key as KeyIcon, + Dashboard as PageTemplateIcon, AdminPanelSettings as AdminPanelSettingsIcon, } from "@mui/icons-material"; @@ -68,7 +69,7 @@ function NavigationSidebar({ currentPage, onNavigate, isAdmin = false }) { "access_users", "access_github_token", ].includes(currentPage), - admin: ["server_info"].includes(currentPage), + admin: ["server_info", "page_template"].includes(currentPage), }), [currentPage] ); @@ -285,6 +286,11 @@ function NavigationSidebar({ currentPage, onNavigate, isAdmin = false }) { label="Server Info" pageKey="server_info" /> + } + label="Page Template" + pageKey="page_template" + /> )}