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,355 @@
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 { 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 { Bell, Mail, Smartphone, Volume2 } from 'lucide-react';
interface EmailNotifications {
review_replies: boolean;
new_followers: boolean;
system_announcements: boolean;
weekly_digest: boolean;
monthly_digest: boolean;
}
interface PushNotifications {
browser_enabled: boolean;
new_content: boolean;
social_updates: boolean;
}
export function NotificationsTab() {
const { user } = useAuth();
const { toast } = useToast();
const [loading, setLoading] = useState(false);
const [emailNotifications, setEmailNotifications] = useState<EmailNotifications>({
review_replies: true,
new_followers: true,
system_announcements: true,
weekly_digest: false,
monthly_digest: true
});
const [pushNotifications, setPushNotifications] = useState<PushNotifications>({
browser_enabled: false,
new_content: true,
social_updates: true
});
useEffect(() => {
fetchNotificationPreferences();
}, [user]);
const fetchNotificationPreferences = async () => {
if (!user) return;
try {
const { data, error } = await supabase
.from('user_preferences')
.select('email_notifications, push_notifications')
.eq('user_id', user.id)
.maybeSingle();
if (error && error.code !== 'PGRST116') {
console.error('Error fetching notification preferences:', error);
return;
}
if (data) {
if (data.email_notifications) {
setEmailNotifications(data.email_notifications as EmailNotifications);
}
if (data.push_notifications) {
setPushNotifications(data.push_notifications as PushNotifications);
}
} else {
// Initialize preferences if they don't exist
await initializePreferences();
}
} catch (error) {
console.error('Error fetching notification preferences:', error);
}
};
const initializePreferences = async () => {
if (!user) return;
try {
const { error } = await supabase
.from('user_preferences')
.insert([{
user_id: user.id,
email_notifications: emailNotifications,
push_notifications: pushNotifications
}]);
if (error) throw error;
} catch (error) {
console.error('Error initializing preferences:', error);
}
};
const updateEmailNotification = (key: keyof EmailNotifications, value: boolean) => {
setEmailNotifications(prev => ({ ...prev, [key]: value }));
};
const updatePushNotification = (key: keyof PushNotifications, value: boolean) => {
setPushNotifications(prev => ({ ...prev, [key]: value }));
};
const saveNotificationPreferences = async () => {
if (!user) return;
setLoading(true);
try {
const { error } = await supabase
.from('user_preferences')
.upsert([{
user_id: user.id,
email_notifications: emailNotifications as any,
push_notifications: pushNotifications as any,
updated_at: new Date().toISOString()
}]);
if (error) throw error;
toast({
title: 'Preferences saved',
description: 'Your notification preferences have been updated.'
});
} catch (error: any) {
toast({
title: 'Error',
description: error.message || 'Failed to save notification preferences',
variant: 'destructive'
});
} finally {
setLoading(false);
}
};
const requestPushPermission = async () => {
if ('Notification' in window) {
const permission = await Notification.requestPermission();
if (permission === 'granted') {
updatePushNotification('browser_enabled', true);
toast({
title: 'Push notifications enabled',
description: 'You will now receive browser push notifications.'
});
} else {
toast({
title: 'Permission denied',
description: 'Push notifications require permission to work.',
variant: 'destructive'
});
}
}
};
return (
<div className="space-y-8">
{/* Email Notifications */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Mail className="w-5 h-5" />
<h3 className="text-lg font-medium">Email Notifications</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Choose which email notifications you'd like to receive.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Review Replies</Label>
<p className="text-sm text-muted-foreground">
Get notified when someone replies to your reviews
</p>
</div>
<Switch
checked={emailNotifications.review_replies}
onCheckedChange={(checked) => updateEmailNotification('review_replies', checked)}
/>
</div>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>New Followers</Label>
<p className="text-sm text-muted-foreground">
Get notified when someone follows you
</p>
</div>
<Switch
checked={emailNotifications.new_followers}
onCheckedChange={(checked) => updateEmailNotification('new_followers', checked)}
/>
</div>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>System Announcements</Label>
<p className="text-sm text-muted-foreground">
Important updates and announcements from ThrillWiki
</p>
</div>
<Switch
checked={emailNotifications.system_announcements}
onCheckedChange={(checked) => updateEmailNotification('system_announcements', checked)}
/>
</div>
<Separator />
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Weekly Digest</Label>
<p className="text-sm text-muted-foreground">
Weekly summary of new parks, rides, and community activity
</p>
</div>
<Switch
checked={emailNotifications.weekly_digest}
onCheckedChange={(checked) => updateEmailNotification('weekly_digest', checked)}
/>
</div>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Monthly Digest</Label>
<p className="text-sm text-muted-foreground">
Monthly roundup of popular content and your activity stats
</p>
</div>
<Switch
checked={emailNotifications.monthly_digest}
onCheckedChange={(checked) => updateEmailNotification('monthly_digest', checked)}
/>
</div>
</CardContent>
</Card>
</div>
<Separator />
{/* Push Notifications */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Smartphone className="w-5 h-5" />
<h3 className="text-lg font-medium">Push Notifications</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Receive instant notifications in your browser when important events happen.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Browser Notifications</Label>
<p className="text-sm text-muted-foreground">
Enable push notifications in your browser
</p>
</div>
<div className="flex items-center gap-2">
{!pushNotifications.browser_enabled && (
<Button
variant="outline"
size="sm"
onClick={requestPushPermission}
>
Enable
</Button>
)}
<Switch
checked={pushNotifications.browser_enabled}
onCheckedChange={(checked) => {
if (!checked) {
updatePushNotification('browser_enabled', false);
} else {
requestPushPermission();
}
}}
/>
</div>
</div>
{pushNotifications.browser_enabled && (
<>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>New Content</Label>
<p className="text-sm text-muted-foreground">
Notifications about new parks, rides, and reviews
</p>
</div>
<Switch
checked={pushNotifications.new_content}
onCheckedChange={(checked) => updatePushNotification('new_content', checked)}
/>
</div>
<div className="flex items-center justify-between">
<div className="space-y-1">
<Label>Social Updates</Label>
<p className="text-sm text-muted-foreground">
Notifications about followers, replies, and mentions
</p>
</div>
<Switch
checked={pushNotifications.social_updates}
onCheckedChange={(checked) => updatePushNotification('social_updates', checked)}
/>
</div>
</>
)}
</CardContent>
</Card>
</div>
<Separator />
{/* Sound Settings */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Volume2 className="w-5 h-5" />
<h3 className="text-lg font-medium">Sound Settings</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Configure sound preferences for notifications and interactions.
</CardDescription>
</CardHeader>
<CardContent>
<div className="text-center p-4 text-muted-foreground">
<Volume2 className="w-8 h-8 mx-auto mb-2" />
<p className="text-sm">Sound settings coming soon</p>
<p className="text-xs mt-1">
Configure notification sounds and interaction feedback.
</p>
</div>
</CardContent>
</Card>
</div>
{/* Save Button */}
<div className="flex justify-end">
<Button onClick={saveNotificationPreferences} disabled={loading}>
{loading ? 'Saving...' : 'Save Notification Preferences'}
</Button>
</div>
</div>
);
}