Files
thrilltrack-explorer/src/components/settings/LocationTab.tsx
gpt-engineer-app[bot] 04d1a9e0e2 Fix select home park label
2025-09-28 20:46:21 +00:00

277 lines
10 KiB
TypeScript

import { useState, useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Card, CardContent, CardDescription, CardHeader } from '@/components/ui/card';
import { Separator } from '@/components/ui/separator';
import { Switch } from '@/components/ui/switch';
import { useToast } from '@/hooks/use-toast';
import { useAuth } from '@/hooks/useAuth';
import { supabase } from '@/integrations/supabase/client';
import { MapPin, Calendar, Globe, Accessibility } from 'lucide-react';
const locationSchema = z.object({
preferred_pronouns: z.string().max(20).optional(),
timezone: z.string(),
preferred_language: z.string(),
location_id: z.string().optional()
});
type LocationFormData = z.infer<typeof locationSchema>;
interface AccessibilityOptions {
font_size: 'small' | 'medium' | 'large';
high_contrast: boolean;
reduced_motion: boolean;
}
export function LocationTab() {
const {
user,
profile,
refreshProfile
} = useAuth();
const {
toast
} = useToast();
const [loading, setLoading] = useState(false);
const [locations, setLocations] = useState<any[]>([]);
const [accessibility, setAccessibility] = useState<AccessibilityOptions>({
font_size: 'medium',
high_contrast: false,
reduced_motion: false
});
const form = useForm<LocationFormData>({
resolver: zodResolver(locationSchema),
defaultValues: {
preferred_pronouns: profile?.preferred_pronouns || '',
timezone: profile?.timezone || 'UTC',
preferred_language: profile?.preferred_language || 'en',
location_id: profile?.location_id || ''
}
});
useEffect(() => {
fetchLocations();
fetchAccessibilityPreferences();
}, [user]);
const fetchLocations = async () => {
try {
const {
data,
error
} = await supabase.from('locations').select('id, name, city, state_province, country').order('name');
if (error) throw error;
setLocations(data || []);
} catch (error) {
console.error('Error fetching locations:', error);
}
};
const fetchAccessibilityPreferences = async () => {
if (!user) return;
try {
const {
data,
error
} = await supabase.from('user_preferences').select('accessibility_options').eq('user_id', user.id).maybeSingle();
if (error && error.code !== 'PGRST116') {
console.error('Error fetching accessibility preferences:', error);
return;
}
if (data?.accessibility_options) {
setAccessibility(data.accessibility_options as any);
}
} catch (error) {
console.error('Error fetching accessibility preferences:', error);
}
};
const onSubmit = async (data: LocationFormData) => {
if (!user) return;
setLoading(true);
try {
// Save profile information
const { error: profileError } = await supabase.from('profiles').update({
preferred_pronouns: data.preferred_pronouns || null,
timezone: data.timezone,
preferred_language: data.preferred_language,
location_id: data.location_id || null,
updated_at: new Date().toISOString()
}).eq('user_id', user.id);
if (profileError) throw profileError;
// Save accessibility preferences
const { error: accessibilityError } = await supabase.from('user_preferences').upsert([{
user_id: user.id,
accessibility_options: accessibility as any,
updated_at: new Date().toISOString()
}]);
if (accessibilityError) throw accessibilityError;
await refreshProfile();
toast({
title: 'Settings saved',
description: 'Your location, personal information, and accessibility settings have been updated.'
});
} catch (error: any) {
toast({
title: 'Error',
description: error.message || 'Failed to save settings',
variant: 'destructive'
});
} finally {
setLoading(false);
}
};
const updateAccessibility = (key: keyof AccessibilityOptions, value: any) => {
setAccessibility(prev => ({
...prev,
[key]: value
}));
};
const timezones = ['UTC', 'America/New_York', 'America/Chicago', 'America/Denver', 'America/Los_Angeles', 'America/Toronto', 'Europe/London', 'Europe/Berlin', 'Europe/Paris', 'Asia/Tokyo', 'Asia/Shanghai', 'Australia/Sydney'];
return <div className="space-y-8">
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
{/* Location Settings */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<MapPin className="w-5 h-5" />
<h3 className="text-lg font-medium">Location Settings</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Set your location for better personalized content and timezone display.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<Label htmlFor="location_id">Home Park</Label>
<Select value={form.watch('location_id')} onValueChange={value => form.setValue('location_id', value)}>
<SelectTrigger>
<SelectValue placeholder="Select your home park" />
</SelectTrigger>
<SelectContent>
{locations.map(location => <SelectItem key={location.id} value={location.id}>
{location.name}
{location.city && `, ${location.city}`}
{location.state_province && `, ${location.state_province}`}
{location.country && `, ${location.country}`}
</SelectItem>)}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="timezone">Timezone</Label>
<Select value={form.watch('timezone')} onValueChange={value => form.setValue('timezone', value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{timezones.map(tz => <SelectItem key={tz} value={tz}>
{tz}
</SelectItem>)}
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
Used to display dates and times in your local timezone.
</p>
</div>
</CardContent>
</Card>
</div>
<Separator />
{/* Personal Information */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Calendar className="w-5 h-5" />
<h3 className="text-lg font-medium">Personal Information</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Optional personal information that can be displayed on your profile.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<Label htmlFor="preferred_pronouns">Preferred Pronouns</Label>
<Input id="preferred_pronouns" {...form.register('preferred_pronouns')} placeholder="e.g., they/them, she/her, he/him" />
<p className="text-sm text-muted-foreground">
How you'd like others to refer to you.
</p>
</div>
</CardContent>
</Card>
</div>
<Separator />
{/* Accessibility Options */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Accessibility className="w-5 h-5" />
<h3 className="text-lg font-medium">Accessibility Options</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Customize the interface to meet your accessibility needs.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-3">
<Label>Font Size</Label>
<Select value={accessibility.font_size} onValueChange={(value: 'small' | 'medium' | 'large') => updateAccessibility('font_size', value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="small">Small</SelectItem>
<SelectItem value="medium">Medium (Default)</SelectItem>
<SelectItem value="large">Large</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>High Contrast</Label>
<p className="text-sm text-muted-foreground">
Increase contrast for better visibility
</p>
</div>
<Switch checked={accessibility.high_contrast} onCheckedChange={checked => updateAccessibility('high_contrast', checked)} />
</div>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Reduced Motion</Label>
<p className="text-sm text-muted-foreground">
Minimize animations and transitions
</p>
</div>
<Switch checked={accessibility.reduced_motion} onCheckedChange={checked => updateAccessibility('reduced_motion', checked)} />
</div>
</CardContent>
</Card>
</div>
<div className="flex justify-end">
<Button type="submit" disabled={loading}>
{loading ? 'Saving...' : 'Save Settings'}
</Button>
</div>
</form>
</div>;
}