/** * Form model of the general + quota cards on the admin settings page. * * Field names MUST NOT contain dots: react-hook-form treats a dot in a * field name as a nested-path separator. A field registered under its * settings key ('instance.name') DISPLAYS fine — RHF's getter falls back * to the literal flat key — but typing writes the value into a nested * object ({ instance: { name } }), which the api's strict PATCH schema * rejects, so nothing ever saved (issue #322). This mapping is the single * place tying a dot-free field name to its dotted settings key; the * converters below translate in both directions. */ export const GENERAL_FORM_FIELDS = { instanceName: 'instance.name', defaultLocale: 'instance.defaultLocale', registrationMode: 'auth.registrationMode', invitationsMaxOpenPerUser: 'invitations.maxOpenPerUser', newPageClassification: 'classification.newPageDefault', uploadPolicy: 'classification.uploadPolicy', quotaEditorsPerPond: 'quota.editorsPerPond', quotaReadersPerPond: 'quota.readersPerPond', quotaAdditionalPonds: 'quota.additionalPonds', quotaStorageBytes: 'quota.storageBytes', quotaMaxFileBytes: 'quota.maxFileBytes', } as const; export type GeneralFormField = keyof typeof GENERAL_FORM_FIELDS; export type GeneralFormSettingKey = (typeof GENERAL_FORM_FIELDS)[GeneralFormField]; export interface GeneralSettingsForm { instanceName: string; defaultLocale: 'de' | 'en'; registrationMode: 'open' | 'closed'; invitationsMaxOpenPerUser: number; newPageClassification: 'unclassified' | 'vs_nfd'; uploadPolicy: 'warn' | 'block'; quotaEditorsPerPond: number; quotaReadersPerPond: number; quotaAdditionalPonds: number; quotaStorageBytes: number; quotaMaxFileBytes: number; } /** The settings this form reads and writes, keyed by their dotted names. */ export type GeneralFormSettings = Record; export function toFormValues(settings: GeneralFormSettings): GeneralSettingsForm { return Object.fromEntries( Object.entries(GENERAL_FORM_FIELDS).map(([field, key]) => [field, settings[key]]), ) as unknown as GeneralSettingsForm; } /** Flat dotted keys, exactly what PATCH /admin/settings expects. */ export function toSettingsPatch(input: GeneralSettingsForm): GeneralFormSettings { return Object.fromEntries( Object.entries(GENERAL_FORM_FIELDS).map(([field, key]) => [ key, input[field as GeneralFormField], ]), ) as GeneralFormSettings; }