From 081bf10558d77f9827dec2cea726bbd449bcb298 Mon Sep 17 00:00:00 2001 From: Mayur Shinde Date: Wed, 10 Jun 2026 12:58:48 +0530 Subject: [PATCH] enable wizard admin features --- .../estimateWizard/EstimateWizardContext.jsx | 20 +++++++++++++++++-- .../estimateWizard/data/tenantConfig.js | 11 ++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/modules/estimateWizard/EstimateWizardContext.jsx b/src/modules/estimateWizard/EstimateWizardContext.jsx index 9c3221c..814aeeb 100644 --- a/src/modules/estimateWizard/EstimateWizardContext.jsx +++ b/src/modules/estimateWizard/EstimateWizardContext.jsx @@ -13,7 +13,7 @@ import React, { createContext, useContext, useState, useEffect, useCallback } from 'react'; import { STEP_FLOW } from './data/questions'; -import { getTenantConfig } from './data/tenantConfig'; +import { getTenantConfig, CONFIG_CHANGE_EVENT } from './data/tenantConfig'; const STORAGE_KEY = 'lup_estimate_wizard_session_v1'; @@ -92,10 +92,26 @@ function loadSession() { } export function EstimateWizardProvider({ children, source = 'public' }) { - const [tenantConfig] = useState(getTenantConfig); + // Snapshot tenant config at mount, but stay reactive: the admin can publish flags + // (kill-switch, AI copy, conservative mode), pricing, and package content while a + // wizard is open. Refresh on same-tab publish, cross-tab storage writes, and on + // tab focus so toggles take effect without a hard reload (spec §14/§18). + const [tenantConfig, setTenantConfig] = useState(getTenantConfig); const [session, setSession] = useState(() => loadSession() || freshSession(source)); const [resumed] = useState(() => !!loadSession()); + useEffect(() => { + const refresh = () => setTenantConfig(getTenantConfig()); + window.addEventListener(CONFIG_CHANGE_EVENT, refresh); // same-tab admin publish + window.addEventListener('storage', refresh); // another tab published + window.addEventListener('focus', refresh); // returning to this tab + return () => { + window.removeEventListener(CONFIG_CHANGE_EVENT, refresh); + window.removeEventListener('storage', refresh); + window.removeEventListener('focus', refresh); + }; + }, []); + // Autosave on every change (debounced via microtask coalescing not needed at this scale). useEffect(() => { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(session)); } catch { /* quota */ } diff --git a/src/modules/estimateWizard/data/tenantConfig.js b/src/modules/estimateWizard/data/tenantConfig.js index c5d6eec..82358e5 100644 --- a/src/modules/estimateWizard/data/tenantConfig.js +++ b/src/modules/estimateWizard/data/tenantConfig.js @@ -183,6 +183,15 @@ export const DISCLAIMER_VERSION = 'disclaimer-v1'; // the defaults so pricing/branding/feature flags are tenant-controlled, not code. const CONFIG_STORAGE_KEY = 'lup_estimate_wizard_config_v1'; +// Same-tab change signal. The public wizard snapshots config at mount, so when the +// admin publishes in the SAME tab/SPA there's no `storage` event to react to — we +// dispatch this so an open wizard can refresh without a hard reload. +export const CONFIG_CHANGE_EVENT = 'lup-estimate-wizard-config-changed'; + +function notifyConfigChanged() { + try { window.dispatchEvent(new Event(CONFIG_CHANGE_EVENT)); } catch { /* SSR/no window */ } +} + function defaults() { return { version: TENANT_CONFIG_VERSION, @@ -211,10 +220,12 @@ export function loadConfigOverrides() { export function saveConfigOverrides(overrides) { try { localStorage.setItem(CONFIG_STORAGE_KEY, JSON.stringify(overrides || {})); } catch { /* quota */ } + notifyConfigChanged(); } export function resetConfigOverrides() { try { localStorage.removeItem(CONFIG_STORAGE_KEY); } catch { /* ignore */ } + notifyConfigChanged(); } /**