diff --git a/src/locales/en.json b/src/locales/en.json index 1386172..e2bfd99 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -57,6 +57,8 @@ "pushDesc": "Receive notifications directly on this device.", "telegram": "Telegram", "telegramDesc": "Receive automated reminders via the MyTaskly Telegram bot.", + "weeklySummary": "Weekly Summary", + "weeklySummaryDesc": "Receive a weekly summary of your tasks.", "timezone": "Timezone", "timezoneDesc": "Reminders are sent according to this timezone. Keep it in sync with your device.", "info": "Information" @@ -76,6 +78,21 @@ "title": "Reminder advance notice", "desc": "How many minutes before the task deadline should the reminder be sent." }, + "weeklySummary": { + "enable": "Enable summary", + "nextScheduled": "Next: {{date}} at {{time}}", + "day": "Day of week", + "hour": "Time", + "days": { + "0": "Sun", + "1": "Mon", + "2": "Tue", + "3": "Wed", + "4": "Thu", + "5": "Fri", + "6": "Sat" + } + }, "timezone": { "server": "Server", "device": "This device", diff --git a/src/locales/it.json b/src/locales/it.json index 2e357cf..de25566 100644 --- a/src/locales/it.json +++ b/src/locales/it.json @@ -58,6 +58,8 @@ "pushDesc": "Ricevi notifiche direttamente su questo dispositivo.", "telegram": "Telegram", "telegramDesc": "Ricevi promemoria automatici tramite il bot Telegram di MyTaskly.", + "weeklySummary": "Riepilogo Settimanale", + "weeklySummaryDesc": "Ricevi un riepilogo dei tuoi task ogni settimana.", "timezone": "Fuso Orario", "timezoneDesc": "I promemoria vengono inviati in base a questo fuso orario. Mantienilo sincronizzato con il tuo dispositivo.", "info": "Informazioni" @@ -77,6 +79,21 @@ "title": "Anticipo promemoria", "desc": "Quanti minuti prima della scadenza del task deve essere inviato il promemoria." }, + "weeklySummary": { + "enable": "Abilita riepilogo", + "nextScheduled": "Prossimo: {{date}} alle {{time}}", + "day": "Giorno della settimana", + "hour": "Orario", + "days": { + "0": "Dom", + "1": "Lun", + "2": "Mar", + "3": "Mer", + "4": "Gio", + "5": "Ven", + "6": "Sab" + } + }, "timezone": { "server": "Server", "device": "Questo dispositivo", diff --git a/src/navigation/screens/NotificationSettings.tsx b/src/navigation/screens/NotificationSettings.tsx index a458ded..01a1ea1 100644 --- a/src/navigation/screens/NotificationSettings.tsx +++ b/src/navigation/screens/NotificationSettings.tsx @@ -22,14 +22,29 @@ import { NotificationSettings, TELEGRAM_REMINDER_OPTIONS, } from '../../services/notificationSettingsService'; +import { + getWeeklySummarySettings, + updateWeeklySummarySettings, + WeeklySummarySettings, +} from '../../services/weeklySummaryService'; import { registerForPushNotificationsAsync, sendTokenToBackend, } from '../../services/notificationService'; +// Costanti per orari +const HOURS = Array.from({ length: 24 }, (_, i) => ({ + value: i, + label: `${i.toString().padStart(2, '0')}:00`, +})); + +// Costanti per giorni della settimana (valori 0-6) +const WEEK_DAYS = [0, 1, 2, 3, 4, 5, 6]; + export default function NotificationSettingsScreen() { const { t } = useTranslation(); const [settings, setSettings] = useState(null); + const [weeklySummarySettings, setWeeklySummarySettings] = useState(null); const [loading, setLoading] = useState(true); const [syncing, setSyncing] = useState(false); const [syncDone, setSyncDone] = useState(false); @@ -73,8 +88,12 @@ export default function NotificationSettingsScreen() { const loadSettings = useCallback(async () => { try { setLoading(true); - const data = await getNotificationSettings(); + const [data, weeklyData] = await Promise.all([ + getNotificationSettings(), + getWeeklySummarySettings(), + ]); setSettings(data); + setWeeklySummarySettings(weeklyData); } catch (error) { console.error('[NotificationSettings] Errore caricamento:', error); Alert.alert( @@ -164,6 +183,64 @@ export default function NotificationSettingsScreen() { } }; + // Funzioni per riepilogo settimanale + const handleWeeklySummaryToggle = async (enabled: boolean) => { + if (!weeklySummarySettings) return; + + const previous = weeklySummarySettings.enabled; + setWeeklySummarySettings((prev) => prev ? { ...prev, enabled } : prev); + setSavingField('weekly_enabled'); + + try { + const updated = await updateWeeklySummarySettings({ enabled }); + setWeeklySummarySettings(updated); + } catch (error: any) { + setWeeklySummarySettings((prev) => prev ? { ...prev, enabled: previous } : prev); + const detail = error?.response?.data?.detail ?? t('notificationSettings.error.saveFailed'); + Alert.alert(t('notificationSettings.error.title'), detail); + } finally { + setSavingField(null); + } + }; + + const handleWeeklySummaryDay = async (day: number) => { + if (!weeklySummarySettings || weeklySummarySettings.day === day) return; + + const previous = weeklySummarySettings.day; + setWeeklySummarySettings((prev) => prev ? { ...prev, day } : prev); + setSavingField('weekly_day'); + + try { + const updated = await updateWeeklySummarySettings({ day }); + setWeeklySummarySettings(updated); + } catch (error: any) { + setWeeklySummarySettings((prev) => prev ? { ...prev, day: previous } : prev); + const detail = error?.response?.data?.detail ?? t('notificationSettings.error.saveFailed'); + Alert.alert(t('notificationSettings.error.title'), detail); + } finally { + setSavingField(null); + } + }; + + const handleWeeklySummaryHour = async (hour: number) => { + if (!weeklySummarySettings || weeklySummarySettings.hour === hour) return; + + const previous = weeklySummarySettings.hour; + setWeeklySummarySettings((prev) => prev ? { ...prev, hour } : prev); + setSavingField('weekly_hour'); + + try { + const updated = await updateWeeklySummarySettings({ hour }); + setWeeklySummarySettings(updated); + } catch (error: any) { + setWeeklySummarySettings((prev) => prev ? { ...prev, hour: previous } : prev); + const detail = error?.response?.data?.detail ?? t('notificationSettings.error.saveFailed'); + Alert.alert(t('notificationSettings.error.title'), detail); + } finally { + setSavingField(null); + } + }; + if (loading) { return ( @@ -346,6 +423,101 @@ export default function NotificationSettingsScreen() { + {/* ───────────────── WEEKLY SUMMARY ───────────────── */} + + {t('notificationSettings.sections.weeklySummary')} + + {t('notificationSettings.sections.weeklySummaryDesc')} + + + + {/* Toggle abilitazione */} + + + + + {t('notificationSettings.weeklySummary.enable')} + {weeklySummarySettings?.enabled && weeklySummarySettings.next_scheduled_date && ( + + {t('notificationSettings.weeklySummary.nextScheduled', { + date: new Date(weeklySummarySettings.next_scheduled_date).toLocaleDateString('it-IT', { + weekday: 'long', + day: 'numeric', + month: 'short', + }), + time: `${weeklySummarySettings.hour}:00` + })} + + )} + + + + + + {/* Selettore giorno - visibile solo se abilitato */} + {weeklySummarySettings?.enabled && ( + <> + + {t('notificationSettings.weeklySummary.day')} + + + + {WEEK_DAYS.map((day) => { + const isSelected = weeklySummarySettings.day === day; + const isSaving = savingField === 'weekly_day'; + return ( + handleWeeklySummaryDay(day)} + disabled={isSaving} + activeOpacity={0.7} + > + + {t(`notificationSettings.weeklySummary.days.${day}`)} + + + ); + })} + + + + {t('notificationSettings.weeklySummary.hour')} + + + + {HOURS.map((hour) => { + const isSelected = weeklySummarySettings.hour === hour.value; + const isSaving = savingField === 'weekly_hour'; + return ( + handleWeeklySummaryHour(hour.value)} + disabled={isSaving} + activeOpacity={0.7} + > + + {hour.label} + + + ); + })} + + + )} + {/* ───────────────── INFO ───────────────── */} {t('notificationSettings.sections.info')} @@ -492,6 +664,53 @@ const styles = StyleSheet.create({ color: '#ffffff', fontWeight: '600', }, + // Sottosezioni + subSectionHeader: { + paddingHorizontal: 20, + paddingTop: 16, + paddingBottom: 8, + backgroundColor: '#ffffff', + }, + subSectionTitle: { + fontSize: 16, + fontWeight: '600', + color: '#000000', + fontFamily: 'System', + }, + // Hours scroll + hoursScroll: { + borderBottomWidth: 1, + borderBottomColor: '#f0f0f0', + }, + hoursScrollContent: { + paddingHorizontal: 16, + paddingVertical: 16, + gap: 8, + }, + hourPill: { + paddingHorizontal: 16, + paddingVertical: 8, + borderRadius: 16, + borderWidth: 1.5, + borderColor: '#dee2e6', + backgroundColor: '#ffffff', + minWidth: 60, + alignItems: 'center', + }, + hourPillSelected: { + backgroundColor: '#000000', + borderColor: '#000000', + }, + hourPillText: { + fontSize: 14, + color: '#495057', + fontWeight: '500', + fontFamily: 'System', + }, + hourPillTextSelected: { + color: '#ffffff', + fontWeight: '600', + }, // Timezone timezoneRow: { flexDirection: 'row', diff --git a/src/services/weeklySummaryService.ts b/src/services/weeklySummaryService.ts new file mode 100644 index 0000000..321d79b --- /dev/null +++ b/src/services/weeklySummaryService.ts @@ -0,0 +1,27 @@ +import axiosInstance from './axiosInstance'; + +export interface WeeklySummarySettings { + enabled: boolean; + day: number; // 0-6, dove 0 = Domenica + hour: number; // 0-23 + last_sent_date?: string; + next_scheduled_date?: string; +} + +/** + * Ottiene le impostazioni correnti del riepilogo settimanale + */ +export async function getWeeklySummarySettings(): Promise { + const response = await axiosInstance.get('/notifications/weekly-summary-settings'); + return response.data; +} + +/** + * Aggiorna le impostazioni del riepilogo settimanale + */ +export async function updateWeeklySummarySettings( + settings: Partial +): Promise { + const response = await axiosInstance.put('/notifications/weekly-summary-settings', settings); + return response.data; +}