diff --git a/web/src/components/settings/ModuleCard.tsx b/web/src/components/settings/ModuleCard.tsx index 1dcf80c..69f3068 100644 --- a/web/src/components/settings/ModuleCard.tsx +++ b/web/src/components/settings/ModuleCard.tsx @@ -182,8 +182,7 @@ export function ModuleCard({ {/* Dependencies note */} {deps.length > 0 && expanded && (
- Requires:{" "} - {deps.map((d) => allModules[d]?.name ?? d).join(", ")} + Requires: {deps.map((d) => allModules[d]?.name ?? d).join(", ")}
)} @@ -194,7 +193,9 @@ export function ModuleCard({ {/* Footer */}
-
+
{hasEdits && (
-
+
{saveSuccess && ( - + Saved )} {saveError && ( - + {saveError} )} @@ -251,11 +264,21 @@ export function ModuleCard({ > {testResult.success ? "OK" : "Failed"} - + {testResult.message} {testResult.latency_ms > 0 && ( - + {testResult.latency_ms}ms )} @@ -279,9 +302,22 @@ function renderModuleFields( case "core": return ( - - - + setValue("host", v)} + /> + setValue("port", Number(v))} + type="number" + /> + setValue("base_url", v)} + /> - - + setValue("directory", v)} + /> + setValue("default", v)} + /> ); case "database": return ( - - - - - - + setValue("host", v)} + /> + setValue("port", Number(v))} + type="number" + /> + setValue("name", v)} + /> + setValue("user", v)} + /> + setValue("password", v)} + /> + setValue("sslmode", v)} + /> + setValue("max_connections", Number(v))} + type="number" + /> ); case "storage": return ( - - - - + setValue("endpoint", v)} + /> + setValue("bucket", v)} + /> + setValue("use_ssl", v)} + /> + setValue("region", v)} + /> ); case "auth": - return renderAuthFields(settings); + return renderAuthFields(settings, getValue, setValue); case "freecad": return ( @@ -386,33 +485,114 @@ function renderModuleFields( } } -function renderAuthFields(settings: Record) { - const local = (settings.local ?? {}) as Record; - const ldap = (settings.ldap ?? {}) as Record; - const oidc = (settings.oidc ?? {}) as Record; +function renderAuthFields( + settings: Record, + getValue: (key: string) => unknown, + setValue: (key: string, value: unknown) => void, +) { + const local = (getValue("local") ?? settings.local ?? {}) as Record< + string, + unknown + >; + const ldap = (getValue("ldap") ?? settings.ldap ?? {}) as Record< + string, + unknown + >; + const oidc = (getValue("oidc") ?? settings.oidc ?? {}) as Record< + string, + unknown + >; + + const setNested = ( + section: string, + current: Record, + field: string, + v: unknown, + ) => { + setValue(section, { ...current, [field]: v }); + }; return (
- - + setNested("local", local, "enabled", v)} + /> + + setNested("local", local, "default_admin_username", v) + } + /> + + setNested("local", local, "default_admin_password", v) + } + /> - - - - + setNested("ldap", ldap, "enabled", v)} + /> + setNested("ldap", ldap, "url", v)} + /> + setNested("ldap", ldap, "base_dn", v)} + /> + setNested("ldap", ldap, "bind_dn", v)} + /> + setNested("ldap", ldap, "bind_password", v)} + /> - - - - + setNested("oidc", oidc, "enabled", v)} + /> + setNested("oidc", oidc, "issuer_url", v)} + /> + setNested("oidc", oidc, "client_id", v)} + /> + setNested("oidc", oidc, "client_secret", v)} + /> + setNested("oidc", oidc, "redirect_url", v)} + />
@@ -440,17 +620,9 @@ function SubSection({ ); } -function ReadOnlyField({ - label, - value, -}: { - label: string; - value: unknown; -}) { +function ReadOnlyField({ label, value }: { label: string; value: unknown }) { const display = - value === undefined || value === null || value === "" - ? "—" - : String(value); + value === undefined || value === null || value === "" ? "—" : String(value); return (
{label}
@@ -476,7 +648,7 @@ function EditableField({
{label}
onChange(e.target.value)} placeholder={isRedacted ? "••••••••" : undefined} @@ -487,6 +659,57 @@ function EditableField({ ); } +function SelectField({ + label, + value, + options, + onChange, +}: { + label: string; + value: unknown; + options: string[]; + onChange: (v: string) => void; +}) { + return ( +
+
{label}
+ +
+ ); +} + +function CheckboxField({ + label, + value, + onChange, +}: { + label: string; + value: unknown; + onChange: (v: boolean) => void; +}) { + return ( +
+ onChange(e.target.checked)} + style={{ accentColor: "var(--ctp-mauve)" }} + /> +
{label}
+
+ ); +} + // --- Styles --- const cardStyle: React.CSSProperties = {