milestone 20: declarative configuration for iac

This commit is contained in:
2026-08-11 23:31:40 +02:00
parent 2f29121e27
commit d76afc147a
74 changed files with 6722 additions and 1949 deletions
+5 -2
View File
@@ -5,6 +5,7 @@ import { settingsPutMutation, settingsQuery } from "@/lib/queries";
import { buildSettingsPatch } from "@/lib/settingsDiff";
import type { Settings, SettingsPatch } from "@/lib/types";
import { raiseRestartBanner } from "./restartBanner";
import { READ_ONLY_HINT, useReadOnlyConfig } from "./authority";
import { focusRing } from "@/ui/classes";
/** True when the patch touches anything besides the write-only `web.password` (ruling 11). */
@@ -242,7 +243,8 @@ export default function SettingsPage() {
);
});
const patch = buildSettingsPatch(baseline, edited, password === "" ? undefined : password);
const saveDisabled = patch === null || passwordsMismatch || hasInvalidNumber || mutation.isPending;
const readOnly = useReadOnlyConfig();
const saveDisabled = patch === null || passwordsMismatch || hasInvalidNumber || mutation.isPending || readOnly;
function setField(section: keyof Settings, key: string, value: unknown): void {
setEdited((prev) => ({
@@ -273,7 +275,7 @@ export default function SettingsPage() {
Changes are validated as a whole; every setting requires a restart to take effect.
</p>
<form onSubmit={handleSubmit} className="mt-4 max-w-3xl">
<fieldset disabled={mutation.isPending} className="space-y-6">
<fieldset disabled={mutation.isPending || readOnly} className="space-y-6">
{SECTIONS.map(({ section, title, fields }) => (
<fieldset key={section} className="rounded border border-zinc-200 p-4 dark:border-zinc-800">
<legend className="px-1 text-sm font-semibold">{title}</legend>
@@ -339,6 +341,7 @@ export default function SettingsPage() {
<button
type="submit"
disabled={saveDisabled}
title={readOnly ? READ_ONLY_HINT : undefined}
className={`rounded bg-blue-600 px-4 py-1.5 text-sm font-medium text-white ${focusRing} disabled:bg-zinc-300 disabled:text-zinc-500 dark:disabled:bg-zinc-800`}
>
{mutation.isPending ? "Saving…" : "Save"}