aboutsummaryrefslogtreecommitdiff
path: root/src/app/(main)/settings/preferences
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/(main)/settings/preferences')
-rw-r--r--src/app/(main)/settings/preferences/DateRangeSetting.tsx28
-rw-r--r--src/app/(main)/settings/preferences/LanguageSetting.tsx48
-rw-r--r--src/app/(main)/settings/preferences/PreferenceSettings.tsx36
-rw-r--r--src/app/(main)/settings/preferences/PreferencesPage.tsx22
-rw-r--r--src/app/(main)/settings/preferences/ThemeSetting.tsx21
-rw-r--r--src/app/(main)/settings/preferences/TimezoneSetting.tsx44
-rw-r--r--src/app/(main)/settings/preferences/page.tsx10
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',
+};