diff --git a/src/app/components/system-config/BackupTab.tsx b/src/app/components/system-config/BackupTab.tsx new file mode 100644 index 0000000..262f0bc --- /dev/null +++ b/src/app/components/system-config/BackupTab.tsx @@ -0,0 +1,7 @@ +"use client"; + +import type { TabProps } from "./SystemConfigDialog"; + +export function BackupTab(_props: TabProps) { + return
Implemented in the next task.
; +} diff --git a/src/app/components/system-config/GeneralTab.tsx b/src/app/components/system-config/GeneralTab.tsx new file mode 100644 index 0000000..3951eb2 --- /dev/null +++ b/src/app/components/system-config/GeneralTab.tsx @@ -0,0 +1,133 @@ +"use client"; + +import { useRef, useState } from "react"; +import { mutate as globalMutate } from "swr"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { errorToast } from "@/lib/errorReporter"; +import { + PUBLIC_SYSTEM_CONFIG_KEY, + logoSrc, + usePublicSystemConfig, +} from "@/app/hooks/usePublicSystemConfig"; +import type { TabProps } from "./SystemConfigDialog"; + +function BrandingUpload({ + kind, + label, + currentSrc, +}: { + kind: "logo" | "favicon"; + label: string; + currentSrc: string; +}) { + const inputRef = useRef+ PNG, JPEG or SVG, up to 512KB. The favicon also accepts ICO. Changes + apply on save for the wordmark; uploads apply immediately. +
+Implemented in the next task.
; +} diff --git a/src/app/components/system-config/SystemConfigDialog.tsx b/src/app/components/system-config/SystemConfigDialog.tsx new file mode 100644 index 0000000..c418d90 --- /dev/null +++ b/src/app/components/system-config/SystemConfigDialog.tsx @@ -0,0 +1,172 @@ +"use client"; + +import { useEffect, useState } from "react"; +import useSWR, { mutate as globalMutate } from "swr"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { errorToast } from "@/lib/errorReporter"; +import { PUBLIC_SYSTEM_CONFIG_KEY } from "@/app/hooks/usePublicSystemConfig"; +import { GeneralTab } from "./GeneralTab"; +import { LoginTermsTab } from "./LoginTermsTab"; +import { SecurityTab } from "./SecurityTab"; +import { BackupTab } from "./BackupTab"; + +export interface SystemConfig { + branding: { wordmark: string; logoFile: string | null; faviconFile: string | null }; + loginTerms: { enabled: boolean; markdown: string }; + security: { + authEnabled: boolean | null; + sessionTtlHours: number | null; + passwordMinLength: number | null; + captchaEnabled: boolean | null; + captchaThreshold: number | null; + }; + backup: { + backendDataDir: string; + schedule: { enabled: boolean; intervalHours: number; keepCount: number }; + }; +} + +export interface TabProps { + draft: SystemConfig; + setDraft: (updater: (config: SystemConfig) => SystemConfig) => void; + onSaved: () => void; +} + +const CONFIG_KEY = "/api/system/config"; + +const fetcher = async (url: string): Promise