/* This file is part of GNU Taler (C) 2022-2024 Taler Systems S.A. GNU Taler is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ import { AmountJson, TranslatedString } from "@gnu-taler/taler-util"; import { useState } from "preact/hooks"; export type UIField = { value: string | undefined; onUpdate: (s: string) => void; error: TranslatedString | undefined; }; type FormHandler = { [k in keyof T]?: T[k] extends string ? UIField : T[k] extends AmountJson ? UIField : FormHandler; }; export type FormValues = { [k in keyof T]: T[k] extends string ? string | undefined : T[k] extends AmountJson ? string | undefined : FormValues; }; export type RecursivePartial = { [k in keyof T]?: T[k] extends string ? string : T[k] extends AmountJson ? AmountJson : RecursivePartial; }; export type FormErrors = { [k in keyof T]?: T[k] extends string ? TranslatedString : T[k] extends AmountJson ? TranslatedString : FormErrors; }; export type FormStatus = | { status: "ok"; result: T; errors: undefined; } | { status: "fail"; result: RecursivePartial; errors: FormErrors; }; function constructFormHandler( form: FormValues, updateForm: (d: FormValues) => void, errors: FormErrors | undefined, ): FormHandler { const keys = Object.keys(form) as Array; const handler = keys.reduce((prev, fieldName) => { const currentValue: unknown = form[fieldName]; const currentError: unknown = errors ? errors[fieldName] : undefined; function updater(newValue: unknown) { updateForm({ ...form, [fieldName]: newValue }); } if (typeof currentValue === "object") { // @ts-expect-error FIXME better typing const group = constructFormHandler(currentValue, updater, currentError); // @ts-expect-error FIXME better typing prev[fieldName] = group; return prev; } const field: UIField = { // @ts-expect-error FIXME better typing error: currentError, // @ts-expect-error FIXME better typing value: currentValue, onUpdate: updater, }; // @ts-expect-error FIXME better typing prev[fieldName] = field; return prev; }, {} as FormHandler); return handler; } /** * FIXME: Consider sending this to web-utils * * * @param defaultValue * @param check * @returns */ export function useFormState( defaultValue: FormValues, check: (f: FormValues) => FormStatus, ): [FormHandler, FormStatus] { const [form, updateForm] = useState>(defaultValue); const status = check(form); const handler = constructFormHandler(form, updateForm, status.errors); return [handler, status]; }