import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery, useMutation } from '@tanstack/react-query'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Globe, Tag, Plus, Edit, Trash2, Layers, User as UserIcon, ShieldCheck, Share2, Server, Copy, Settings as SettingsIcon, Bell, Loader2, Clock } from 'lucide-react'; import { Switch } from '@/components/ui/switch'; import { ShareAccessModal } from '@/components/ShareAccessModal'; import { ChangePasswordModal } from '@/components/ChangePasswordModal'; import { UpdateProfileModal } from '@/components/UpdateProfileModal'; import { ShareLabelModal } from '@/components/ShareLabelModal'; import { Label } from '@shared/schema'; import { User } from '@shared/schema'; import { queryClient, apiRequest } from '@/lib/queryClient'; import { useToast } from '@/hooks/use-toast'; import { useLocation } from "wouter"; import { useNotifications } from '@/hooks/use-notifications'; import { DataExportCard } from '@/components/user/DataExportCard'; const NotificationSettings = () => { const { t } = useTranslation(); const { toast } = useToast(); const { enabled, toggleEnabled, permission, requestPermission, pushSupported, isStandalone, sendTestNotification, isLoading } = useNotifications({ poll: false }); const handleToggle = async (checked: boolean) => { if (checked && permission !== 'granted') { const success = await requestPermission(); if (success) { toast({ title: t('notifications.enabledTitle'), description: t('notifications.enabledBody'), }); } } else { await toggleEnabled(checked); } }; const handleTestNotification = async () => { const success = await sendTestNotification(); if (success) { toast({ title: t('notifications.testSent', 'Test Sent'), description: t('notifications.testSentDesc', 'Check for a notification on your device.'), }); } else { toast({ title: t('common.error'), description: t('notifications.testFailed', 'Failed to send test notification.'), variant: 'destructive', }); } }; return (
{/* Push Support Status */} {pushSupported && (
{t('notifications.pushSupported', 'Push Supported')} {isStandalone && ( {t('notifications.installedApp', 'Installed App')} )}
)} {/* iOS PWA Hint */} {!pushSupported && /iPhone|iPad|iPod/.test(navigator.userAgent) && !isStandalone && (

{t('notifications.iosHintTitle', 'Add to Home Screen')}
{t('notifications.iosHintDesc', 'To receive push notifications on iOS, tap the Share button and select "Add to Home Screen", then open the app from there.')}

)} {/* Main Toggle */}

{pushSupported ? t('notifications.enablePush', 'Push Notifications') : t('notifications.enableBrowser')}

{permission === 'denied' ? ( {t('notifications.permissionDenied', 'Permission denied by browser. Please reset site permissions.')} ) : pushSupported ? ( t('notifications.pushDescription', 'Receive notifications even when the app is closed.') ) : ( t('notifications.description') )}

{/* Test Button */} {enabled && pushSupported && ( )}
); }; const ScheduleSettings = ({ user }: { user: User }) => { const { t } = useTranslation(); const { toast } = useToast(); const [activeTab, setActiveTab] = useState<'work' | 'personal'>('work'); // Helper to safely get availability data const getAvailability = (type: 'work' | 'personal') => { // Cast to any because TS might not know about the JSON structure fully yet if types aren't perfectly synced in IDE const avail = user.availability as any; if (avail && avail[type]) { return avail[type]; } // Fallback defaults if (type === 'work') return user.workHours || { start: "09:00", end: "17:00", days: [1, 2, 3, 4, 5] }; return { start: "18:00", end: "22:00", days: [1, 2, 3, 4, 5, 0, 6] }; }; // State for both schedules const [workSchedule, setWorkSchedule] = useState(getAvailability('work')); const [personalSchedule, setPersonalSchedule] = useState(getAvailability('personal')); const currentSchedule = activeTab === 'work' ? workSchedule : personalSchedule; const setCurrentSchedule = (newSched: any) => { if (activeTab === 'work') setWorkSchedule(newSched); else setPersonalSchedule(newSched); }; const updateScheduleMutation = useMutation({ mutationFn: async () => { const payload = { availability: { work: workSchedule, personal: personalSchedule } }; const res = await apiRequest("PATCH", "/api/user/schedule", payload); return res.json(); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/user"] }); toast({ title: t('settings.schedule.saved') }); } }); const toggleDay = (day: number) => { const currentDays = currentSchedule.days || []; let newDays; if (currentDays.includes(day)) { newDays = currentDays.filter((d: number) => d !== day); } else { newDays = [...currentDays, day].sort(); } setCurrentSchedule({ ...currentSchedule, days: newDays }); }; const handleChange = (field: 'start' | 'end', value: string) => { setCurrentSchedule({ ...currentSchedule, [field]: value }); }; const handleSave = () => { updateScheduleMutation.mutate(); }; const days = [ { id: 1, label: t('analytics.mon') }, { id: 2, label: t('analytics.tue') }, { id: 3, label: t('analytics.wed') }, { id: 4, label: t('analytics.thu') }, { id: 5, label: t('analytics.fri') }, { id: 6, label: t('analytics.sat') }, { id: 0, label: t('analytics.sun') }, ]; return (
handleChange('start', e.target.value)} />
handleChange('end', e.target.value)} />
{days.map(day => ( ))}
{activeTab === 'work' ? t('settings.schedule.workDesc', "Tasks with 'Work' labels will be scheduled during these hours.") : t('settings.schedule.personalDesc', "Tasks with 'Personal' labels will be scheduled during these hours. 'Neutral' tasks can use either.")}
); }; interface SettingsProps { onNavigateToTemplates: () => void; } export default function Settings({ onNavigateToTemplates }: SettingsProps) { const { t, i18n } = useTranslation(); const { toast } = useToast(); const [, setLocation] = useLocation(); // Fetch user const { data: user, isLoading: isLoadingUser } = useQuery({ queryKey: ['/api/user'] }); if (isLoadingUser) { return (
); } if (!user) { return (

{t('common.loginRequired')}

); } const [isLabelDialogOpen, setIsLabelDialogOpen] = useState(false); const [editingLabel, setEditingLabel] = useState