feat(web): TODO-001 kill switch maintenance banner + disable timer creation
This commit is contained in:
parent
8ca9e27532
commit
8bddbec43c
@ -1,34 +1,32 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { createContext, useContext, useEffect, useState } from 'react';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import { AuthProvider } from '@/lib/auth-context';
|
import { AuthProvider } from '@/lib/auth-context';
|
||||||
import { initTelemetry, trackPageView } from '@/lib/telemetry';
|
import { initTelemetry, trackPageView } from '@/lib/telemetry';
|
||||||
import { initFeatureFlags } from '@/lib/feature-flags';
|
import { initFeatureFlags } from '@/lib/feature-flags';
|
||||||
import { initDiagnostics } from '@/lib/diagnostics';
|
import { initDiagnostics } from '@/lib/diagnostics';
|
||||||
import { checkKillSwitch } from '@/lib/kill-switch';
|
import { checkKillSwitch } from '@/lib/kill-switch';
|
||||||
|
import { MaintenanceBanner } from '@/components/MaintenanceBanner';
|
||||||
import { ToastProvider } from '@bytelyst/ui';
|
import { ToastProvider } from '@bytelyst/ui';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
const MaintenanceContext = createContext(false);
|
||||||
|
|
||||||
|
export function useMaintenanceMode(): boolean {
|
||||||
|
return useContext(MaintenanceContext);
|
||||||
|
}
|
||||||
|
|
||||||
export function Providers({ children }: { children: ReactNode }) {
|
export function Providers({ children }: { children: ReactNode }) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
const [isMaintenanceMode, setIsMaintenanceMode] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initTelemetry();
|
initTelemetry();
|
||||||
initFeatureFlags();
|
initFeatureFlags();
|
||||||
initDiagnostics();
|
initDiagnostics();
|
||||||
// TODO-001: Kill switch maintenance banner
|
|
||||||
// Priority: medium | Phase: 0
|
|
||||||
// When checkKillSwitch() returns disabled=true:
|
|
||||||
// 1. Set a React state flag (e.g. `isMaintenanceMode`)
|
|
||||||
// 2. Render a <MaintenanceBanner /> component at the top of the app
|
|
||||||
// 3. Disable timer creation buttons (pass flag via context or prop)
|
|
||||||
// 4. Use --cm-warning-* design tokens for the banner styling
|
|
||||||
// File to create: web/src/components/MaintenanceBanner.tsx
|
|
||||||
checkKillSwitch().then((disabled) => {
|
checkKillSwitch().then((disabled) => {
|
||||||
if (disabled) {
|
setIsMaintenanceMode(disabled);
|
||||||
// TODO-001: Surface this in the UI (see instructions above)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -40,7 +38,12 @@ export function Providers({ children }: { children: ReactNode }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<ToastProvider>{children}</ToastProvider>
|
<MaintenanceContext.Provider value={isMaintenanceMode}>
|
||||||
|
<ToastProvider>
|
||||||
|
{isMaintenanceMode && <MaintenanceBanner />}
|
||||||
|
{children}
|
||||||
|
</ToastProvider>
|
||||||
|
</MaintenanceContext.Provider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import type { UrgencyLevel } from '@/lib/urgency';
|
|||||||
import { CASCADE_PRESET_LABELS } from '@/lib/cascade';
|
import { CASCADE_PRESET_LABELS } from '@/lib/cascade';
|
||||||
import type { CascadePreset } from '@/lib/cascade';
|
import type { CascadePreset } from '@/lib/cascade';
|
||||||
import { X, AlarmClock, Timer, Coffee, Sparkles, CalendarDays } from 'lucide-react';
|
import { X, AlarmClock, Timer, Coffee, Sparkles, CalendarDays } from 'lucide-react';
|
||||||
|
import { useMaintenanceMode } from '@/app/providers';
|
||||||
import { BUILT_IN_CATEGORIES, getCategoryById } from '@/lib/categories';
|
import { BUILT_IN_CATEGORIES, getCategoryById } from '@/lib/categories';
|
||||||
import { parseNaturalLanguage } from '@/lib/nl-parser';
|
import { parseNaturalLanguage } from '@/lib/nl-parser';
|
||||||
import type { ParseResult } from '@/lib/nl-parser';
|
import type { ParseResult } from '@/lib/nl-parser';
|
||||||
@ -21,6 +22,7 @@ interface CreateTimerModalProps {
|
|||||||
|
|
||||||
export function CreateTimerModal({ isOpen, onClose }: CreateTimerModalProps) {
|
export function CreateTimerModal({ isOpen, onClose }: CreateTimerModalProps) {
|
||||||
const { addAlarm, addCountdown, addPomodoro, addEvent } = useTimerStore();
|
const { addAlarm, addCountdown, addPomodoro, addEvent } = useTimerStore();
|
||||||
|
const maintenanceMode = useMaintenanceMode();
|
||||||
|
|
||||||
const [tab, setTab] = useState<TabType>('countdown');
|
const [tab, setTab] = useState<TabType>('countdown');
|
||||||
const [nlInput, setNlInput] = useState('');
|
const [nlInput, setNlInput] = useState('');
|
||||||
@ -570,13 +572,14 @@ export function CreateTimerModal({ isOpen, onClose }: CreateTimerModalProps) {
|
|||||||
{/* Create button */}
|
{/* Create button */}
|
||||||
<button
|
<button
|
||||||
onClick={handleCreate}
|
onClick={handleCreate}
|
||||||
className="w-full py-3 rounded-xl text-sm font-semibold transition-colors cursor-pointer"
|
disabled={maintenanceMode}
|
||||||
|
className="w-full py-3 rounded-xl text-sm font-semibold transition-colors cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: 'var(--cm-accent)',
|
backgroundColor: 'var(--cm-accent)',
|
||||||
color: 'var(--cm-white)',
|
color: 'var(--cm-white)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Create {tab === 'pomodoro' ? 'Pomodoro' : tab === 'alarm' ? 'Alarm' : 'Countdown'}
|
{maintenanceMode ? 'Timer creation disabled (maintenance)' : `Create ${tab === 'pomodoro' ? 'Pomodoro' : tab === 'alarm' ? 'Alarm' : 'Countdown'}`}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
25
web/src/components/MaintenanceBanner.tsx
Normal file
25
web/src/components/MaintenanceBanner.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { AlertTriangle } from 'lucide-react';
|
||||||
|
|
||||||
|
interface MaintenanceBannerProps {
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MaintenanceBanner({ message }: MaintenanceBannerProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="alert"
|
||||||
|
aria-label="Maintenance mode active"
|
||||||
|
className="w-full px-4 py-3 flex items-center justify-center gap-2 text-sm font-medium"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--cm-important-15)',
|
||||||
|
color: 'var(--cm-important)',
|
||||||
|
borderBottom: '1px solid var(--cm-important)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AlertTriangle size={16} aria-hidden="true" />
|
||||||
|
<span>{message || 'ChronoMind is currently in maintenance mode. Timer creation is temporarily disabled.'}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user