feat: control maintenance banner with a feature flag#1968
Conversation
WalkthroughReplaced env-driven maintenance banner with an Unleash variant-based feature flag. Layout and TopBanner now read the "maintenance_banner" variant via a new Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Layout as Layout.tsx
participant VariantHook as useVariant
participant Unleash as Unleash / Variant Store
participant Banner as MaintenanceBanner
User->>Layout: Request page
Layout->>VariantHook: useVariant("maintenance_banner")
VariantHook->>Unleash: fetch variant (or return dummy if ENABLE_ALL)
Unleash-->>VariantHook: variant { enabled, payload }
VariantHook-->>Layout: variant
alt variant.enabled == true
Layout->>Banner: render with payload.message / payload.date (open=true)
else
Layout->>Banner: do not render (or keep closed)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@jzsfkzm we don't want to only enable or disable it, we also want to change the message without redeployment. For example, we will need to change date. Sorry this was not specified anywhere in the task but maintanance related variables should be controlled dynamically. FFs in unleash has variants, later this variants can be retried on client side. I thought to use variants to store message and date |
f5a8752 to
85df374
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/deploy-web/src/components/layout/Layout.tsx (1)
56-104: Ensure maintenance banner reacts to flag updates
isMaintenanceBannerOpenis initialised once and never re-synchronised with the Unleash flag. If the flag flips from false→true while the user is on the page, the banner stays closed, so we don't satisfy the “toggle dynamically without redeploy” requirement. Please derive the open state from the latestmaintenanceBannerFlag.enabledvalue.- const [isMaintenanceBannerOpen, setIsMaintenanceBannerOpen] = useState(!!maintenanceBannerFlag.enabled); + const [isMaintenanceBannerOpen, setIsMaintenanceBannerOpen] = useState(!!maintenanceBannerFlag.enabled); + + useEffect(() => { + if (!maintenanceBannerFlag.enabled) { + setIsMaintenanceBannerOpen(false); + return; + } + + setIsMaintenanceBannerOpen(true); + }, [maintenanceBannerFlag.enabled]);apps/deploy-web/src/components/layout/TopBanner.tsx (1)
22-36: Guard against invalid maintenance banner payloads
JSON.parse(maintenanceBannerFlag.payload?.value as string)will throw when the payload is absent, not JSON, or when the variant carries a plain string. Any of those cases crashes the entire layout, andFormattedDatewill also raise if you pass an empty/invalid date. We need defensive parsing plus a validity check before rendering the date.-export function MaintenanceBanner({ onClose }: { onClose: () => void }) { - const maintenanceBannerFlag = useVariant("maintenance_banner"); - - const { message, date } = maintenanceBannerFlag.enabled - ? (JSON.parse(maintenanceBannerFlag.payload?.value as string) as unknown as { message: string; date: string }) - : { message: "", date: "" }; +export function MaintenanceBanner({ onClose }: { onClose: () => void }) { + const maintenanceBannerFlag = useVariant("maintenance_banner"); + + const payloadValue = maintenanceBannerFlag.payload?.value; + let message = ""; + let upgradeDate: Date | undefined; + + if (maintenanceBannerFlag.enabled && typeof payloadValue === "string") { + try { + const parsedPayload = JSON.parse(payloadValue) as { message?: unknown; date?: unknown }; + + if (typeof parsedPayload.message === "string") { + message = parsedPayload.message; + } + + if (typeof parsedPayload.date === "string") { + const parsedDate = new Date(parsedPayload.date); + if (!Number.isNaN(parsedDate.getTime())) { + upgradeDate = parsedDate; + } + } + } catch (error) { + console.error("Invalid maintenance_banner payload", error); + } + } return ( <div className="fixed top-0 z-10 flex h-[40px] w-full items-center justify-center bg-primary px-3 py-2 md:space-x-4"> <span className="text-xs font-semibold text-white md:text-sm"> - {message} Upgrade time: <FormattedDate value={date} year="numeric" month="2-digit" day="2-digit" hour="2-digit" minute="2-digit" /> + {message} + {upgradeDate ? ( + <> + {" "} + Upgrade time: <FormattedDate value={upgradeDate} year="numeric" month="2-digit" day="2-digit" hour="2-digit" minute="2-digit" /> + </> + ) : null} </span>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
apps/deploy-web/env/.env.production(0 hunks)apps/deploy-web/env/.env.sample(0 hunks)apps/deploy-web/env/.env.staging(0 hunks)apps/deploy-web/src/components/layout/Layout.tsx(3 hunks)apps/deploy-web/src/components/layout/TopBanner.tsx(2 hunks)apps/deploy-web/src/config/browser-env.config.ts(0 hunks)apps/deploy-web/src/config/env-config.schema.ts(0 hunks)apps/deploy-web/src/hooks/useVariant.tsx(1 hunks)apps/deploy-web/src/types/feature-flags.ts(1 hunks)
💤 Files with no reviewable changes (5)
- apps/deploy-web/src/config/browser-env.config.ts
- apps/deploy-web/env/.env.sample
- apps/deploy-web/env/.env.staging
- apps/deploy-web/src/config/env-config.schema.ts
- apps/deploy-web/env/.env.production
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/deploy-web/src/types/feature-flags.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
Never use type any or cast to type any. Always define the proper TypeScript types.
Files:
apps/deploy-web/src/components/layout/TopBanner.tsxapps/deploy-web/src/components/layout/Layout.tsxapps/deploy-web/src/hooks/useVariant.tsx
**/*.{js,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code
Files:
apps/deploy-web/src/components/layout/TopBanner.tsxapps/deploy-web/src/components/layout/Layout.tsxapps/deploy-web/src/hooks/useVariant.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: stalniy
PR: akash-network/console#1480
File: apps/deploy-web/src/hooks/useFlag.tsx:0-0
Timestamp: 2025-07-28T10:40:13.595Z
Learning: In the Akash Network Console project, backend-specific feature flags are intentional and acceptable. The frontend FeatureFlag union type should only include flags that are actually used by the frontend, not all flags defined in the backend. Backend can have internal feature flags for backend-only functionality without requiring frontend synchronization.
🧬 Code graph analysis (3)
apps/deploy-web/src/components/layout/TopBanner.tsx (1)
apps/deploy-web/src/hooks/useVariant.tsx (1)
useVariant(12-12)
apps/deploy-web/src/components/layout/Layout.tsx (1)
apps/deploy-web/src/hooks/useVariant.tsx (1)
useVariant(12-12)
apps/deploy-web/src/hooks/useVariant.tsx (2)
apps/deploy-web/src/config/browser-env.config.ts (1)
browserEnvConfig(4-41)apps/deploy-web/src/types/feature-flags.ts (1)
FeatureFlag(1-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: validate / validate-app
- GitHub Check: test-build
|
Awesome! |
closes #1949
Summary by CodeRabbit
New Features
Chores