Dashboard Builder

Server-synced admin preferences with 4-layer merge engine, 61 customizable settings, FOUC prevention, 409 conflict resolution, and edition-based feature gating.

The Dashboard Builder is SCRIPE's enterprise-grade admin preference system that syncs 61 customizable dashboard settings between the browser and server. It uses a 4-layer merge engine (Platform → Tenant → Admin → Runtime) to resolve settings with tenant-level override control, cross-device persistence via AdminSettingsJson, and 5 edge case protections including FOUC prevention, tab-close keepalive, and field-level 409 conflict resolution.

System Overview

The Dashboard Builder provides a complete admin preference lifecycle — from immediate cache-first rendering to background server reconciliation. Every admin gets 61 customizable settings covering layout, colors, typography, component styles, and accessibility, all persisted to the server and synced across devices.

ComponentDescriptionTechnology
4-Layer Merge EnginePlatform → Tenant → Admin → Runtime settings resolutionReact Context + localStorage
Server Sync HookBidirectional sync of admin preferences to AdminSettingsJsonCustom React Hook + REST API
FOUC PreventionOptimistic render from cache, silent server reconcilelocalStorage + CustomEvent
Override ControlTenant admins control which settings admins can customizePath-level whitelist
Preset SystemPre-built and custom theme presets with marketplaceDatabase + JSON blobs
Edition GatingFeature visibility controlled by subscription tierFeatureChecker pipeline

4-Layer Merge Engine

Settings follow a strict 4-layer precedence chain. Each layer can override the previous, with optional path-level access control at the tenant layer. This architecture enables white-label deployments where tenant administrators enforce brand standards while still allowing individual admins to personalize their workspace within allowed boundaries.

Settings Merge Pipeline
Layer 1: Platform Defaults
Spread merge
Layer 3: Tenant Defaults
Path-filtered
Layer 4: Admin Overrides
Applied to DOM
Final Applied Settings
LayerSourcePersistenceScope
1. Platform DefaultsdefaultSettings in settings-provider.tsxHardcodedAll users
3. Tenant DefaultsDashboardThemeJson on TenantSettingsDatabase (tenant)All admins in tenant
4. Admin OverridesAdminSettingsJson on AdminDatabase (per-admin)Individual admin

Server Sync Hook

The useAdminSettingsSync hook manages the complete lifecycle of admin settings: initial load from cache, deferred flush of pending changes from previous sessions, background server fetch, silent reconciliation, debounced save on changes, and tab-close protection. The hook is wired in DashboardLayout — the root client component for all authenticated pages.

Admin Settings Lifecycle
Admin Logs In
User Changes Setting
Check localStorage Cache
2s Debounce
Check PENDING_SETTINGS_FLUSH
PUT to Server
GET AdminSettingsJson
Silent Reconcile
useAdminSettingsSync.ts — UsageTypeScript
// In DashboardLayout (authenticated layout root):
const { isSettingsReady } = useAdminSettingsSync();

// Shimmer only on first-ever device login (no cache)
if (!isSettingsReady) {
  return <LoadingShimmer />;
}

Widget & Grid Configuration Schema

Each widget placed on the dashboard builder canvas is a positioned block on a 12-column CSS Grid. The builder configures their columns, rows, alignment, specific props, zIndex, and visibility, and serializes these into the DashboardThemeJson alongside existing theme tokens.

DashboardWidget.ts SchemaTypeScript
export interface DashboardWidget {
  id: string;                      // Unique identifier
  type: DashboardWidgetType;       // 'statsCard' | 'chart' | 'dataTable' | 'quickActions' | ...
  gridColumn: string;              // CSS Grid span configuration (e.g. "1 / 7")
  gridRow: string;                 // CSS Grid row configuration (e.g. "1 / 3")
  alignment: WidgetAlignment;      // 'start' | 'center' | 'end' | 'stretch'
  props: Record<string, unknown>;  // Element specific settings (title, chartType, customUrl)
  zIndex: number;                  // Overlap layer control
  visible: boolean;                // Hide/show toggle
}

export interface DashboardBuilderCanvas {
  enabled: boolean;                // True if layout customization is enabled
  widgets: DashboardWidget[];      // Collection of positioned dashboard widgets
  gridRows: number;                // Configured row height (default: 6)
}

Edge Case Protections

