Refactor user settings implementation

This commit is contained in:
gpt-engineer-app[bot]
2025-09-28 19:54:33 +00:00
parent 7fc30413ad
commit 01837bc999
12 changed files with 2456 additions and 0 deletions

View File

@@ -0,0 +1,356 @@
import { useState, useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Separator } from '@/components/ui/separator';
import { useToast } from '@/hooks/use-toast';
import { useAuth } from '@/hooks/useAuth';
import { supabase } from '@/integrations/supabase/client';
import { Eye, UserX, Shield, Search } from 'lucide-react';
interface PrivacySettings {
activity_visibility: 'public' | 'private';
search_visibility: boolean;
show_location: boolean;
show_age: boolean;
}
interface ProfilePrivacy {
privacy_level: 'public' | 'private';
show_pronouns: boolean;
}
export function PrivacyTab() {
const { user, profile, refreshProfile } = useAuth();
const { toast } = useToast();
const [loading, setLoading] = useState(false);
const [preferences, setPreferences] = useState<PrivacySettings | null>(null);
const form = useForm<ProfilePrivacy & PrivacySettings>({
defaultValues: {
privacy_level: profile?.privacy_level || 'public',
show_pronouns: profile?.show_pronouns || false,
activity_visibility: 'public',
search_visibility: true,
show_location: false,
show_age: false
}
});
useEffect(() => {
fetchPreferences();
}, [user]);
const fetchPreferences = async () => {
if (!user) return;
try {
const { data, error } = await supabase
.from('user_preferences')
.select('privacy_settings')
.eq('user_id', user.id)
.maybeSingle();
if (error && error.code !== 'PGRST116') {
console.error('Error fetching preferences:', error);
return;
}
if (data?.privacy_settings) {
const privacySettings = data.privacy_settings as any;
setPreferences(privacySettings);
form.reset({
privacy_level: profile?.privacy_level === 'friends' ? 'public' : (profile?.privacy_level || 'public'),
show_pronouns: profile?.show_pronouns || false,
...privacySettings
});
} else {
// Initialize preferences if they don't exist
await initializePreferences();
}
} catch (error) {
console.error('Error fetching preferences:', error);
}
};
const initializePreferences = async () => {
if (!user) return;
const defaultSettings: PrivacySettings = {
activity_visibility: 'public',
search_visibility: true,
show_location: false,
show_age: false
};
try {
const { error } = await supabase
.from('user_preferences')
.insert([{
user_id: user.id,
privacy_settings: defaultSettings as any
}]);
if (error) throw error;
setPreferences(defaultSettings);
form.reset({
privacy_level: profile?.privacy_level || 'public',
show_pronouns: profile?.show_pronouns || false,
...defaultSettings
});
} catch (error) {
console.error('Error initializing preferences:', error);
}
};
const onSubmit = async (data: any) => {
if (!user) return;
setLoading(true);
try {
// Update profile privacy settings
const { error: profileError } = await supabase
.from('profiles')
.update({
privacy_level: data.privacy_level,
show_pronouns: data.show_pronouns,
updated_at: new Date().toISOString()
})
.eq('user_id', user.id);
if (profileError) throw profileError;
// Update user preferences
const privacySettings: PrivacySettings = {
activity_visibility: data.activity_visibility,
search_visibility: data.search_visibility,
show_location: data.show_location,
show_age: data.show_age
};
const { error: prefsError } = await supabase
.from('user_preferences')
.upsert([{
user_id: user.id,
privacy_settings: privacySettings as any,
updated_at: new Date().toISOString()
}]);
if (prefsError) throw prefsError;
await refreshProfile();
setPreferences(privacySettings);
toast({
title: 'Privacy settings updated',
description: 'Your privacy preferences have been successfully saved.'
});
} catch (error: any) {
toast({
title: 'Error',
description: error.message || 'Failed to update privacy settings',
variant: 'destructive'
});
} finally {
setLoading(false);
}
};
return (
<div className="space-y-8">
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
{/* Profile Visibility */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Eye className="w-5 h-5" />
<h3 className="text-lg font-medium">Profile Visibility</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Control who can see your profile and personal information.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-3">
<Label htmlFor="privacy_level">Profile Privacy</Label>
<Select
value={form.watch('privacy_level')}
onValueChange={(value: 'public' | 'private') =>
form.setValue('privacy_level', value)
}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="public">
Public - Anyone can view your profile
</SelectItem>
<SelectItem value="private">
Private - Only you can view your profile
</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Show Pronouns</Label>
<p className="text-sm text-muted-foreground">
Display your preferred pronouns on your profile
</p>
</div>
<Switch
checked={form.watch('show_pronouns')}
onCheckedChange={(checked) => form.setValue('show_pronouns', checked)}
/>
</div>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Show Location</Label>
<p className="text-sm text-muted-foreground">
Display your location on your profile
</p>
</div>
<Switch
checked={form.watch('show_location')}
onCheckedChange={(checked) => form.setValue('show_location', checked)}
/>
</div>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Show Age</Label>
<p className="text-sm text-muted-foreground">
Display your age calculated from date of birth
</p>
</div>
<Switch
checked={form.watch('show_age')}
onCheckedChange={(checked) => form.setValue('show_age', checked)}
/>
</div>
</CardContent>
</Card>
</div>
<Separator />
{/* Activity & Content */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Shield className="w-5 h-5" />
<h3 className="text-lg font-medium">Activity & Content</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Control the visibility of your activities and content.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-3">
<Label htmlFor="activity_visibility">Activity Visibility</Label>
<Select
value={form.watch('activity_visibility')}
onValueChange={(value: 'public' | 'private') =>
form.setValue('activity_visibility', value)
}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="public">
Public - Anyone can see your reviews and lists
</SelectItem>
<SelectItem value="private">
Private - Only you can see your activities
</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
This affects the visibility of your reviews, ride credits, and top lists.
</p>
</div>
</CardContent>
</Card>
</div>
<Separator />
{/* Search & Discovery */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Search className="w-5 h-5" />
<h3 className="text-lg font-medium">Search & Discovery</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Control how others can find and discover your profile.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Search Visibility</Label>
<p className="text-sm text-muted-foreground">
Allow your profile to appear in search results
</p>
</div>
<Switch
checked={form.watch('search_visibility')}
onCheckedChange={(checked) => form.setValue('search_visibility', checked)}
/>
</div>
</CardContent>
</Card>
</div>
<Separator />
{/* Blocked Users */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<UserX className="w-5 h-5" />
<h3 className="text-lg font-medium">Blocked Users</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Manage users you have blocked from interacting with you.
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-center p-4 text-muted-foreground">
<UserX className="w-8 h-8 mx-auto mb-2" />
<p className="text-sm">No blocked users</p>
<p className="text-xs mt-1">
Blocked users will appear here and can be unblocked at any time.
</p>
</div>
</CardContent>
</Card>
</div>
{/* Save Button */}
<div className="flex justify-end">
<Button type="submit" disabled={loading}>
{loading ? 'Saving...' : 'Save Privacy Settings'}
</Button>
</div>
</form>
</div>
);
}