taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit a8542fb65a0676ae55d74b49a9dda573accec82b
parent 9a7dee809ec56bdc2aa4b33c425e3f5970692bc8
Author: Sebastian <sebasjm@gmail.com>
Date:   Wed,  9 Oct 2024 11:14:28 -0300

simple form with name and type of field

Diffstat:
Mpackages/aml-backoffice-ui/src/pages/CaseDetails.tsx | 194++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mpackages/aml-backoffice-ui/src/pages/Search.tsx | 2+-
Mpackages/web-util/src/forms/Caption.tsx | 2+-
Mpackages/web-util/src/forms/InputArray.tsx | 2+-
4 files changed, 130 insertions(+), 70 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx @@ -184,9 +184,11 @@ export function getEventsFromAmlHistory( return ke.sort(selectSooner); } +type NewDecision = { request: Omit<Omit<AmlDecisionRequest, "justification">, "officer_sig">, askInformation: boolean } + export function CaseDetails({ account, paytoString }: { account: string, paytoString?: PaytoString }) { const [selected, setSelected] = useState<AbsoluteTime>(AbsoluteTime.now()); - const [request, setDesicionRequest] = useState<Omit<AmlDecisionRequest, "officer_sig"> | undefined>(undefined) + const [request, setDesicionRequest] = useState<NewDecision | undefined>(undefined) const { config } = useExchangeApiContext(); const { i18n } = useTranslationContext(); @@ -238,7 +240,7 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt if (request) { - return <SubmitNewDecision request={request} onComplete={() => { + return <SubmitNewDecision decision={request} onComplete={() => { setDesicionRequest(undefined) }} /> } @@ -267,22 +269,24 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt <button onClick={async () => { setDesicionRequest({ - payto_uri: paytoString, - decision_time: AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.now(), - ), - h_payto: account, - justification: "", - keep_investigating: false, - properties: {}, - new_rules: { - custom_measures: {}, - expiration_time: AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.never(), + request: { + payto_uri: paytoString, + decision_time: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.now(), ), - rules: FREEZE_RULES(config.currency), - successor_measure: "verboten", + h_payto: account, + keep_investigating: false, + properties: {}, + new_rules: { + custom_measures: {}, + expiration_time: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.never(), + ), + rules: FREEZE_RULES(config.currency), + successor_measure: "verboten", + }, }, + askInformation: false, }) }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700" @@ -292,22 +296,24 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt <button onClick={async () => { setDesicionRequest({ - payto_uri: paytoString, - decision_time: AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.now(), - ), - h_payto: account, - justification: "", - keep_investigating: false, - properties: {}, - new_rules: { - custom_measures: {}, - expiration_time: AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.never(), + request: { + payto_uri: paytoString, + decision_time: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.now(), ), - rules: THRESHOLD_100_HOUR(config.currency), - successor_measure: "verboten", + h_payto: account, + keep_investigating: false, + properties: {}, + new_rules: { + custom_measures: {}, + expiration_time: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.never(), + ), + rules: THRESHOLD_100_HOUR(config.currency), + successor_measure: "verboten", + }, }, + askInformation: false, }); }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700" @@ -317,22 +323,24 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt <button onClick={async () => { setDesicionRequest({ - payto_uri: paytoString, - decision_time: AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.now(), - ), - h_payto: account, - justification: "", - keep_investigating: false, - properties: {}, - new_rules: { - custom_measures: {}, - expiration_time: AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.never(), + request: { + payto_uri: paytoString, + decision_time: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.now(), ), - rules: THRESHOLD_2000_WEEK(config.currency), - successor_measure: "verboten", + h_payto: account, + keep_investigating: false, + properties: {}, + new_rules: { + custom_measures: {}, + expiration_time: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.never(), + ), + rules: THRESHOLD_2000_WEEK(config.currency), + successor_measure: "verboten", + }, }, + askInformation: false, }); }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700" @@ -342,23 +350,27 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt <button onClick={async () => { setDesicionRequest({ - payto_uri: paytoString, - decision_time: AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.now(), - ), - h_payto: account, - justification: "", - keep_investigating: false, - properties: {}, - new_measures: "m2", - new_rules: { - custom_measures: {}, - expiration_time: AbsoluteTime.toProtocolTimestamp( - AbsoluteTime.never(), + request: { + payto_uri: paytoString, + decision_time: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.now(), ), - rules: FREEZE_RULES(config.currency), - successor_measure: "verboten", + h_payto: account, + keep_investigating: false, + properties: { + "pepe": "text", + }, + new_measures: "ask", + new_rules: { + custom_measures: {}, + expiration_time: AbsoluteTime.toProtocolTimestamp( + AbsoluteTime.never(), + ), + rules: FREEZE_RULES(config.currency), + successor_measure: "verboten", + }, }, + askInformation: true, }); }} class="m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700" @@ -442,7 +454,7 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt } -function SubmitNewDecision({ request, onComplete }: { onComplete: () => void; request: Omit<AmlDecisionRequest, "officer_sig"> }): VNode { +function SubmitNewDecision({ decision, onComplete }: { onComplete: () => void; decision: NewDecision }): VNode { const { i18n } = useTranslationContext(); const { lib } = useExchangeApiContext(); const [notification, withErrorHandler] = useLocalNotificationHandler(); @@ -453,9 +465,46 @@ function SubmitNewDecision({ request, onComplete }: { onComplete: () => void; re required: true, label: i18n.str`Justification`, }] + + if (decision.askInformation) { + formDesign.push({ + type: "caption", + label: i18n.str`Form definition`, + help: i18n.str`The user will need to complete this form.`, + }) + formDesign.push({ + id: "fields" as UIHandlerId, + type: "array", + required: true, + label: i18n.str`Fields`, + fields: [{ + id: "name" as UIHandlerId, + type: "text", + required: true, + label: i18n.str`Name`, + help:i18n.str`Name of the field in the form`, + },{ + id: "type" as UIHandlerId, + type: "choiceStacked", + required: true, + label: i18n.str`Type`, + help:i18n.str`Type of information being asked`, + choices: [{ + value: "number", + label: i18n.str`Number`, + description:i18n.str`Numeric information`, + },{ + value: "text", + label: i18n.str`Text`, + description:i18n.str`Free form text input`, + }] + }], + labelFieldId: "name" as UIHandlerId, + }) + } const officer = useOfficer(); const session = officer.state === "ready" ? officer.account : undefined; - const decisionForm = useFormState<{ justification: string }>( + const decisionForm = useFormState<{ justification: string, fields: object }>( getShapeFromFields(formDesign), { justification: "" }, (d) => { @@ -473,7 +522,14 @@ function SubmitNewDecision({ request, onComplete }: { onComplete: () => void; re ? undefined : withErrorHandler( () => { - request.justification = decisionForm.status.result.justification ?? "empty" + const request: Omit<AmlDecisionRequest, "officer_sig"> = { + ...decision.request, + properties: { + ...decision.request.properties, + fields: decisionForm.status.result.fields, + }, + justification: decisionForm.status.result.justification ?? "empty", + } return lib.exchange.makeAmlDesicion(session, request); }, onComplete, @@ -542,16 +598,20 @@ function SubmitNewDecision({ request, onComplete }: { onComplete: () => void; re </form> + <h1 class="my-2 text-xl font-bold tracking-tight text-gray-900 "> + <i18n.Translate>New rules to submit</i18n.Translate> + </h1> + <ShowDecisionLimitInfo since={AbsoluteTime.fromProtocolTimestamp( - request.decision_time, + decision.request.decision_time, )} until={AbsoluteTime.fromProtocolTimestamp( - request.new_rules.expiration_time, + decision.request.new_rules.expiration_time, )} - ruleSet={request.new_rules} - - startOpen /> + ruleSet={decision.request.new_rules} + startOpen + /> </div> diff --git a/packages/aml-backoffice-ui/src/pages/Search.tsx b/packages/aml-backoffice-ui/src/pages/Search.tsx @@ -369,7 +369,7 @@ function IbanForm({ class="disabled:bg-gray-100 disabled:text-gray-500 m-4 rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700" onClick={() => onSearch(paytoUri)} > - Search + <i18n.Translate>Search</i18n.Translate> </button> </form> ); diff --git a/packages/web-util/src/forms/Caption.tsx b/packages/web-util/src/forms/Caption.tsx @@ -13,7 +13,7 @@ interface Props { export function Caption({ before, after, label, tooltip, help }: Props): VNode { return ( - <div class="sm:col-span-6 flex"> + <div class="sm:col-span-6"> {before !== undefined && <RenderAddon addon={before} />} <LabelWithTooltipMaybeRequired label={label} tooltip={tooltip} /> {after !== undefined && <RenderAddon addon={after} />} diff --git a/packages/web-util/src/forms/InputArray.tsx b/packages/web-util/src/forms/InputArray.tsx @@ -128,7 +128,7 @@ export function InputArray<T extends object, K extends keyof T>( {!state.disabled && ( <div class="pt-2"> <Option - label={"Add..." as TranslatedString} + label={"Add new..." as TranslatedString} isSelected={selectedIndex === list.length} isLast isFirst