@@ -49,6 +55,13 @@ const FieldCategory = ({
})}
/>
{section.title}
+ {!isAllSelected && nbSelected ? (
+
+ {t('scenarios:sanction.lists.nb_selected', {
+ count: nbSelected,
+ })}
+
+ ) : null}
@@ -89,10 +102,9 @@ const FieldCategory = ({
);
-};
+});
export const FieldSanction = ({
- name,
onChange,
onBlur,
sections,
@@ -100,7 +112,6 @@ export const FieldSanction = ({
}: {
defaultValue?: string[];
sections: OpenSanctionsCatalogSection[];
- name?: string;
onChange?: (value: string[]) => void;
onBlur?: () => void;
}) => {
@@ -113,8 +124,7 @@ export const FieldSanction = ({
}, [selectedIds, onChange]);
return (
-
-
+
{sections.map((section) => (
void;
onBlur?: () => void;
@@ -44,9 +42,8 @@ export const FieldTrigger = ({
}, [astNode, onChange]);
return (
- <>
-
+
);
};
diff --git a/packages/app-builder/src/components/Scenario/Sanction/MatchOperand.tsx b/packages/app-builder/src/components/Scenario/Sanction/MatchOperand.tsx
index f3c8f659d..583ddc271 100644
--- a/packages/app-builder/src/components/Scenario/Sanction/MatchOperand.tsx
+++ b/packages/app-builder/src/components/Scenario/Sanction/MatchOperand.tsx
@@ -3,10 +3,11 @@ import {
useGetAstNodeOperandProps,
useOperandOptions,
} from '@app-builder/services/editor/options';
+import { memo } from 'react';
import { Operand } from '../AstBuilder/AstBuilderNode/Operand';
-export const MatchOperand = ({
+export const MatchOperand = memo(function MatchOperand({
node,
onSave,
placeholder,
@@ -14,7 +15,7 @@ export const MatchOperand = ({
node: AstNode;
onSave?: (astNode: AstNode) => void;
placeholder?: string;
-}) => {
+}) {
const getOperandAstNodeOperandProps = useGetAstNodeOperandProps();
const options = useOperandOptions();
@@ -27,4 +28,4 @@ export const MatchOperand = ({
onSave={onSave}
/>
);
-};
+});
diff --git a/packages/app-builder/src/locales/ar/scenarios.json b/packages/app-builder/src/locales/ar/scenarios.json
index b17c18c43..552064075 100644
--- a/packages/app-builder/src/locales/ar/scenarios.json
+++ b/packages/app-builder/src/locales/ar/scenarios.json
@@ -393,5 +393,7 @@
"sanction.nudge": "تحسين قواعدك من خلال فحص العقوبات بناءً على واجهة برمجة تطبيقات OpenSacntion",
"sanction_counterparty_id": "معرف الطرف المقابل",
"sanction_counterparty_name": "اسم الطرف المقابل",
- "sanction_transaction_label": "تسمية المعاملة"
+ "sanction_transaction_label": "تسمية المعاملة",
+ "sanction.lists.nb_selected_one": "({{count}} المحدد)",
+ "sanction.lists.nb_selected_other": "({{count}} المحدد)"
}
diff --git a/packages/app-builder/src/locales/en/scenarios.json b/packages/app-builder/src/locales/en/scenarios.json
index c0c932f09..9bba4f029 100644
--- a/packages/app-builder/src/locales/en/scenarios.json
+++ b/packages/app-builder/src/locales/en/scenarios.json
@@ -182,6 +182,8 @@
"sanction.match_settings.callout": "Choose information that should be checked.",
"sanction.lists.title": "Sanction lists",
"sanction.lists.callout": "Select lists that are relevant to your business",
+ "sanction.lists.nb_selected_one": "({{count}} selected)",
+ "sanction.lists.nb_selected_other": "({{count}} selected)",
"sanction_transaction_label": "Transaction Label",
"sanction_counterparty_id": "Counterparty ID",
"sanction_counterparty_name": "Counterparty name",
diff --git a/packages/app-builder/src/locales/fr/scenarios.json b/packages/app-builder/src/locales/fr/scenarios.json
index 76c8d954b..7d935dad7 100644
--- a/packages/app-builder/src/locales/fr/scenarios.json
+++ b/packages/app-builder/src/locales/fr/scenarios.json
@@ -393,5 +393,7 @@
"sanction.nudge": "Améliorez vos règles avec un chèque de sanction basé sur l'OpenSacntion API",
"sanction_counterparty_id": "Identifiant de contrepartie",
"sanction_counterparty_name": "Nom de contrepartie",
- "sanction_transaction_label": "Étiquette de transaction"
+ "sanction_transaction_label": "Étiquette de transaction",
+ "sanction.lists.nb_selected_one": "({{count}} sélectionnée)",
+ "sanction.lists.nb_selected_other": "({{count}} sélectionnées)"
}
diff --git a/packages/app-builder/src/routes/_builder+/scenarios+/$scenarioId+/i+/$iterationId+/sanction.tsx b/packages/app-builder/src/routes/_builder+/scenarios+/$scenarioId+/i+/$iterationId+/sanction.tsx
index 83816097f..7be322bfd 100644
--- a/packages/app-builder/src/routes/_builder+/scenarios+/$scenarioId+/i+/$iterationId+/sanction.tsx
+++ b/packages/app-builder/src/routes/_builder+/scenarios+/$scenarioId+/i+/$iterationId+/sanction.tsx
@@ -11,7 +11,7 @@ import { FormLabel } from '@app-builder/components/Form/Tanstack/FormLabel';
import { setToastMessage } from '@app-builder/components/MarbleToaster';
import { type AstBuilderProps } from '@app-builder/components/Scenario/AstBuilder';
import { FieldNode } from '@app-builder/components/Scenario/Sanction/FieldNode';
-import { FieldMatches } from '@app-builder/components/Scenario/Sanction/FieldNodeConcat';
+import { FieldNodeConcat } from '@app-builder/components/Scenario/Sanction/FieldNodeConcat';
import { FieldOutcomes } from '@app-builder/components/Scenario/Sanction/FieldOutcomes';
import { FieldRuleGroup } from '@app-builder/components/Scenario/Sanction/FieldRuleGroup';
import { FieldSanction } from '@app-builder/components/Scenario/Sanction/FieldSanction';
@@ -27,7 +27,6 @@ import { OptionsProvider } from '@app-builder/services/editor/options';
import { serverServices } from '@app-builder/services/init.server';
import { getRoute } from '@app-builder/utils/routes';
import { fromParams, fromUUID, useParam } from '@app-builder/utils/short-uuid';
-import { parseWithZod } from '@conform-to/zod';
import {
type ActionFunctionArgs,
json,
@@ -35,6 +34,7 @@ import {
} from '@remix-run/node';
import { useFetcher, useLoaderData } from '@remix-run/react';
import { useForm } from '@tanstack/react-form';
+import { decode as formDataToObject } from 'decode-formdata';
import { type Namespace } from 'i18next';
import { serialize as objectToFormData } from 'object-to-formdata';
import { useMemo } from 'react';
@@ -126,7 +126,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
const editSanctionFormSchema = z.object({
name: z.string().nonempty(),
description: z.string().optional(),
- scoreModifier: z.number(),
+ scoreModifier: z.coerce.number(),
ruleGroup: z.string().nonempty(),
forceOutcome: z.union([
z.literal('review'),
@@ -150,31 +150,32 @@ export async function action({ request, params }: ActionFunctionArgs) {
toastSessionService: { getSession, commitSession },
} = serverServices;
- const session = await getSession(request);
- const formData = await request.formData();
- const iterationId = fromParams(params, 'iterationId');
-
- const submission = parseWithZod(formData, { schema: editSanctionFormSchema });
+ const [session, formData, { scenarioIterationSanctionRepository }] =
+ await Promise.all([
+ getSession(request),
+ request.formData(),
+ authService.isAuthenticated(request, {
+ failureRedirect: getRoute('/sign-in'),
+ }),
+ ]);
- if (submission.status !== 'success') {
- return json(submission.reply());
- }
-
- const { scenarioIterationSanctionRepository } =
- await authService.isAuthenticated(request, {
- failureRedirect: getRoute('/sign-in'),
- });
+ const iterationId = fromParams(params, 'iterationId');
+ const formDataDecoded = formDataToObject(formData, {
+ arrays: ['datasets'],
+ });
try {
+ const data = editSanctionFormSchema.parse(formDataDecoded);
+
await scenarioIterationSanctionRepository.upsertSanctionCheckConfig({
iterationId,
changes: {
- ...submission.value,
- counterPartyId: submission.value.counterPartyId as AstNode | undefined,
- triggerRule: submission.value.triggerRule as AstNode | undefined,
+ ...data,
+ counterPartyId: data.counterPartyId as AstNode | undefined,
+ triggerRule: data.triggerRule as AstNode | undefined,
query: {
- name: submission.value.query.name as AstNode,
- label: submission.value.query.label as AstNode | undefined,
+ name: data.query.name as AstNode,
+ label: data.query.label as AstNode | undefined,
},
},
});
@@ -455,7 +456,6 @@ export default function SanctionDetail() {
options={options}
onBlur={field.handleBlur}
onChange={field.handleChange}
- name={field.name}
trigger={field.state.value}
/>
)}
@@ -482,7 +482,6 @@ export default function SanctionDetail() {
{t('scenarios:sanction_counterparty_id')}
{t('scenarios:sanction_counterparty_name')}
-
(
;
@@ -44,24 +44,22 @@ export async function action({ request }: LoaderFunctionArgs) {
toastSessionService: { getSession, commitSession },
} = serverServices;
- const session = await getSession(request);
- const formData = await request.formData();
+ const [session, formData, { organization: repository }] = await Promise.all([
+ getSession(request),
+ request.formData(),
+ authService.isAuthenticated(request, {
+ failureRedirect: getRoute('/sign-in'),
+ }),
+ ]);
- const submission = parseWithZod(formData, { schema: editOrganizationSchema });
-
- if (submission.status !== 'success') {
- return json(submission.reply());
- }
-
- const { organization: repository } = await authService.isAuthenticated(
- request,
- { failureRedirect: getRoute('/sign-in') },
- );
+ const formDataDecoded = formDataToObject(formData);
try {
+ const data = editOrganizationSchema.parse(formDataDecoded);
+
await repository.updateOrganization({
- organizationId: submission.value.organizationId,
- changes: submission.value,
+ organizationId: data.organizationId,
+ changes: data,
});
setToastMessage(session, {