commit a2011cb08bfbea3d82b0ec680a6836a6e09c8e19
parent e9e12687488be1350f708daa7dc3f51bc4907df2
Author: Sebastian <sebasjm@gmail.com>
Date: Wed, 5 Feb 2025 14:13:12 -0300
using error summary in kyc
Diffstat:
4 files changed, 41 insertions(+), 215 deletions(-)
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -199,56 +199,6 @@ export function CaseDetails({
const events = getEventsFromAmlHistory(accountDetails, i18n, allForms);
- // if (selectMeasure) {
- // return (
- // <ShowMeasuresToSelect
- // onSelect={(d) => {
- // setSelectMeasure(false);
- // setDesicionRequest({
- // request: {
- // // payto_uri: paytoString,
- // decision_time: AbsoluteTime.toProtocolTimestamp(
- // AbsoluteTime.now(),
- // ),
- // h_payto: account,
- // keep_investigating: false,
- // properties: {},
- // // the custom measure with context
- // new_measures: d.name,
- // new_rules: {
- // // this value is going to be overridden
- // custom_measures: {},
- // expiration_time: AbsoluteTime.toProtocolTimestamp(
- // AbsoluteTime.never(),
- // ),
- // rules: FREEZE_RULES(config.config.currency),
- // },
- // },
- // askInformation: false,
- // });
- // }}
- // />
- // );
- // }
- // if (request) {
- // return (
- // <SubmitNewDecision
- // decision={request}
- // onComplete={() => {
- // setDesicionRequest(undefined);
- // }}
- // />
- // );
- // }
- // if (decisionWizardStep) {
- // return (
- // <AmlDecisionRequestWizard
- // step={decisionWizardStep}
- // onMove={(step) => setDecisionWizardStep(step)}
- // />
- // );
- // }
-
function ShortcutActionButtons(): VNode {
return (
<div>
@@ -271,126 +221,6 @@ export function CaseDetails({
>
<i18n.Translate>New decision</i18n.Translate>
</button>
- {/*
- <button
- onClick={async () => {
- setDesicionRequest({
- request: {
- // payto_uri: paytoString,
- decision_time: AbsoluteTime.toProtocolTimestamp(
- AbsoluteTime.now(),
- ),
- h_payto: account,
- keep_investigating: false,
- properties: {},
- new_rules: {
- custom_measures: {},
- expiration_time: AbsoluteTime.toProtocolTimestamp(
- AbsoluteTime.never(),
- ),
- rules: FREEZE_RULES(config.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"
- >
- <i18n.Translate>Freeze account</i18n.Translate>
- </button>
- <button
- onClick={async () => {
- setDesicionRequest({
- request: {
- // payto_uri: paytoString,
- decision_time: AbsoluteTime.toProtocolTimestamp(
- AbsoluteTime.now(),
- ),
- h_payto: account,
- keep_investigating: false,
- properties: {},
- new_rules: {
- custom_measures: {},
- expiration_time: AbsoluteTime.toProtocolTimestamp(
- AbsoluteTime.never(),
- ),
- rules: THRESHOLD_100_HOUR(config.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"
- >
- <i18n.Translate>Set threshold to 100 / hour</i18n.Translate>
- </button>
- <button
- onClick={async () => {
- setDesicionRequest({
- request: {
- // payto_uri: paytoString,
- decision_time: AbsoluteTime.toProtocolTimestamp(
- AbsoluteTime.now(),
- ),
- h_payto: account,
- keep_investigating: false,
- properties: {},
- new_rules: {
- custom_measures: {},
- expiration_time: AbsoluteTime.toProtocolTimestamp(
- AbsoluteTime.never(),
- ),
- rules: THRESHOLD_2000_WEEK(config.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"
- >
- <i18n.Translate>Set threshold to 2000 / week</i18n.Translate>
- </button>
- <button
- onClick={async () => {
- setDesicionRequest({
- request: {
- // payto_uri: paytoString,
- decision_time: AbsoluteTime.toProtocolTimestamp(
- AbsoluteTime.now(),
- ),
- h_payto: account,
- keep_investigating: false,
- properties: {},
- // the custom measure with context
- new_measures: "askMoreInfo",
- new_rules: {
- // this value is going to be overridden
- custom_measures: {},
- expiration_time: AbsoluteTime.toProtocolTimestamp(
- AbsoluteTime.never(),
- ),
- rules: FREEZE_RULES(config.config.currency),
- },
- },
- 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"
- >
- <i18n.Translate>Ask for more information</i18n.Translate>
- </button> */}
-
- {/* <button
- onClick={async () => {
- setSelectMeasure(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"
- >
- <i18n.Translate>Set predefined measure</i18n.Translate>
- </button> */}
</div>
);
}
@@ -827,15 +657,6 @@ export function ShowDecisionLimitInfo({
/>
)}
</svg>
- {/* <svg
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- stroke-width="1.5"
- stroke="currentColor"
- class="size-6 w-6 h-6"
- >
- </svg> */}
</div>
)}
</div>
diff --git a/packages/kyc-ui/src/pages/FillForm.tsx b/packages/kyc-ui/src/pages/FillForm.tsx
@@ -24,6 +24,7 @@ import {
} from "@gnu-taler/taler-util";
import {
Button,
+ ErrorsSummary,
FormMetadata,
FormUI,
InternationalizationAPI,
@@ -158,7 +159,7 @@ export function FillForm({
) : (
<Fragment />
)}
- <div class="mt-6 flex items-center justify-end gap-x-6">
+ <div class="my-6 flex items-center justify-end gap-x-6">
<button
onClick={onComplete}
class="text-sm font-semibold leading-6 text-gray-900"
@@ -174,6 +175,7 @@ export function FillForm({
<i18n.Translate>Submit</i18n.Translate>
</Button>
</div>
+ {!status.errors ? undefined : <ErrorsSummary errors={status.errors} />}
</div>
);
}
diff --git a/packages/web-util/src/forms/fields/InputSelectMultiple.tsx b/packages/web-util/src/forms/fields/InputSelectMultiple.tsx
@@ -29,6 +29,7 @@ export function InputSelectMultiple<T extends object, K extends keyof T>(
props.handler ?? noHandlerPropsAndNoContextForField(props.name);
const [filter, setFilter] = useState<string | undefined>(undefined);
+ const [dirty, setDirty] = useState<boolean>();
const regex = new RegExp(`.*${filter}.*`, "i");
const choiceMap = choices.reduce(
(prev, curr) => {
@@ -54,38 +55,6 @@ export function InputSelectMultiple<T extends object, K extends keyof T>(
tooltip={tooltip}
name={props.name as string}
/>
- {list.map((v, idx) => {
- return (
- <span
- key={idx}
- class="inline-flex items-center gap-x-0.5 rounded-md bg-gray-100 p-1 mr-2 text-xs font-medium text-gray-600"
- >
- {choiceMap[v]}
- <button
- type="button"
- disabled={state.disabled}
- onClick={() => {
- const newValue = [...list];
- newValue.splice(idx, 1);
- onChange(newValue as any);
- setFilter(undefined);
- }}
- class="group relative h-5 w-5 rounded-sm hover:bg-gray-500/20"
- >
- <span class="sr-only">
- <i18n.Translate>Remove</i18n.Translate>
- </span>
- <svg
- viewBox="0 0 14 14"
- class="h-5 w-5 stroke-gray-700/50 group-hover:stroke-gray-700/75"
- >
- <path d="M4 4l6 6m0-6l-6 6" />
- </svg>
- <span class="absolute -inset-1"></span>
- </button>
- </span>
- );
- })}
{!state.disabled && (
<div class="relative mt-2">
@@ -95,6 +64,7 @@ export function InputSelectMultiple<T extends object, K extends keyof T>(
value={filter ?? ""}
onChange={(e) => {
setFilter(e.currentTarget.value);
+ setDirty(true);
}}
placeholder={placeholder}
class="w-full rounded-md border-0 bg-white py-1.5 pl-3 pr-12 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
@@ -107,6 +77,7 @@ export function InputSelectMultiple<T extends object, K extends keyof T>(
disabled={state.disabled}
onClick={() => {
setFilter(filter === undefined ? "" : undefined);
+ setDirty(true);
}}
class="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none"
>
@@ -124,7 +95,7 @@ export function InputSelectMultiple<T extends object, K extends keyof T>(
</svg>
</button>
- {!filter ? undefined : filteredChoices === undefined ||
+ {filter === undefined ? undefined : filteredChoices === undefined ||
!filteredChoices.length ? (
<ul
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
@@ -172,6 +143,38 @@ export function InputSelectMultiple<T extends object, K extends keyof T>(
)}
</div>
)}
+ {list.map((v, idx) => {
+ return (
+ <span
+ key={idx}
+ class="inline-flex items-center gap-x-0.5 rounded-md bg-gray-100 p-1 mt-2 mr-2 text-xs font-medium text-gray-600"
+ >
+ {choiceMap[v]}
+ <button
+ type="button"
+ disabled={state.disabled}
+ onClick={() => {
+ const newValue = [...list];
+ newValue.splice(idx, 1);
+ onChange(newValue as any);
+ setFilter(undefined);
+ }}
+ class="group relative h-5 w-5 rounded-sm hover:bg-gray-500/20"
+ >
+ <span class="sr-only">
+ <i18n.Translate>Remove</i18n.Translate>
+ </span>
+ <svg
+ viewBox="0 0 14 14"
+ class="h-5 w-5 stroke-gray-700/50 group-hover:stroke-gray-700/75"
+ >
+ <path d="M4 4l6 6m0-6l-6 6" />
+ </svg>
+ <span class="absolute -inset-1"></span>
+ </button>
+ </span>
+ );
+ })}
{help && (
<p class="mt-2 text-sm text-gray-500" id="email-description">
{help}
diff --git a/packages/web-util/src/forms/forms-ui.tsx b/packages/web-util/src/forms/forms-ui.tsx
@@ -178,7 +178,7 @@ export function RenderAllFieldsByUiConfig({
);
}
-function ErrorsSummary<T>({
+export function ErrorsSummary<T>({
errors,
formName = DEFAULT_FORM_UI_NAME,
startOpen,
@@ -221,7 +221,7 @@ function ErrorsSummary<T>({
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
- class="size-4"
+ class="size-4 h-4 w-4"
>
{opened ? (
<path