diff options
Diffstat (limited to 'src/app/(main)/settings/preferences')
7 files changed, 209 insertions, 0 deletions
diff --git a/src/app/(main)/settings/preferences/DateRangeSetting.tsx b/src/app/(main)/settings/preferences/DateRangeSetting.tsx new file mode 100644 index 0000000..3f5e664 --- /dev/null +++ b/src/app/(main)/settings/preferences/DateRangeSetting.tsx @@ -0,0 +1,28 @@ +import { Button, Row } from '@umami/react-zen'; +import { useState } from 'react'; +import { useMessages } from '@/components/hooks'; +import { DateFilter } from '@/components/input/DateFilter'; +import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE_VALUE } from '@/lib/constants'; +import { getItem, setItem } from '@/lib/storage'; + +export function DateRangeSetting() { + const { formatMessage, labels } = useMessages(); + const [date, setDate] = useState(getItem(DATE_RANGE_CONFIG) || DEFAULT_DATE_RANGE_VALUE); + + const handleChange = (value: string) => { + setItem(DATE_RANGE_CONFIG, value); + setDate(value); + }; + + const handleReset = () => { + setItem(DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE_VALUE); + setDate(DEFAULT_DATE_RANGE_VALUE); + }; + + return ( + <Row gap="3"> + <DateFilter value={date} onChange={handleChange} placement="bottom start" /> + <Button onPress={handleReset}>{formatMessage(labels.reset)}</Button> + </Row> + ); +} diff --git a/src/app/(main)/settings/preferences/LanguageSetting.tsx b/src/app/(main)/settings/preferences/LanguageSetting.tsx new file mode 100644 index 0000000..00a2d74 --- /dev/null +++ b/src/app/(main)/settings/preferences/LanguageSetting.tsx @@ -0,0 +1,48 @@ +import { Button, ListItem, Row, Select } from '@umami/react-zen'; +import { useState } from 'react'; +import { useLocale, useMessages } from '@/components/hooks'; +import { DEFAULT_LOCALE } from '@/lib/constants'; +import { languages } from '@/lib/lang'; + +export function LanguageSetting() { + const [search, setSearch] = useState(''); + const { formatMessage, labels } = useMessages(); + const { locale, saveLocale } = useLocale(); + const items = search + ? Object.keys(languages).filter(n => { + return ( + n.toLowerCase().includes(search.toLowerCase()) || + languages[n].label.toLowerCase().includes(search.toLowerCase()) + ); + }) + : Object.keys(languages); + + const handleReset = () => saveLocale(DEFAULT_LOCALE); + + const handleOpen = (isOpen: boolean) => { + if (isOpen) { + setSearch(''); + } + }; + + return ( + <Row gap> + <Select + value={locale} + onChange={val => saveLocale(val as string)} + allowSearch + onSearch={setSearch} + onOpenChange={handleOpen} + listProps={{ style: { maxHeight: 300 } }} + > + {items.map(item => ( + <ListItem key={item} id={item}> + {languages[item].label} + </ListItem> + ))} + {!items.length && <ListItem></ListItem>} + </Select> + <Button onPress={handleReset}>{formatMessage(labels.reset)}</Button> + </Row> + ); +} diff --git a/src/app/(main)/settings/preferences/PreferenceSettings.tsx b/src/app/(main)/settings/preferences/PreferenceSettings.tsx new file mode 100644 index 0000000..a2890ce --- /dev/null +++ b/src/app/(main)/settings/preferences/PreferenceSettings.tsx @@ -0,0 +1,36 @@ +import { Column, Label } from '@umami/react-zen'; +import { useLoginQuery, useMessages } from '@/components/hooks'; +import { DateRangeSetting } from './DateRangeSetting'; +import { LanguageSetting } from './LanguageSetting'; +import { ThemeSetting } from './ThemeSetting'; +import { TimezoneSetting } from './TimezoneSetting'; + +export function PreferenceSettings() { + const { user } = useLoginQuery(); + const { formatMessage, labels } = useMessages(); + + if (!user) { + return null; + } + + return ( + <Column width="400px" gap="6"> + <Column> + <Label>{formatMessage(labels.defaultDateRange)}</Label> + <DateRangeSetting /> + </Column> + <Column> + <Label>{formatMessage(labels.timezone)}</Label> + <TimezoneSetting /> + </Column> + <Column> + <Label>{formatMessage(labels.language)}</Label> + <LanguageSetting /> + </Column> + <Column> + <Label>{formatMessage(labels.theme)}</Label> + <ThemeSetting /> + </Column> + </Column> + ); +} diff --git a/src/app/(main)/settings/preferences/PreferencesPage.tsx b/src/app/(main)/settings/preferences/PreferencesPage.tsx new file mode 100644 index 0000000..61e2669 --- /dev/null +++ b/src/app/(main)/settings/preferences/PreferencesPage.tsx @@ -0,0 +1,22 @@ +'use client'; +import { Column } from '@umami/react-zen'; +import { PageBody } from '@/components/common/PageBody'; +import { PageHeader } from '@/components/common/PageHeader'; +import { Panel } from '@/components/common/Panel'; +import { useMessages } from '@/components/hooks'; +import { PreferenceSettings } from './PreferenceSettings'; + +export function PreferencesPage() { + const { formatMessage, labels } = useMessages(); + + return ( + <PageBody> + <Column gap="6"> + <PageHeader title={formatMessage(labels.preferences)} /> + <Panel> + <PreferenceSettings /> + </Panel> + </Column> + </PageBody> + ); +} diff --git a/src/app/(main)/settings/preferences/ThemeSetting.tsx b/src/app/(main)/settings/preferences/ThemeSetting.tsx new file mode 100644 index 0000000..03bd6a6 --- /dev/null +++ b/src/app/(main)/settings/preferences/ThemeSetting.tsx @@ -0,0 +1,21 @@ +import { Button, Icon, Row, useTheme } from '@umami/react-zen'; +import { Moon, Sun } from '@/components/icons'; + +export function ThemeSetting() { + const { theme, setTheme } = useTheme(); + + return ( + <Row gap> + <Button variant={theme === 'light' ? 'primary' : undefined} onPress={() => setTheme('light')}> + <Icon> + <Sun /> + </Icon> + </Button> + <Button variant={theme === 'dark' ? 'primary' : undefined} onPress={() => setTheme('dark')}> + <Icon> + <Moon /> + </Icon> + </Button> + </Row> + ); +} diff --git a/src/app/(main)/settings/preferences/TimezoneSetting.tsx b/src/app/(main)/settings/preferences/TimezoneSetting.tsx new file mode 100644 index 0000000..cf20b20 --- /dev/null +++ b/src/app/(main)/settings/preferences/TimezoneSetting.tsx @@ -0,0 +1,44 @@ +import { Button, ListItem, Row, Select } from '@umami/react-zen'; +import { useState } from 'react'; +import { useMessages, useTimezone } from '@/components/hooks'; +import { getTimezone } from '@/lib/date'; + +const timezones = Intl.supportedValuesOf('timeZone'); + +export function TimezoneSetting() { + const [search, setSearch] = useState(''); + const { formatMessage, labels } = useMessages(); + const { timezone, saveTimezone } = useTimezone(); + const items = search + ? timezones.filter(n => n.toLowerCase().includes(search.toLowerCase())) + : timezones; + + const handleReset = () => saveTimezone(getTimezone()); + + const handleOpen = isOpen => { + if (isOpen) { + setSearch(''); + } + }; + + return ( + <Row gap> + <Select + value={timezone} + onChange={(value: any) => saveTimezone(value)} + allowSearch={true} + onSearch={setSearch} + onOpenChange={handleOpen} + listProps={{ style: { maxHeight: 300 } }} + > + {items.map((item: any) => ( + <ListItem key={item} id={item}> + {item} + </ListItem> + ))} + {!items.length && <ListItem></ListItem>} + </Select> + <Button onPress={handleReset}>{formatMessage(labels.reset)}</Button> + </Row> + ); +} diff --git a/src/app/(main)/settings/preferences/page.tsx b/src/app/(main)/settings/preferences/page.tsx new file mode 100644 index 0000000..dd16870 --- /dev/null +++ b/src/app/(main)/settings/preferences/page.tsx @@ -0,0 +1,10 @@ +import type { Metadata } from 'next'; +import { PreferencesPage } from './PreferencesPage'; + +export default function () { + return <PreferencesPage />; +} + +export const metadata: Metadata = { + title: 'Preferences', +}; |