Add admin settings page with seed data and limitations toggle

- Add settings handler with endpoints for:
  - GET/PUT /api/admin/settings (enable_limitations toggle)
  - POST /api/admin/settings/seed-lookups (run 001_lookups.sql)
  - POST /api/admin/settings/seed-test-data (run 002_test_data.sql)

- Add settings page to admin panel with:
  - Toggle switch to enable/disable subscription limitations
  - Button to seed lookup data (categories, priorities, etc.)
  - Button to seed test data (with warning for non-production use)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-28 08:47:49 -06:00
parent 218e79220c
commit f02c1e6a64
4 changed files with 396 additions and 0 deletions
+226
View File
@@ -0,0 +1,226 @@
'use client';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Database, TestTube, Shield } from 'lucide-react';
import { settingsApi } from '@/lib/api';
import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { toast } from 'sonner';
export default function SettingsPage() {
const queryClient = useQueryClient();
const { data: settings, isLoading } = useQuery({
queryKey: ['settings'],
queryFn: settingsApi.get,
});
const updateMutation = useMutation({
mutationFn: settingsApi.update,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['settings'] });
toast.success('Settings updated');
},
onError: () => {
toast.error('Failed to update settings');
},
});
const seedLookupsMutation = useMutation({
mutationFn: settingsApi.seedLookups,
onSuccess: (data) => {
toast.success(data.message);
},
onError: () => {
toast.error('Failed to seed lookup data');
},
});
const seedTestDataMutation = useMutation({
mutationFn: settingsApi.seedTestData,
onSuccess: (data) => {
toast.success(data.message);
},
onError: () => {
toast.error('Failed to seed test data');
},
});
const handleLimitationsToggle = () => {
if (settings) {
updateMutation.mutate({
enable_limitations: !settings.enable_limitations,
});
}
};
if (isLoading) {
return (
<div className="p-6">
<div className="animate-pulse space-y-4">
<div className="h-8 bg-gray-200 rounded w-1/4"></div>
<div className="h-32 bg-gray-200 rounded"></div>
<div className="h-32 bg-gray-200 rounded"></div>
</div>
</div>
);
}
return (
<div className="p-6 space-y-6">
<div>
<h1 className="text-2xl font-bold">Settings</h1>
<p className="text-muted-foreground">
Manage system settings and seed data
</p>
</div>
<div className="grid gap-6 md:grid-cols-2">
{/* Subscription Limitations */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Shield className="h-5 w-5" />
Subscription Limitations
</CardTitle>
<CardDescription>
Control whether tier-based limitations are enforced for users
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label htmlFor="enable-limitations">Enable Limitations</Label>
<p className="text-sm text-muted-foreground">
When enabled, free tier users will have restricted access to features
</p>
</div>
<Switch
id="enable-limitations"
checked={settings?.enable_limitations ?? false}
onCheckedChange={handleLimitationsToggle}
disabled={updateMutation.isPending}
/>
</div>
</CardContent>
</Card>
{/* Seed Lookup Data */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Database className="h-5 w-5" />
Seed Lookup Data
</CardTitle>
<CardDescription>
Populate or refresh static lookup tables (categories, priorities, statuses, etc.)
</CardDescription>
</CardHeader>
<CardContent>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="outline"
disabled={seedLookupsMutation.isPending}
>
{seedLookupsMutation.isPending ? 'Seeding...' : 'Seed Lookup Data'}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Seed Lookup Data?</AlertDialogTitle>
<AlertDialogDescription>
This will insert or update all lookup tables including:
<ul className="list-disc list-inside mt-2 space-y-1">
<li>Residence types</li>
<li>Task categories, priorities, statuses, frequencies</li>
<li>Contractor specialties</li>
<li>Subscription tiers and feature benefits</li>
</ul>
Existing data will be preserved or updated.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={() => seedLookupsMutation.mutate()}>
Seed Data
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</CardContent>
</Card>
{/* Seed Test Data */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<TestTube className="h-5 w-5" />
Seed Test Data
</CardTitle>
<CardDescription>
Populate the database with sample users, residences, and tasks for testing
</CardDescription>
</CardHeader>
<CardContent>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="outline"
disabled={seedTestDataMutation.isPending}
>
{seedTestDataMutation.isPending ? 'Seeding...' : 'Seed Test Data'}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Seed Test Data?</AlertDialogTitle>
<AlertDialogDescription>
This will create sample data for testing including:
<ul className="list-disc list-inside mt-2 space-y-1">
<li>Test users</li>
<li>Sample residences</li>
<li>Example tasks and completions</li>
</ul>
<strong className="text-destructive block mt-2">
Warning: This should only be used in development/testing environments.
</strong>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={() => seedTestDataMutation.mutate()}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
Seed Test Data
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</CardContent>
</Card>
</div>
</div>
);
}
+32
View File
@@ -632,4 +632,36 @@ export const notificationPrefsApi = {
},
};
// Settings types
export interface SystemSettings {
enable_limitations: boolean;
}
export interface UpdateSettingsRequest {
enable_limitations?: boolean;
}
// Settings API
export const settingsApi = {
get: async (): Promise<SystemSettings> => {
const response = await api.get<SystemSettings>('/settings');
return response.data;
},
update: async (data: UpdateSettingsRequest): Promise<SystemSettings> => {
const response = await api.put<SystemSettings>('/settings', data);
return response.data;
},
seedLookups: async (): Promise<{ message: string }> => {
const response = await api.post<{ message: string }>('/settings/seed-lookups');
return response.data;
},
seedTestData: async (): Promise<{ message: string }> => {
const response = await api.post<{ message: string }>('/settings/seed-test-data');
return response.data;
},
};
export default api;