import { useState, useEffect } from 'react'; import { useAppConfig, AppConfig } from '@/hooks/useAppConfig'; import { useAuth, PERMISSION_KEYS } from '@/contexts/AuthContext'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Skeleton } from '@/components/ui/skeleton'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Save, AlertTriangle, RefreshCw, Users } from 'lucide-react'; import { toast } from 'sonner'; import { format } from 'date-fns'; export function AppSettingsPage() { const { hasPermission, isSuperAdmin } = useAuth(); const { config, loading, error, updateConfig, refetch } = useAppConfig(); // Local form state const [formData, setFormData] = useState>({}); const [isSaving, setIsSaving] = useState(false); const [hasChanges, setHasChanges] = useState(false); // Permission check - only super admins or those with MANAGE_APP_CONFIG can access const canManage = isSuperAdmin || hasPermission(PERMISSION_KEYS.MANAGE_APP_CONFIG); // Initialize form data when config loads useEffect(() => { if (config) { setFormData({ answer_gap_threshold: config.answer_gap_threshold ?? 10, daily_response_limit: config.daily_response_limit ?? 0, }); setHasChanges(false); } }, [config]); const handleChange = (field: K, value: AppConfig[K]) => { setFormData(prev => ({ ...prev, [field]: value })); setHasChanges(true); }; const handleSave = async () => { setIsSaving(true); try { const { error: saveError } = await updateConfig(formData); if (saveError) { toast.error(saveError); } else { toast.success('App settings saved successfully'); setHasChanges(false); } } catch { toast.error('Failed to save settings'); } finally { setIsSaving(false); } }; const handleReset = () => { if (config) { setFormData({ answer_gap_threshold: config.answer_gap_threshold ?? 10, daily_response_limit: config.daily_response_limit ?? 0, }); setHasChanges(false); } }; if (!canManage) { return (
Access Denied You don't have permission to manage app settings. Contact a super admin for access.
); } if (loading) { return (

App Settings

Configure mobile app behavior settings

); } if (error) { return (

App Settings

Configure mobile app behavior settings

Error loading settings {error}
); } return (
{/* Header */}

App Settings

Configure mobile app behavior settings

{hasChanges && ( )}
{config?.updated_at && (

Last updated: {format(new Date(config.updated_at), 'MMM d, yyyy HH:mm')}

)} {/* Answer Spam Prevention */} Answer Spam Prevention Prevent one partner from getting too far ahead in answering questions
handleChange('answer_gap_threshold', parseInt(e.target.value) || 0)} className="max-w-[200px]" />

Maximum number of questions a user can answer ahead of their partner. When exceeded, they'll see a "Waiting for your partner" message.

Set to 0 to disable this feature.

{/* Daily Response Limit */} Daily Response Limit (Free Users) Limit the number of questions non-premium users can answer per day, on their own local calendar day
handleChange('daily_response_limit', parseInt(e.target.value) || 0)} className="max-w-[200px]" />

Maximum number of questions a non-premium user can answer per day. Premium users (via personal or partner subscription) bypass this limit.

Set to 0 to disable daily limits entirely.

Resets at midnight in the user's own timezone, as reported by their device. Users who have not reported a timezone yet fall back to 00:00 UTC.

Does not count toward the limit: editing an existing answer, or answering a question the partner had already answered (catching up is always free).

); }