First Commit

This commit is contained in:
cesnimda
2026-03-21 11:55:27 +01:00
commit 2e8a29b4d0
1757 changed files with 166084 additions and 0 deletions
@@ -0,0 +1,213 @@
import React, { useMemo, useState } from "react";
import {
Box,
Button,
Checkbox,
FormControl,
FormControlLabel,
InputLabel,
MenuItem,
Paper,
Select,
Tab,
Tabs,
TextField,
Typography,
} from "@mui/material";
import { JobTableColumns } from "./JobTable";
import ImportExportJobs from "./ImportExportJobs";
import GoogleAuthCard from "./GoogleAuthCard";
import RulesSettingsCard from "./RulesSettingsCard";
import BackupCard from "./BackupCard";
import AuthStatusCard from "./AuthStatusCard";
export type ThemeModePref = "system" | "light" | "dark";
interface Props {
pageSize: 15 | 20 | 25;
onPageSizeChange: (n: 15 | 20 | 25) => void;
columns: JobTableColumns;
onColumnsChange: (next: JobTableColumns) => void;
themeMode: ThemeModePref;
onThemeModeChange: (v: ThemeModePref) => void;
accentColor: string;
onAccentColorChange: (v: string) => void;
onResetAccentColor: () => void;
}
function TabPanel({ value, index, children }: { value: number; index: number; children: React.ReactNode }) {
if (value !== index) return null;
return <Box sx={{ mt: 2 }}>{children}</Box>;
}
const ACCENTS = ["#7c4dff", "#4f8cff", "#16a34a", "#f59e0b", "#e11d48", "#06b6d4"]; // violet, blue, green, amber, rose, cyan
export default function SettingsView({
pageSize,
onPageSizeChange,
columns,
onColumnsChange,
themeMode,
onThemeModeChange,
accentColor,
onAccentColorChange,
onResetAccentColor,
}: Props) {
const [tab, setTab] = useState(0);
const accentOk = useMemo(() => /^#[0-9a-fA-F]{6}$/.test(accentColor), [accentColor]);
return (
<Paper sx={{ mt: 0, p: 2 }}>
<Typography variant="h5" sx={{ mb: 1, fontWeight: 900 }}>
Settings
</Typography>
<Typography sx={{ color: "text.secondary", mb: 2 }}>
Preferences and admin tools.
</Typography>
<Tabs value={tab} onChange={(_, v) => setTab(v)} sx={{ mb: 1 }}>
<Tab label="General" />
<Tab label="Follow-ups" />
<Tab label="Notifications" />
<Tab label="Account" />
<Tab label="Backup" />
</Tabs>
<TabPanel value={tab} index={0}>
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", md: "1fr 1fr" }, gap: 2 }}>
<Paper sx={{ p: 2 }}>
<Typography sx={{ fontWeight: 950, mb: 1 }}>Appearance</Typography>
<FormControl fullWidth sx={{ mb: 2 }}>
<InputLabel id="theme-mode-label">Theme</InputLabel>
<Select
labelId="theme-mode-label"
value={themeMode}
label="Theme"
onChange={(e) => onThemeModeChange(e.target.value as ThemeModePref)}
>
<MenuItem value="system">System</MenuItem>
<MenuItem value="dark">Dark</MenuItem>
<MenuItem value="light">Light</MenuItem>
</Select>
</FormControl>
<Box sx={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 2, flexWrap: "wrap" }}>
<TextField
label="Accent"
type="color"
value={accentOk ? accentColor : "#7c4dff"}
onChange={(e) => onAccentColorChange(e.target.value)}
sx={{ width: 160 }}
InputLabelProps={{ shrink: true }}
/>
<Button variant="outlined" onClick={onResetAccentColor}>
Reset
</Button>
</Box>
<Box sx={{ display: "flex", gap: 1, flexWrap: "wrap", mt: 2 }}>
{ACCENTS.map((c) => (
<button
key={c}
type="button"
onClick={() => onAccentColorChange(c)}
title={c}
style={{
width: 28,
height: 28,
borderRadius: 999,
border: c.toLowerCase() === accentColor.toLowerCase() ? "2px solid rgba(255,255,255,0.85)" : "1px solid rgba(148,163,184,0.35)",
background: c,
cursor: "pointer",
}}
/>
))}
</Box>
<Typography variant="caption" sx={{ color: "text.secondary", display: "block", mt: 1 }}>
Saved per user on this browser.
</Typography>
</Paper>
<Paper sx={{ p: 2 }}>
<Typography sx={{ fontWeight: 950, mb: 1 }}>Jobs</Typography>
<Box sx={{ display: "flex", gap: 3, flexWrap: "wrap" }}>
<Box sx={{ minWidth: 240 }}>
<Typography variant="h6" sx={{ mb: 1 }}>
Pagination
</Typography>
<FormControl fullWidth>
<InputLabel id="page-size-label">Rows per page</InputLabel>
<Select
labelId="page-size-label"
value={pageSize}
label="Rows per page"
onChange={(e) => onPageSizeChange(e.target.value as 15 | 20 | 25)}
>
<MenuItem value={15}>15</MenuItem>
<MenuItem value={20}>20</MenuItem>
<MenuItem value={25}>25</MenuItem>
</Select>
</FormControl>
</Box>
<Box sx={{ minWidth: 240 }}>
<Typography variant="h6" sx={{ mb: 1 }}>
Columns
</Typography>
{(
[
["status", "Status"],
["dateApplied", "Date applied"],
["daysSince", "Days"],
["jobUrl", "Job URL"],
] as const
).map(([key, label]) => (
<FormControlLabel
key={key}
control={
<Checkbox
checked={columns[key]}
onChange={() => onColumnsChange({ ...columns, [key]: !columns[key] })}
/>
}
label={label}
/>
))}
</Box>
</Box>
<ImportExportJobs />
</Paper>
</Box>
</TabPanel>
<TabPanel value={tab} index={1}>
<RulesSettingsCard />
</TabPanel>
<TabPanel value={tab} index={2}>
<Paper sx={{ p: 2, mt: 2 }}>
<Typography sx={{ fontWeight: 950, mb: 0.5 }}>Email notifications</Typography>
<Typography sx={{ color: "text.secondary" }}>
Notifications are sent via SMTP (Gmail works). Configure SMTP in the API (`Email:*` settings or env vars like `EMAIL_SMTP_HOST`).
</Typography>
</Paper>
</TabPanel>
<TabPanel value={tab} index={3}>
<AuthStatusCard />
<GoogleAuthCard />
</TabPanel>
<TabPanel value={tab} index={4}>
<BackupCard />
</TabPanel>
</Paper>
);
}