milestone 32: task-shaped configuration, file mode as a rendering, config status api
Gates / frontend (push) Successful in 1m22s
Gates / test (push) Successful in 1m54s
Gates / test-aarch64 (push) Successful in 7m57s
Gates / package (push) Successful in 5m29s
Gates / container (push) Successful in 10s
CI / gates (push) Successful in 32m51s
Gates / frontend (push) Successful in 1m22s
Gates / test (push) Successful in 1m54s
Gates / test-aarch64 (push) Successful in 7m57s
Gates / package (push) Successful in 5m29s
Gates / container (push) Successful in 10s
CI / gates (push) Successful in 32m51s
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import * as stylex from "@stylexjs/stylex";
|
||||
import type { Settings } from "@/lib/types";
|
||||
import DefinitionList, { type Definition } from "@/ui/DefinitionList";
|
||||
import { colors } from "@/ui/tokens.stylex";
|
||||
import { SECTIONS, sectionValues, type AnyFieldDef } from "./settingsSections";
|
||||
import { styles as config } from "./styles";
|
||||
|
||||
const styles = stylex.create({
|
||||
absent: {
|
||||
color: colors.textMuted,
|
||||
},
|
||||
});
|
||||
|
||||
/** The field key as a sentence. The exact key travels beside it, so this is free to read well. */
|
||||
export function humanize(key: string): string {
|
||||
const spaced = key.replace(/_/g, " ");
|
||||
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Values are shown as the file spells them: `true`, not "Yes". The reader's
|
||||
* next step is editing that file, and a value they cannot type back is a
|
||||
* translation they have to undo.
|
||||
*/
|
||||
function renderValue(value: unknown) {
|
||||
if (typeof value === "boolean") return String(value);
|
||||
if (typeof value === "number") return String(value);
|
||||
if (value === "") return <span {...stylex.props(styles.absent)}>empty</span>;
|
||||
return String(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The settings under file authority: definition lists, one per section, each
|
||||
* scalar carrying its exact ZON key. No inputs, no Save — the file is the
|
||||
* form.
|
||||
*/
|
||||
export default function SettingsDefinitions({ settings }: { settings: Settings }) {
|
||||
return (
|
||||
<>
|
||||
{SECTIONS.map(({ section, title, fields }) => {
|
||||
const values = sectionValues(settings, section);
|
||||
const items: Definition[] = (fields as readonly AnyFieldDef[]).map((def) => ({
|
||||
label: humanize(def.key),
|
||||
zonKey: `${section}.${def.key}`,
|
||||
value: renderValue(values[def.key]),
|
||||
}));
|
||||
if (section === "web") {
|
||||
// Derived from whether a password hash is stored, so it has no key of
|
||||
// its own (D13). Naming one would send the reader to a line that is
|
||||
// not in the file.
|
||||
items.push({
|
||||
label: "Authentication",
|
||||
value: settings.web.auth_enabled ? "required" : "not configured",
|
||||
});
|
||||
}
|
||||
return (
|
||||
<section key={section} {...stylex.props(config.panel)}>
|
||||
<h2 {...stylex.props(config.panelHeading)}>{title}</h2>
|
||||
<div {...stylex.props(config.note)}>
|
||||
<DefinitionList items={items} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user