/* 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 { AbsoluteTime, AmountJson, Amounts, HttpStatusCode, TalerExchangeApi, TalerProtocolTimestamp, assertUnreachable, } from "@gnu-taler/taler-util"; import { Button, FormMetadata, InternationalizationAPI, LocalNotificationBanner, RenderAllFieldsByUiConfig, UIHandlerId, convertUiField, getConverterById, useExchangeApiContext, useLocalNotificationHandler, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { privatePages } from "../Routing.js"; import { useUiFormsContext } from "../context/ui-forms.js"; import { preloadedForms } from "../forms/index.js"; import { FormErrors, getRequiredFields, getShapeFromFields, useFormState, validateRequiredFields, } from "../hooks/form.js"; import { useOfficer } from "../hooks/officer.js"; import { Justification } from "./CaseDetails.js"; import { undefinedIfEmpty } from "./CreateAccount.js"; import { HandleAccountNotReady } from "./HandleAccountNotReady.js"; function searchForm( i18n: InternationalizationAPI, forms: FormMetadata[], formId: string, ): FormMetadata | undefined { { const found = forms.find((v) => v.id === formId); if (found) return found; } { const pf = preloadedForms(i18n); const found = pf.find((v) => v.id === formId); if (found) return found; } return undefined; } type FormType = { when: AbsoluteTime; state: TalerExchangeApi.AmlState; threshold: AmountJson; comment: string; }; export function CaseUpdate({ account, type: formId, }: { account: string; type: string; }): VNode { const { i18n } = useTranslationContext(); const officer = useOfficer(); const { lib: { exchange: api }, } = useExchangeApiContext(); const [notification, withErrorHandler] = useLocalNotificationHandler(); const { config } = useExchangeApiContext(); const { forms } = useUiFormsContext(); const initial: FormType = { when: AbsoluteTime.now(), state: TalerExchangeApi.AmlState.pending, threshold: Amounts.zeroOfCurrency(config.currency), comment: "", }; if (officer.state !== "ready") { return ; } const theForm = searchForm(i18n, forms, formId); if (!theForm) { return
form with id {formId} not found
; } const shape: Array = []; const requiredFields: Array = []; theForm.config.design.forEach((section) => { Array.prototype.push.apply(shape, getShapeFromFields(section.fields)); Array.prototype.push.apply( requiredFields, getRequiredFields(section.fields), ); }); const [form, state] = useFormState(shape, initial, (st) => { const partialErrors = undefinedIfEmpty>({ state: st.state === undefined ? i18n.str`required` : undefined, threshold: !st.threshold ? i18n.str`required` : undefined, when: !st.when ? i18n.str`required` : undefined, }); const errors = undefinedIfEmpty | undefined>( validateRequiredFields(partialErrors, st, requiredFields), ); if (errors === undefined) { return { status: "ok", result: st as any, errors: undefined, }; } return { status: "fail", result: st as any, errors, }; }); const validatedForm = state.status !== "ok" ? undefined : state.result; const submitHandler = validatedForm === undefined ? undefined : withErrorHandler( () => { const justification: Justification = { id: theForm.id, label: theForm.label, version: theForm.version, value: validatedForm, }; const decision: Omit = { justification: JSON.stringify(justification), decision_time: TalerProtocolTimestamp.now(), h_payto: account, new_state: justification.value .state as TalerExchangeApi.AmlState, new_threshold: Amounts.stringify( justification.value.threshold as AmountJson, ), kyc_requirements: undefined, }; return api.addDecisionDetails(officer.account, decision); }, () => { window.location.href = privatePages.cases.url({}); }, (fail) => { switch (fail.case) { case HttpStatusCode.Forbidden: case HttpStatusCode.Unauthorized: return i18n.str`Wrong credentials for "${officer.account}"`; case HttpStatusCode.NotFound: return i18n.str`Officer or account not found`; case HttpStatusCode.Conflict: return i18n.str`Officer disabled or more recent decision was already submitted.`; default: assertUnreachable(fail); } }, ); return (
{theForm.config.design.map((section, i) => { if (!section) return ; return (

{section.title}

{section.description && (

{section.description}

)}
); })}
Cancel
); } export function SelectForm({ account }: { account: string }) { const { i18n } = useTranslationContext(); const { forms } = useUiFormsContext(); const pf = preloadedForms(i18n); return (
New form for account: {account.substring(0, 16)}...
{forms.map((form) => { return ( {form.label} ); })} {pf.map((form) => { return ( {form.label} ); })}
); }