The sync system handles 5 critical edge cases that are common in real-world enterprise deployments where admins work across multiple devices, close tabs without saving, and occasionally have network interruptions.

Edge CaseProblemSolution
FOUC (Flash of Unstyled Content)Blocking render for server fetch causes visible flashOptimistic render from localStorage cache; shimmer only on first-ever device
Tab Close Data Loss2s debounce means last change may be lostfetch({ keepalive: true }) with JWT in beforeunload handler
409 Concurrency ConflictTwo sessions editing same admin's settingsField-level last-write-wins merge using changedFieldsSinceLastSync tracker
Payload Size BombLarge base64 in logoText could overflow 10KB column8KB client-side guard with admin-settings-size-error event
JWT Expired at Tab Closebeforeunload fetch fails with 401Deferred flush via PENDING_SETTINGS_FLUSH localStorage key, flushed on next login

Settings Reference (61 Settings)

All 61 settings are organized into 9 sections. Each setting has a defined type, default value, DOM data-attribute for CSS theming, and an optional edition gate that controls which subscription tier can access it.

SectionCountExamplesEdition Gate
Layout & Structure7layoutTemplate, sidebarPosition, headerStyleAlways
Colors & Theme16colorTheme, gradientDirection, customPrimaryColorGradients / CustomColors
Typography & Spacing5fontSize, borderRadius, spacingSizeAlways
Component Styles16buttonStyle, inputStyle, tableStyleComponentStyles.Enabled
Logo & Branding5logoType, logoAnimation, logoSizeLogoCustomization.Enabled
Navigation & UX9navigationStyle, highContrast, showDetailPanelAlways
Toast Configuration3toastStyle, showToastIcons, toastDurationComponentStyles.Enabled
Hover Effects2hoverEffectType, hoverEffectIntensityHoverEffects.Enabled

Admin Override Control

Tenant administrators can control which settings individual admins are allowed to customize. When AllowAdminThemeOverride is true, the AllowedAdminSettingsJson field contains a JSON whitelist of setting paths that admins can modify. Settings not in the whitelist are rendered as read-only with a lock indicator.

ScenarioSettings Page Behavior
AllowAdminThemeOverride = true, no path filterFull edit mode (current behavior)
AllowAdminThemeOverride = true, with path filterMixed mode — editable settings show controls, locked settings show 🔒 badge
AllowAdminThemeOverride = falseFull read-only mode — all controls disabled with banner

Security Model

The Dashboard Builder implements defense-in-depth security to prevent cross-admin data leakage, payload overflow, and unauthorized settings modification.

ThreatMitigation
Cross-admin settings leakAUTH_STORAGE_KEYS_TO_CLEAR wipes all settings on logout
Raw theme key leakclearAllLocalStorage() removes theme and scripe_admin_prefs_version explicitly
Oversized payload8KB client guard + 10KB server column limit
Admin modifies locked settingServer validates keys against AllowedAdminSettingsJson
Concurrent 409 conflictField-level last-write-wins merge with toast notification
Tab-close data lossfetch({ keepalive: true }) with JWT Authorization header
JWT expired at tab closeDeferred flush via PENDING_SETTINGS_FLUSH (survives logout)

Architecture & File Map

The Dashboard Builder is implemented across 7 files in the core layer, following SCRIPE's provider-based architecture pattern. The sync hook and providers handle all state management, while the layout component serves as the entry point.

Key FilesText
src/core/providers/
├── useAdminSettingsSync.ts    # Server sync hook (5 edge case protections)
├── settings-provider.tsx      # 4-layer merge engine + field tracking
├── tenant-branding-provider.tsx # Layer 3 sync + override metadata

src/core/config/
├── storage-keys.ts            # Centralized localStorage key definitions
├── api-endpoints.ts           # Dashboard builder API endpoint constants

src/core/ui/layout/
├── dashboard-layout.tsx       # FOUC shimmer gate (layout entry point)

src/modules/auth/core/data/
├── repositories/AuthRepository.ts  # Logout cleanup (raw key removal)

src/modules/monitoring/dashboard/
├── domain/entities/DashboardWidget.ts # 12-column widget schema
├── presentation/components/dashboard-builder/DashboardBuilderPanel.tsx # DnD wrapper
└── presentation/viewmodels/useDashboardBuilderStore.ts # Zustand layout manager