Files
Sauci/apps/admin/src/hooks/useAppConfig.ts
T
jjbruntonandClaude Opus 4.5 b0f1700e40 Add match archiving, chat enhancements, and daily limits
- Match archives with swipe-to-archive and unread filtering
- Chat settings sheet and full-screen media modals
- Match notification modal for new matches
- Admin AI settings with model/provider configuration
- Daily response limit feature with recommended questions
- New test coverage for stores, hooks, and components
- Claude Code agent and command configurations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 20:18:34 +00:00

113 lines
3.2 KiB
TypeScript

import { useState, useEffect, useCallback } from 'react';
import { supabase } from '@/config';
export interface AppConfig {
id: string;
answer_gap_threshold: number;
daily_response_limit: number;
updated_at: string | null;
updated_by: string | null;
}
interface UseAppConfigReturn {
config: AppConfig | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
updateConfig: (updates: Partial<AppConfig>) => Promise<{ error: string | null }>;
}
// Cache for the config to avoid refetching on every use
let cachedConfig: AppConfig | null = null;
let cacheTimestamp: number = 0;
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
export function useAppConfig(): UseAppConfigReturn {
const [config, setConfig] = useState<AppConfig | null>(cachedConfig);
const [loading, setLoading] = useState(!cachedConfig);
const [error, setError] = useState<string | null>(null);
const fetchConfig = useCallback(async () => {
// Use cache if valid
if (cachedConfig && Date.now() - cacheTimestamp < CACHE_TTL) {
setConfig(cachedConfig);
setLoading(false);
return;
}
setLoading(true);
setError(null);
try {
const { data, error: fetchError } = await supabase
.from('app_config')
.select('*')
.limit(1)
.maybeSingle();
if (fetchError) {
setError(fetchError.message);
setConfig(null);
} else if (data) {
cachedConfig = data as AppConfig;
cacheTimestamp = Date.now();
setConfig(data as AppConfig);
} else {
// No config row exists yet
setConfig(null);
setError('App config not initialized. Please contact support.');
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to fetch app config');
} finally {
setLoading(false);
}
}, []);
const updateConfig = useCallback(async (updates: Partial<AppConfig>): Promise<{ error: string | null }> => {
if (!config?.id) {
return { error: 'No config to update' };
}
try {
const { error: updateError } = await supabase
.from('app_config')
.update(updates)
.eq('id', config.id);
if (updateError) {
return { error: updateError.message };
}
// Invalidate cache and refetch
cachedConfig = null;
cacheTimestamp = 0;
await fetchConfig();
return { error: null };
} catch (err) {
return { error: err instanceof Error ? err.message : 'Failed to update app config' };
}
}, [config?.id, fetchConfig]);
useEffect(() => {
fetchConfig();
}, [fetchConfig]);
return {
config,
loading,
error,
refetch: fetchConfig,
updateConfig,
};
}
/**
* Clear the config cache (useful when logging out)
*/
export function clearAppConfigCache(): void {
cachedConfig = null;
cacheTimestamp = 0;
}