diff options
author | Sebastian <sebasjm@gmail.com> | 2024-11-14 13:37:49 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2024-11-14 13:37:49 -0300 |
commit | cd09d8c37cabf70358c3e02c64f83c16375d44a7 (patch) | |
tree | 5bfbf4781d028c0d959fb90743801e8e37c7a742 | |
parent | 567a9617cee2dbb5001040f06a5e40db222d4666 (diff) |
ui issues
-rw-r--r-- | packages/aml-backoffice-ui/src/pages/CaseDetails.tsx | 993 |
1 files changed, 514 insertions, 479 deletions
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx index d07c991a8..ed29fc067 100644 --- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx +++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx @@ -184,11 +184,22 @@ export function getEventsFromAmlHistory( return ke.sort(selectSooner); } -type NewDecision = { request: Omit<Omit<AmlDecisionRequest, "justification">, "officer_sig">, askInformation: boolean } +type NewDecision = { + request: Omit<Omit<AmlDecisionRequest, "justification">, "officer_sig">; + askInformation: boolean; +}; -export function CaseDetails({ account, paytoString }: { account: string, paytoString?: PaytoString }) { +export function CaseDetails({ + account, + paytoString, +}: { + account: string; + paytoString?: PaytoString; +}) { const [selected, setSelected] = useState<AbsoluteTime>(AbsoluteTime.now()); - const [request, setDesicionRequest] = useState<NewDecision | undefined>(undefined) + const [request, setDesicionRequest] = useState<NewDecision | undefined>( + undefined, + ); const { config } = useExchangeApiContext(); const { i18n } = useTranslationContext(); @@ -237,12 +248,15 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt const events = getEventsFromAmlHistory(accountDetails, i18n, allForms); - - if (request) { - return <SubmitNewDecision decision={request} onComplete={() => { - setDesicionRequest(undefined) - }} /> + return ( + <SubmitNewDecision + decision={request} + onComplete={() => { + setDesicionRequest(undefined); + }} + /> + ); } return ( @@ -287,7 +301,7 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt }, }, 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" > @@ -358,7 +372,7 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt h_payto: account, keep_investigating: false, properties: { - "pepe": "text", + pepe: "text", }, new_measures: "ask", new_rules: { @@ -382,8 +396,8 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt {!activeDecision ? ( <Attention title={i18n.str`No active rules found`} type="warning" /> ) : ( - <Fragment> - <h1 class="my-4 text-base font-semibold leading-6 text-black"> + <div class="my-4"> + <h1 class="mb-4 text-base font-semibold leading-6 text-black"> <i18n.Translate>Current active rules</i18n.Translate> </h1> <ShowDecisionLimitInfo @@ -395,16 +409,23 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt )} justification={activeDecision.justification} ruleSet={activeDecision.limits} - - startOpen /> - </Fragment> + startOpen + /> + </div> )} - <h1 class="my-4 text-base font-semibold leading-6 text-black"> - <i18n.Translate>KYC collection events</i18n.Translate> - </h1> - {events.length === 0 ? - <Attention title={i18n.str`No events found`} type="warning" /> - : + <div class="px-4 sm:px-0"> + <h1 class="text-base font-semibold leading-6 text-black"> + <i18n.Translate>KYC collection events</i18n.Translate> + </h1> + <p class="mt-1 text-sm leading-6 text-gray-600"> + <i18n.Translate> + Every event when the user was asked information. + </i18n.Translate> + </p> + </div> + {events.length === 0 ? ( + <Attention title={i18n.str`The event list is empty`} type="warning" /> + ) : ( <ShowTimeline history={events} onSelect={(e) => { @@ -423,88 +444,101 @@ export function CaseDetails({ account, paytoString }: { account: string, paytoSt } }} /> - } + )} {/* {selected && <ShowEventDetails event={selected} />} */} {selected && <ShowConsolidated history={events} until={selected} />} {restDecisions.length > 0 ? ( - <Fragment> - <h1 class="my-4 text-base font-semibold leading-6 text-black"> + <div class="my-4 grid gap-y-4"> + <h1 class="text-base font-semibold leading-6 text-black"> <i18n.Translate>Previous AML decisions</i18n.Translate> </h1> {restDecisions.map((d) => { - return <ShowDecisionLimitInfo since={AbsoluteTime.fromProtocolTimestamp( - d.decision_time, - )} - until={AbsoluteTime.fromProtocolTimestamp( - d.limits.expiration_time, - )} - justification={d.justification} - ruleSet={d.limits} - />; + return ( + <ShowDecisionLimitInfo + since={AbsoluteTime.fromProtocolTimestamp(d.decision_time)} + until={AbsoluteTime.fromProtocolTimestamp( + d.limits.expiration_time, + )} + justification={d.justification} + ruleSet={d.limits} + /> + ); })} - </Fragment> - ) : ( - !activeDecision ? - <div class="ty-4"> - <Attention title={i18n.str`No aml history found`} type="warning" /> - </div> : undefined - )} + </div> + ) : !activeDecision ? ( + <div class="ty-4"> + <Attention title={i18n.str`No aml history found`} type="warning" /> + </div> + ) : undefined} </div> ); } - -function SubmitNewDecision({ decision, onComplete }: { onComplete: () => void; decision: NewDecision }): VNode { +function SubmitNewDecision({ + decision, + onComplete, +}: { + onComplete: () => void; + decision: NewDecision; +}): VNode { const { i18n } = useTranslationContext(); const { lib } = useExchangeApiContext(); const [notification, withErrorHandler] = useLocalNotificationHandler(); - const formDesign: UIFormElementConfig[] = [{ - id: "justification" as UIHandlerId, - type: "textArea", - required: true, - label: i18n.str`Justification`, - }] + const formDesign: UIFormElementConfig[] = [ + { + id: "justification" as UIHandlerId, + type: "textArea", + required: true, + label: i18n.str`Justification`, + }, + ]; if (decision.askInformation) { - formDesign.push({ + 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`, - }] - }], + 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, fields: object }>( + const decisionForm = useFormState<{ justification: string; fields: object }>( getShapeFromFields(formDesign), { justification: "" }, (d) => { @@ -512,8 +546,8 @@ function SubmitNewDecision({ decision, onComplete }: { onComplete: () => void; d return { status: "ok", errors: undefined, - result: d as any - } + result: d as any, + }; }, ); @@ -521,100 +555,98 @@ function SubmitNewDecision({ decision, onComplete }: { onComplete: () => void; d decisionForm === undefined || !session ? undefined : withErrorHandler( - () => { - 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, - (fail) => { - switch (fail.case) { - case HttpStatusCode.Forbidden: - if (session) { - return i18n.str`Wrong credentials for "${session}"`; - } else { - return i18n.str`Wrong credentials.`; - } - case HttpStatusCode.NotFound: - return i18n.str`The account was not found`; - case HttpStatusCode.Conflict: - return i18n.str`Officer disabled or more recent decision was already submitted.`; - default: - assertUnreachable(fail); - } - }, - ); - - - - return <div> - <LocalNotificationBanner notification={notification} /> - <h1 class="my-2 text-3xl font-bold tracking-tight text-gray-900 "> - <i18n.Translate>Submit decision</i18n.Translate> - </h1> - <form - class="space-y-6" - noValidate - onSubmit={(e) => { - e.preventDefault(); - }} - autoCapitalize="none" - autoCorrect="off" - > - <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3"> - <RenderAllFieldsByUiConfig - fields={convertUiField( - i18n, - formDesign, - decisionForm.handler, - getConverterById, - )} - /> - </div> - - <div class="mt-6 flex items-center justify-end gap-x-6"> - <button - onClick={onComplete} - class="text-sm font-semibold leading-6 text-gray-900" - > - <i18n.Translate>Cancel</i18n.Translate> - </button> - - <Button - type="submit" - handler={submitHandler} - disabled={!submitHandler} - class="disabled:opacity-50 disabled:cursor-default rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" - > - <i18n.Translate>Confirm</i18n.Translate> - </Button> - </div> + () => { + 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, + (fail) => { + switch (fail.case) { + case HttpStatusCode.Forbidden: + if (session) { + return i18n.str`Wrong credentials for "${session}"`; + } else { + return i18n.str`Wrong credentials.`; + } + case HttpStatusCode.NotFound: + return i18n.str`The account was not found`; + case HttpStatusCode.Conflict: + return i18n.str`Officer disabled or more recent decision was already submitted.`; + default: + assertUnreachable(fail); + } + }, + ); - </form> + return ( + <div> + <LocalNotificationBanner notification={notification} /> + <h1 class="my-2 text-3xl font-bold tracking-tight text-gray-900 "> + <i18n.Translate>Submit decision</i18n.Translate> + </h1> + <form + class="space-y-6" + noValidate + onSubmit={(e) => { + e.preventDefault(); + }} + autoCapitalize="none" + autoCorrect="off" + > + <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3"> + <RenderAllFieldsByUiConfig + fields={convertUiField( + i18n, + formDesign, + decisionForm.handler, + getConverterById, + )} + /> + </div> - <h1 class="my-2 text-xl font-bold tracking-tight text-gray-900 "> - <i18n.Translate>New rules to submit</i18n.Translate> - </h1> + <div class="mt-6 flex items-center justify-end gap-x-6"> + <button + onClick={onComplete} + class="text-sm font-semibold leading-6 text-gray-900" + > + <i18n.Translate>Cancel</i18n.Translate> + </button> - <ShowDecisionLimitInfo - since={AbsoluteTime.fromProtocolTimestamp( - decision.request.decision_time, - )} - until={AbsoluteTime.fromProtocolTimestamp( - decision.request.new_rules.expiration_time, - )} - ruleSet={decision.request.new_rules} - startOpen - /> + <Button + type="submit" + handler={submitHandler} + disabled={!submitHandler} + class="disabled:opacity-50 disabled:cursor-default rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600" + > + <i18n.Translate>Confirm</i18n.Translate> + </Button> + </div> + </form> - </div> + <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( + decision.request.decision_time, + )} + until={AbsoluteTime.fromProtocolTimestamp( + decision.request.new_rules.expiration_time, + )} + ruleSet={decision.request.new_rules} + startOpen + /> + </div> + ); } function ShowDecisionLimitInfo({ @@ -622,9 +654,8 @@ function ShowDecisionLimitInfo({ since, until, startOpen, - justification + justification, }: { - since: AbsoluteTime; until: AbsoluteTime; justification?: string; @@ -635,52 +666,43 @@ function ShowDecisionLimitInfo({ const { config } = useExchangeApiContext(); const [opened, setOpened] = useState(startOpen ?? false); - function Header() { return ( - <ul - role="list" - class="divide-y divide-gray-100 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl" + <div + class="p-4 relative bg-gray-50 flex justify-between cursor-pointer" + onClick={() => setOpened((o) => !o)} > - <li class="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6"> - <div class="flex min-w-0 gap-x-4"> - <div class="flex mt-2 rounded-md shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600"> - <div class="pointer-events-none bg-gray-200 inset-y-0 flex items-center px-3"> - <i18n.Translate>Since</i18n.Translate> - </div> - <div class="p-2 disabled:bg-gray-200 text-right rounded-md rounded-l-none data-[left=true]:text-left w-full py-1.5 pl-3 text-gray-900 placeholder:text-gray-400 sm:text-sm sm:leading-6"> - <Time - format="dd/MM/yyyy HH:mm:ss" - timestamp={since} - /> - </div> + <div class="flex min-w-0 gap-x-4"> + <div class="flex rounded-md shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600"> + <div class="pointer-events-none bg-gray-200 inset-y-0 flex items-center px-3"> + <i18n.Translate>Since</i18n.Translate> + </div> + <div class="p-2 disabled:bg-gray-200 text-right rounded-md rounded-l-none data-[left=true]:text-left w-full py-1.5 pl-3 text-gray-900 placeholder:text-gray-400 sm:text-sm sm:leading-6"> + <Time format="dd/MM/yyyy HH:mm:ss" timestamp={since} /> </div> </div> - <div class="flex shrink-0 items-center gap-x-4"> - <div class="flex mt-2 rounded-md shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600"> - <div class="pointer-events-none bg-gray-200 inset-y-0 flex items-center px-3"> - {AbsoluteTime.isExpired(until) ? ( - <i18n.Translate>Expired</i18n.Translate> - ) : ( - <i18n.Translate>Expires</i18n.Translate> - )} - </div> - <div class="p-2 disabled:bg-gray-200 text-right rounded-md rounded-l-none data-[left=true]:text-left w-full py-1.5 pl-3 text-gray-900 placeholder:text-gray-400 sm:text-sm sm:leading-6"> - <Time - format="dd/MM/yyyy HH:mm:ss" - timestamp={until} - /> - </div> + </div> + <div class="flex shrink-0 items-center gap-x-4"> + <div class="flex rounded-md shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600"> + <div class="pointer-events-none bg-gray-200 inset-y-0 flex items-center px-3"> + {AbsoluteTime.isExpired(until) ? ( + <i18n.Translate>Expired</i18n.Translate> + ) : ( + <i18n.Translate>Expires</i18n.Translate> + )} + </div> + <div class="p-2 disabled:bg-gray-200 text-right rounded-md rounded-l-none data-[left=true]:text-left w-full py-1.5 pl-3 text-gray-900 placeholder:text-gray-400 sm:text-sm sm:leading-6"> + <Time format="dd/MM/yyyy HH:mm:ss" timestamp={until} /> </div> </div> - </li> - </ul> + </div> + </div> ); } if (!opened) { return ( - <div class="mt-4 cursor-pointer" onClick={() => setOpened(true)}> + <div class="overflow-hidden ring-1 ring-gray-900/5 rounded-xl"> <Header /> </div> ); @@ -690,101 +712,114 @@ function ShowDecisionLimitInfo({ ); return ( - <div> - <div class="mt-4 cursor-pointer" onClick={() => setOpened(false)}> + <div class="overflow-hidden ring-1 ring-gray-900/5 rounded-xl"> <Header /> - </div> + <div class="p-4 grid gap-y-4"> + {!justification ? undefined : ( + <div class=""> + <label + for="comment" + class="block text-sm font-medium leading-6 text-gray-900" + > + <i18n.Translate>AML officer justification</i18n.Translate> + </label> + <div class="mt-2"> + <textarea + rows={2} + readOnly + name="comment" + id="comment" + class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" + > + {justification} + </textarea> + </div> + </div> + )} - {!justification ? undefined : ( - <div class="mt-4"> - <label - for="comment" - class="block text-sm font-medium leading-6 text-gray-900" - > - <i18n.Translate>AML officer justification</i18n.Translate> - </label> - <div class="mt-2"> - <textarea - rows={2} - readOnly - name="comment" - id="comment" - class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" - > - {justification} - </textarea> + <div class=""> + <div class="flex mt-2 rounded-md w-fit shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600"> + <div class="whitespace-nowrap pointer-events-none bg-gray-200 inset-y-0 items-center px-3 flex"> + <i18n.Translate>Max balance</i18n.Translate> + </div> + <div class="p-2 disabled:bg-gray-200 text-right rounded-md rounded-l-none data-[left=true]:text-left py-1.5 pl-3 text-gray-900 placeholder:text-gray-400 sm:text-sm sm:leading-6"> + {!balanceLimit ? ( + <i18n.Translate>Unlimited</i18n.Translate> + ) : ( + <RenderAmount + value={Amounts.parseOrThrow(balanceLimit.threshold)} + spec={config.currency_specification} + /> + )} + </div> + </div> </div> - </div> - )} - {!balanceLimit ? undefined : ( - <div class="px-4"> - <div class="flex mt-2 rounded-md w-fit shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600"> - <div class="whitespace-nowrap pointer-events-none bg-gray-200 inset-y-0 items-center px-3 flex"> - <i18n.Translate>Max balance</i18n.Translate> + {!ruleSet.rules.length ? ( + <Attention + title={i18n.str`There are no rules for operations`} + type="warning" + /> + ) : ( + <div class=""> + <table class="min-w-full divide-y divide-gray-300"> + <thead class="bg-gray-50"> + <tr> + <th + scope="col" + class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6" + > + <i18n.Translate>Operation</i18n.Translate> + </th> + <th + scope="col" + class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900" + > + <i18n.Translate>Timeframe</i18n.Translate> + </th> + <th + scope="col" + class="relative py-3.5 pl-3 pr-4 sm:pr-6 text-right" + > + <i18n.Translate>Amount</i18n.Translate> + </th> + </tr> + </thead> + <tbody class="divide-y divide-gray-200"> + {ruleSet.rules.map((r) => { + if (r.operation_type === "BALANCE") return; + return ( + <tr> + <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 text-left"> + {r.operation_type} + </td> + <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"> + {r.timeframe.d_us === "forever" ? ( + <i18n.Translate>Forever</i18n.Translate> + ) : ( + formatDuration( + intervalToDuration({ + start: 0, + end: r.timeframe.d_us / 1000, + }), + ) + )} + </td> + <td class=" relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 text-right"> + <RenderAmount + value={Amounts.parseOrThrow(r.threshold)} + spec={config.currency_specification} + /> + </td> + </tr> + ); + })} + </tbody> + </table> </div> - <div class="p-2 disabled:bg-gray-200 text-right rounded-md rounded-l-none data-[left=true]:text-left py-1.5 pl-3 text-gray-900 placeholder:text-gray-400 sm:text-sm sm:leading-6"> - <RenderAmount - value={Amounts.parseOrThrow(balanceLimit.threshold)} - spec={config.currency_specification} - /> - </div> - </div> + )} </div> - )} - - <div class="w-full px-4 pt-4"> - <table class="min-w-full divide-y divide-gray-300"> - <thead class="bg-gray-50"> - <tr> - <th - scope="col" - class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6" - > - <i18n.Translate>Operation</i18n.Translate> - </th> - <th - scope="col" - class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900" - > - <i18n.Translate>Timeframe</i18n.Translate> - </th> - <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6"> - <i18n.Translate>Amount</i18n.Translate> - </th> - </tr> - </thead> - <tbody class="divide-y divide-gray-200"> - {ruleSet.rules.map((r) => { - if (r.operation_type === "BALANCE") return; - return ( - <tr> - <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 text-left"> - {r.operation_type} - </td> - <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500"> - {r.timeframe.d_us === "forever" - ? "" - : formatDuration( - intervalToDuration({ - start: 0, - end: r.timeframe.d_us / 1000, - }), - )} - </td> - <td class=" relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 text-right"> - <RenderAmount - value={Amounts.parseOrThrow(r.threshold)} - spec={config.currency_specification} - /> - </td> - </tr> - ); - })} - </tbody> - </table> </div> - </div> ); } @@ -1005,13 +1040,13 @@ function InputAmount( if ( sep_pos !== -1 && l - sep_pos - 1 > - config.currency_specification.num_fractional_input_digits + config.currency_specification.num_fractional_input_digits ) { e.currentTarget.value = e.currentTarget.value.substring( 0, sep_pos + - config.currency_specification.num_fractional_input_digits + - 1, + config.currency_specification.num_fractional_input_digits + + 1, ); } onChange(e.currentTarget.value); @@ -1049,9 +1084,9 @@ function parseJustification( listOfAllKnownForms: FormMetadata[], ): | OperationOk<{ - justification: Justification; - metadata: FormMetadata; - }> + justification: Justification; + metadata: FormMetadata; + }> | OperationFail<ParseJustificationFail> { try { const justification = JSON.parse(s); @@ -1099,212 +1134,212 @@ function parseJustification( const THRESHOLD_2000_WEEK: (currency: string) => TalerExchangeApi.KycRule[] = ( currency, ) => [ - { - operation_type: "WITHDRAW", - threshold: `${currency}:2000`, - timeframe: { - d_us: 7 * 24 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + { + operation_type: "WITHDRAW", + threshold: `${currency}:2000`, + timeframe: { + d_us: 7 * 24 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "DEPOSIT", - threshold: `${currency}:2000`, - timeframe: { - d_us: 7 * 24 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "DEPOSIT", + threshold: `${currency}:2000`, + timeframe: { + d_us: 7 * 24 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "AGGREGATE", - threshold: `${currency}:2000`, - timeframe: { - d_us: 7 * 24 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "AGGREGATE", + threshold: `${currency}:2000`, + timeframe: { + d_us: 7 * 24 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "MERGE", - threshold: `${currency}:2000`, - timeframe: { - d_us: 7 * 24 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "MERGE", + threshold: `${currency}:2000`, + timeframe: { + d_us: 7 * 24 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "BALANCE", - threshold: `${currency}:2000`, - timeframe: { - d_us: 7 * 24 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "BALANCE", + threshold: `${currency}:2000`, + timeframe: { + d_us: 7 * 24 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "CLOSE", - threshold: `${currency}:2000`, - timeframe: { - d_us: 7 * 24 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "CLOSE", + threshold: `${currency}:2000`, + timeframe: { + d_us: 7 * 24 * 60 * 60 * 1000 * 1000, }, - ]; + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, +]; const THRESHOLD_100_HOUR: (currency: string) => TalerExchangeApi.KycRule[] = ( currency, ) => [ - { - operation_type: "WITHDRAW", - threshold: `${currency}:100`, - timeframe: { - d_us: 1 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + { + operation_type: "WITHDRAW", + threshold: `${currency}:100`, + timeframe: { + d_us: 1 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "DEPOSIT", - threshold: `${currency}:100`, - timeframe: { - d_us: 1 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "DEPOSIT", + threshold: `${currency}:100`, + timeframe: { + d_us: 1 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "AGGREGATE", - threshold: `${currency}:100`, - timeframe: { - d_us: 1 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "AGGREGATE", + threshold: `${currency}:100`, + timeframe: { + d_us: 1 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "MERGE", - threshold: `${currency}:100`, - timeframe: { - d_us: 1 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "MERGE", + threshold: `${currency}:100`, + timeframe: { + d_us: 1 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "BALANCE", - threshold: `${currency}:100`, - timeframe: { - d_us: 1 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "BALANCE", + threshold: `${currency}:100`, + timeframe: { + d_us: 1 * 60 * 60 * 1000 * 1000, }, - { - operation_type: "CLOSE", - threshold: `${currency}:100`, - timeframe: { - d_us: 1 * 60 * 60 * 1000 * 1000, - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "CLOSE", + threshold: `${currency}:100`, + timeframe: { + d_us: 1 * 60 * 60 * 1000 * 1000, }, - ]; + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, +]; const FREEZE_RULES: (currency: string) => TalerExchangeApi.KycRule[] = ( currency, ) => [ - { - operation_type: "WITHDRAW", - threshold: `${currency}:0`, - timeframe: { - d_us: "forever", - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + { + operation_type: "WITHDRAW", + threshold: `${currency}:0`, + timeframe: { + d_us: "forever", }, - { - operation_type: "DEPOSIT", - threshold: `${currency}:0`, - timeframe: { - d_us: "forever", - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "DEPOSIT", + threshold: `${currency}:0`, + timeframe: { + d_us: "forever", }, - { - operation_type: "AGGREGATE", - threshold: `${currency}:0`, - timeframe: { - d_us: "forever", - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "AGGREGATE", + threshold: `${currency}:0`, + timeframe: { + d_us: "forever", }, - { - operation_type: "MERGE", - threshold: `${currency}:0`, - timeframe: { - d_us: "forever", - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "MERGE", + threshold: `${currency}:0`, + timeframe: { + d_us: "forever", }, - { - operation_type: "BALANCE", - threshold: `${currency}:0`, - timeframe: { - d_us: "forever", - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "BALANCE", + threshold: `${currency}:0`, + timeframe: { + d_us: "forever", }, - { - operation_type: "CLOSE", - threshold: `${currency}:0`, - timeframe: { - d_us: "forever", - }, - measures: ["verboten"], - display_priority: 1, - exposed: true, - is_and_combinator: true, + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, + { + operation_type: "CLOSE", + threshold: `${currency}:0`, + timeframe: { + d_us: "forever", }, - ]; + measures: ["verboten"], + display_priority: 1, + exposed: true, + is_and_combinator: true, + }, +]; |