import React, { useState } from "react"; import { Box, List, ListItem, ListItemIcon, ListItemText, Tabs, Tab, IconButton, FormControl, Select, MenuItem, Typography, Snackbar, Switch, FormControlLabel, } from "@mui/material"; import ArrowBackIcon from "@mui/icons-material/ArrowBack"; import SettingsIcon from "@mui/icons-material/Settings"; // import SyncIcon from "@mui/icons-material/Sync"; import { useAppDispatch, useAppSelector } from "../../app/hooks"; import { setView, setLanguage as setSettingsLanguage, setTimeZone as setSettingsTimeZone, setIsBrowserDefaultTimeZone, setHideDeclinedEvents, } from "./SettingsSlice"; import { updateUserConfigurationsAsync, setLanguage as setUserLanguage, setTimezone as setUserTimeZone, setAlarmEmails, } from "../User/userSlice"; import { AVAILABLE_LANGUAGES } from "./constants"; import { useI18n } from "twake-i18n"; import "./SettingsPage.styl"; import { useTimeZoneList, getTimezoneOffset, } from "../../components/Calendar/TimezoneSelector"; import { TimezoneAutocomplete } from "../../components/Timezone/TimezoneAutocomplete"; import { browserDefaultTimeZone } from "../../utils/timezone"; type SidebarNavItem = "settings" | "sync"; type SettingsSubTab = "settings" | "notifications"; export default function SettingsPage() { const dispatch = useAppDispatch(); const { t } = useI18n(); const previousConfig = useAppSelector((state) => state.user.coreConfig); const userLanguage = useAppSelector( (state) => state.user?.coreConfig.language ); const settingsLanguage = useAppSelector((state) => state.settings?.language); const currentLanguage = userLanguage || settingsLanguage || "en"; const timezoneList = useTimeZoneList(); const userTimeZone = useAppSelector( (state) => state.user?.coreConfig?.datetime?.timeZone ); const settingTimeZone = useAppSelector((state) => state.settings?.timeZone); const currentTimeZone = userTimeZone ?? settingTimeZone ?? browserDefaultTimeZone; const isBrowserDefault = useAppSelector( (state) => state.settings.isBrowserDefaultTimeZone ); const alarmEmailsEnabled = useAppSelector( (state) => state.user?.alarmEmailsEnabled ?? true ); const hideDeclinedEvents = useAppSelector( (state) => state.settings?.hideDeclinedEvents ); const [activeNavItem, setActiveNavItem] = useState("settings"); const [activeSettingsSubTab, setActiveSettingsSubTab] = useState("settings"); const [languageErrorOpen, setLanguageErrorOpen] = useState(false); const [timeZoneErrorOpen, setTimeZoneErrorOpen] = useState(false); const [alarmEmailsErrorOpen, setAlarmEmailsErrorOpen] = useState(false); const [hideDeclinedEventsErrorOpen, setHideDeclinedEventsErrorOpen] = useState(false); const handleBackClick = () => { dispatch(setView("calendar")); }; const handleNavItemClick = (item: SidebarNavItem) => { setActiveNavItem(item); if (item === "settings") { setActiveSettingsSubTab("settings"); } }; const handleSettingsSubTabChange = ( _event: React.SyntheticEvent, newValue: SettingsSubTab ) => { setActiveSettingsSubTab(newValue); }; const handleLanguageChange = (event: any) => { const newLanguage = event.target.value; const previousLanguage = currentLanguage; // Optimistic update - update UI immediately dispatch(setUserLanguage(newLanguage)); dispatch(setSettingsLanguage(newLanguage)); // Call API in background, don't wait for it dispatch(updateUserConfigurationsAsync({ language: newLanguage })) .unwrap() .catch((error) => { console.error("Failed to update language:", error); // Rollback on error dispatch(setUserLanguage(previousLanguage)); dispatch(setSettingsLanguage(previousLanguage)); setLanguageErrorOpen(true); }); }; const handleLanguageErrorClose = () => { setLanguageErrorOpen(false); }; const handleTimeZoneChange = (newTimeZone: string) => { const previousTimeZone = currentTimeZone; // Optimistic update - update UI immediately dispatch(setUserTimeZone(newTimeZone)); dispatch(setSettingsTimeZone(newTimeZone)); // Call API in background, don't wait for it dispatch( updateUserConfigurationsAsync({ timezone: newTimeZone, previousConfig }) ) .unwrap() .catch((error) => { console.error("Failed to update TimeZone:", error); // Rollback on error dispatch(setUserTimeZone(previousTimeZone)); dispatch(setSettingsTimeZone(previousTimeZone)); setTimeZoneErrorOpen(true); }); }; const handleTimeZoneDefaultChange = (isDefault: boolean) => { const previousTimeZone = currentTimeZone; // Optimistic update - update UI immediately dispatch(setIsBrowserDefaultTimeZone(isDefault)); if (isDefault) { dispatch(setUserTimeZone(null)); dispatch(setSettingsTimeZone(browserDefaultTimeZone)); // Call API in background, don't wait for it dispatch( updateUserConfigurationsAsync({ timezone: null, previousConfig }) ) .unwrap() .catch((error) => { console.error("Failed to update TimeZone:", error); // Rollback on error dispatch(setUserTimeZone(previousTimeZone)); dispatch(setSettingsTimeZone(previousTimeZone)); dispatch(setIsBrowserDefaultTimeZone(!isDefault)); setTimeZoneErrorOpen(true); }); } }; const handleTimeZoneErrorClose = () => { setTimeZoneErrorOpen(false); }; const handleHideDeclinedEvents = (doHideDeclinedEvents: boolean) => { // Optimistic update - update UI immediately dispatch(setHideDeclinedEvents(doHideDeclinedEvents)); // Call API in background, don't wait for it dispatch( updateUserConfigurationsAsync({ hideDeclinedEvents: doHideDeclinedEvents, }) ) .unwrap() .catch((error) => { console.error("Failed to update hide declined event:", error); dispatch(setHideDeclinedEvents(!doHideDeclinedEvents)); setHideDeclinedEventsErrorOpen(true); }); }; const handleHideDeclinedEventsErrorClose = () => { setHideDeclinedEventsErrorOpen(false); }; const handleAlarmEmailsToggle = ( event: React.ChangeEvent ) => { const newValue = event.target.checked; const previousValue = alarmEmailsEnabled; // Optimistic update - update UI immediately dispatch(setAlarmEmails(newValue)); // Call API in background, don't wait for it dispatch(updateUserConfigurationsAsync({ alarmEmails: newValue })) .unwrap() .catch((error) => { console.error("Failed to update alarm emails:", error); // Rollback on error dispatch(setAlarmEmails(previousValue)); setAlarmEmailsErrorOpen(true); }); }; const handleAlarmEmailsErrorClose = () => { setAlarmEmailsErrorOpen(false); }; return (
handleNavItemClick("settings")} sx={{ cursor: "pointer" }} > {/* handleNavItemClick("sync")} sx={{ cursor: "pointer" }} > */} {activeNavItem === "settings" && ( )} {activeNavItem === "settings" && ( <> {activeSettingsSubTab === "settings" && ( {t("settings.language") || "Language"} {t("settings.languageDescription") || "This will be the language used in your Twake Calendar"} {t("settings.timeZone")} handleTimeZoneDefaultChange(!isBrowserDefault) } aria-label={t("settings.timeZoneBrowserDefault")} /> } label={t("settings.timeZoneBrowserDefault")} labelPlacement="start" sx={{ minWidth: 400, justifyContent: "space-between", marginLeft: 0, mb: 2, }} /> {!isBrowserDefault && ( )} {t("settings.calAndEvent")} handleHideDeclinedEvents(!hideDeclinedEvents) } aria-label={t("settings.showDeclinedEvent")} /> } label={t("settings.showDeclinedEvent")} labelPlacement="start" sx={{ minWidth: 400, justifyContent: "space-between", marginLeft: 0, mb: 2, }} /> )} {activeSettingsSubTab === "notifications" && ( {t("settings.notifications.deliveryMethod") || "Delivery method"} } label={t("settings.notifications.email") || "Email"} labelPlacement="start" sx={{ minWidth: 400, justifyContent: "space-between", marginLeft: 0, }} /> )} )} {activeNavItem === "sync" && ( {t("settings.sync.empty") || "Sync settings coming soon"} )}
); }