mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 13:51:13 -05:00
Refactor user settings implementation
This commit is contained in:
355
src/components/settings/NotificationsTab.tsx
Normal file
355
src/components/settings/NotificationsTab.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user