// ======================================== // GlobalSettingsTab Component // ======================================== // Global settings for personal spec defaults and spec statistics import { useState, useEffect } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useIntl } from 'react-intl'; import { toast } from 'sonner'; import { Settings, RefreshCw } from 'lucide-react'; import { Card, CardHeader, CardTitle, CardDescription, CardContent, } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; import { Label } from '@/components/ui/Label'; import { Switch } from '@/components/ui/Switch'; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, } from '@/components/ui/Select'; import { cn } from '@/lib/utils'; // ========== Types ========== interface PersonalSpecDefaults { defaultReadMode: 'required' | 'optional'; autoEnable: boolean; } interface SystemSettings { injectionControl: { maxLength: number; warnThreshold: number; truncateOnExceed: boolean; }; personalSpecDefaults: PersonalSpecDefaults; } interface SpecDimensionStats { count: number; requiredCount: number; } interface SpecStats { dimensions: { specs: SpecDimensionStats; personal: SpecDimensionStats; }; injectionLength?: { requiredOnly: number; withKeywords: number; maxLength: number; percentage: number; }; } // ========== API Functions ========== const API_BASE = '/api'; async function fetchSystemSettings(): Promise { const response = await fetch(`${API_BASE}/system/settings`, { credentials: 'same-origin', }); if (!response.ok) { throw new Error(`Failed to fetch settings: ${response.statusText}`); } const data = await response.json(); return data; } async function updateSystemSettings( settings: Partial ): Promise<{ success: boolean; settings: SystemSettings }> { const response = await fetch(`${API_BASE}/system/settings`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'same-origin', body: JSON.stringify(settings), }); if (!response.ok) { throw new Error(`Failed to update settings: ${response.statusText}`); } return response.json(); } async function fetchSpecStats(): Promise { const response = await fetch(`${API_BASE}/specs/stats`, { credentials: 'same-origin', }); if (!response.ok) { throw new Error(`Failed to fetch spec stats: ${response.statusText}`); } return response.json(); } // ========== Query Keys ========== const settingsKeys = { all: ['system-settings'] as const, settings: () => [...settingsKeys.all, 'settings'] as const, stats: () => [...settingsKeys.all, 'stats'] as const, }; // ========== Component ========== export function GlobalSettingsTab() { const { formatMessage } = useIntl(); const queryClient = useQueryClient(); // Local state for immediate UI feedback const [localDefaults, setLocalDefaults] = useState({ defaultReadMode: 'optional', autoEnable: true, }); // Fetch system settings const { data: settings, isLoading: isLoadingSettings, error: settingsError, } = useQuery({ queryKey: settingsKeys.settings(), queryFn: fetchSystemSettings, staleTime: 60000, // 1 minute }); // Fetch spec stats const { data: stats, isLoading: isLoadingStats, error: statsError, refetch: refetchStats, } = useQuery({ queryKey: settingsKeys.stats(), queryFn: fetchSpecStats, staleTime: 30000, // 30 seconds }); // Update settings mutation const updateMutation = useMutation({ mutationFn: updateSystemSettings, onSuccess: (data) => { queryClient.setQueryData(settingsKeys.settings(), data.settings); toast.success(formatMessage({ id: 'specs.injection.saveSuccess', defaultMessage: 'Settings saved successfully' })); }, onError: (error) => { toast.error(formatMessage( { id: 'specs.injection.saveError', defaultMessage: 'Failed to save settings: {error}' }, { error: error.message } )); }, }); // Sync local state with server state useEffect(() => { if (settings?.personalSpecDefaults) { setLocalDefaults(settings.personalSpecDefaults); } }, [settings]); // Handlers const handleReadModeChange = (value: 'required' | 'optional') => { const newDefaults = { ...localDefaults, defaultReadMode: value }; setLocalDefaults(newDefaults); updateMutation.mutate({ personalSpecDefaults: newDefaults }); }; const handleAutoEnableChange = (checked: boolean) => { const newDefaults = { ...localDefaults, autoEnable: checked }; setLocalDefaults(newDefaults); updateMutation.mutate({ personalSpecDefaults: newDefaults }); }; // Calculate totals const dimensions = stats?.dimensions || {}; const dimensionEntries = Object.entries(dimensions) as [ keyof typeof dimensions, SpecDimensionStats ][]; const totalCount = dimensionEntries.reduce( (sum, [, data]) => sum + data.count, 0 ); const totalRequired = dimensionEntries.reduce( (sum, [, data]) => sum + data.requiredCount, 0 ); const isLoading = isLoadingSettings || isLoadingStats; const hasError = settingsError || statsError; return (
{/* Personal Spec Defaults Card */}
{formatMessage({ id: 'specs.settings.personalSpecDefaults', defaultMessage: 'Personal Spec Defaults' })}
{formatMessage({ id: 'specs.settings.personalSpecDefaultsDesc', defaultMessage: 'These settings will be applied when creating new personal specs' })}
{/* Default Read Mode */}

{formatMessage({ id: 'specs.settings.defaultReadModeHelp', defaultMessage: 'The default read mode for newly created personal specs' })}

{/* Auto Enable */}

{formatMessage({ id: 'specs.settings.autoEnableDescription', defaultMessage: 'Automatically enable newly created personal specs' })}

{/* Spec Statistics Card */}
{formatMessage({ id: 'specs.settings.specStatistics', defaultMessage: 'Spec Statistics' })}
{isLoading ? (
{[1, 2, 3, 4].map((i) => (
))}
) : hasError ? (
{formatMessage({ id: 'specs.injection.loadError', defaultMessage: 'Failed to load statistics' })}
) : ( <>
{dimensionEntries.map(([dim, data]) => (
{data.count}
{formatMessage({ id: `specs.dimension.${dim}`, defaultMessage: dim })}
{data.requiredCount} {formatMessage({ id: 'specs.required', defaultMessage: 'required' })}
))}
{/* Summary */}
{formatMessage( { id: 'specs.settings.totalSpecs', defaultMessage: 'Total: {count} spec files' }, { count: totalCount } )} {totalRequired} {formatMessage({ id: 'specs.readMode.required', defaultMessage: 'required' })} | {totalCount - totalRequired}{' '} {formatMessage({ id: 'specs.readMode.optional', defaultMessage: 'optional' })}
)}
); } export default GlobalSettingsTab;