feat(ui): establish Settings page layout primitives

This commit is contained in:
cesnimda
2026-09-12 21:59:35 +02:00
parent 806eee4e1e
commit 2cdcab9e21
58 changed files with 3019 additions and 94 deletions
+21 -32
View File
@@ -9,7 +9,6 @@ import {
FormControlLabel,
InputLabel,
MenuItem,
Paper,
Select,
Skeleton,
Tab,
@@ -30,6 +29,7 @@ import { ThemeModePref } from "../themePrefs";
import { useI18n } from "../i18n/I18nProvider";
import { api, getApiErrorMessage } from "../api";
import { useToast } from "../toast";
import { PageSection } from "../layout/PageLayout";
interface Props {
pageSize: 15 | 20 | 25;
@@ -42,17 +42,7 @@ interface Props {
function TabPanel({ value, index, children }: { value: number; index: number; children: React.ReactNode }) {
if (value !== index) return null;
return <Box sx={{ mt: 2.5 }}>{children}</Box>;
}
function SectionCard({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) {
return (
<Paper sx={{ p: "var(--app-card-padding)", borderRadius: 4, border: "none", boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)" }}>
<Typography variant="overline" sx={{ color: "text.secondary", fontWeight: 800 }}>{title}</Typography>
{subtitle ? <Typography variant="body2" sx={{ color: "text.secondary", mt: 0.25, mb: 1.5 }}>{subtitle}</Typography> : <Box sx={{ mb: 1.5 }} />}
{children}
</Paper>
);
return <Box sx={{ mt: "var(--app-section-gap)", minWidth: 0 }}>{children}</Box>;
}
type NotificationPrefs = {
@@ -97,7 +87,7 @@ export default function SettingsView({
};
return (
<Paper sx={{ mt: 0, p: "var(--app-card-padding)", borderRadius: 4, border: "none", boxShadow: "0px 1px 2px 0px rgba(15,23,42,0.04), 0px 8px 24px -12px rgba(15,23,42,0.12)", maxWidth: 960, mx: "auto" }}>
<Box sx={{ minWidth: 0 }}>
<FormControl fullWidth sx={{ display: { xs: "flex", sm: "none" }, mb: 1 }}>
<InputLabel id="settings-section-label">{t("settingsNavigation")}</InputLabel>
<Select
@@ -113,7 +103,7 @@ export default function SettingsView({
</Select>
</FormControl>
<Tabs value={tab} onChange={(_, v) => setTab(v)} variant="scrollable" scrollButtons="auto" sx={{ display: { xs: "none", sm: "flex" }, mb: 1 }}>
<Tabs aria-label={t("settings")} value={tab} onChange={(_, v) => setTab(v)} variant="scrollable" scrollButtons="auto" sx={{ display: { xs: "none", sm: "flex" }, mb: 1 }}>
<Tab label={t("settingsTabGeneral")} />
<Tab label={t("settingsTabFollowUps")} />
<Tab label={t("settingsTabNotifications")} />
@@ -121,9 +111,9 @@ export default function SettingsView({
</Tabs>
<TabPanel value={tab} index={0}>
<Box sx={{ display: "grid", gap: 2.5 }}>
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" }, gap: 2.5 }}>
<SectionCard title={t("settingsAppearance")}>
<Box sx={{ display: "grid", gap: "var(--app-section-gap)", minWidth: 0 }}>
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "minmax(0, 1fr)", md: "repeat(2, minmax(0, 1fr))" }, gap: "var(--app-section-gap)" }}>
<PageSection surface title={t("settingsAppearance")}>
<FormControl fullWidth>
<InputLabel id="theme-mode-label">{t("settingsTheme")}</InputLabel>
<Select
@@ -137,9 +127,9 @@ export default function SettingsView({
<MenuItem value="light">{t("settingsThemeLight")}</MenuItem>
</Select>
</FormControl>
</SectionCard>
</PageSection>
<SectionCard title={t("settingsLanguageTitle")} subtitle={t("settingsLanguageBody")}>
<PageSection surface title={t("settingsLanguageTitle")} description={t("settingsLanguageBody")}>
<FormControl fullWidth>
<InputLabel id="language-label">{t("settingsPreferredLanguage")}</InputLabel>
<Select
@@ -152,13 +142,13 @@ export default function SettingsView({
<MenuItem value="nb">{t("settingsNorwegian")}</MenuItem>
</Select>
</FormControl>
</SectionCard>
</PageSection>
</Box>
<SectionCard title={t("settingsJobs")}>
<PageSection surface title={t("settingsJobs")}>
<Box sx={{ display: "flex", gap: 4, flexWrap: "wrap" }}>
<Box sx={{ minWidth: 240 }}>
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 700 }}>
<Box sx={{ flex: "1 1 15rem", minWidth: 0 }}>
<Typography component="h3" variant="subtitle2" sx={{ mb: 1, fontWeight: 700 }}>
{t("settingsPagination")}
</Typography>
<FormControl fullWidth>
@@ -176,8 +166,8 @@ export default function SettingsView({
</FormControl>
</Box>
<Box sx={{ minWidth: 240 }}>
<Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 700 }}>
<Box sx={{ flex: "1 1 15rem", minWidth: 0 }}>
<Typography component="h3" variant="subtitle2" sx={{ mb: 1, fontWeight: 700 }}>
{t("settingsColumns")}
</Typography>
{(
@@ -200,14 +190,13 @@ export default function SettingsView({
<Box sx={{ mt: 2 }}>
<ImportExportJobs />
</Box>
</SectionCard>
</PageSection>
<AiPrivacySettingsCard />
<AiUsageCard />
<QuickCaptureCard />
<SectionCard title={t("connectedAccounts")} subtitle={t("settingsConnectedAccountsBody")}>
<Button variant="outlined" onClick={() => navigate("/settings/connected-accounts")}>{t("settingsManageConnectedAccounts")}</Button>
</SectionCard>
<PageSection surface title={t("connectedAccounts")} description={t("settingsConnectedAccountsBody")}
actions={<Button variant="outlined" onClick={() => navigate("/settings/connected-accounts")}>{t("settingsManageConnectedAccounts")}</Button>} />
</Box>
</TabPanel>
@@ -216,7 +205,7 @@ export default function SettingsView({
</TabPanel>
<TabPanel value={tab} index={2}>
<SectionCard title={t("settingsNotificationsTitle")} subtitle={t("settingsNotificationsBody")}>
<PageSection surface title={t("settingsNotificationsTitle")} description={t("settingsNotificationsBody")}>
{notificationError ? <Alert severity="error" sx={{ mb: 1.5 }}>{notificationError}</Alert> : null}
{!notificationPrefs ? <Skeleton variant="rounded" height={70} /> : <Box sx={{ display: "grid", gap: 1 }}>
<FormControlLabel
@@ -232,12 +221,12 @@ export default function SettingsView({
<Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", mt: 1.5 }}>
<Button variant="outlined" onClick={() => navigate("/reminders")}>{t("settingsOpenReminderInbox")}</Button>
</Box>
</SectionCard>
</PageSection>
</TabPanel>
<TabPanel value={tab} index={3}>
<BackupCard />
</TabPanel>
</Paper>
</Box>
);
}