From be11346a860fbb4b96e4992a7f19a7b2e4856334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morel=20Se=CC=81bastien?= Date: Mon, 12 Feb 2024 14:20:58 -0800 Subject: [PATCH] feat(import-app): channel validation if present in the schema --- components/crystallize-import/product.json | 43 ------------------- .../src/contracts/form-submission.ts | 1 + .../src/contracts/ui-types.ts | 1 + .../crystallize/fetch-validations-schema.ts | 2 + .../crystallize-import/src/routes/_index.tsx | 32 ++++++++------ .../src/routes/api.preflight.ts | 9 +++- .../src/ui/import/components/DataForm.tsx | 17 ++++++++ .../import/components/action-bar/Submit.tsx | 16 ++++--- 8 files changed, 57 insertions(+), 64 deletions(-) delete mode 100644 components/crystallize-import/product.json diff --git a/components/crystallize-import/product.json b/components/crystallize-import/product.json deleted file mode 100644 index 73e8fb1..0000000 --- a/components/crystallize-import/product.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "title": "Product", - "description": "A product in the catalog", - "type": "object", - "properties": { - "name": { - "description": "The name of the product", - "type": "string", - "minLength": 5, - "maxLength": 255 - }, - "components": { - "description": "The story telling", - "type": "object", - "properties": { - "title": { - "description": "The title of the story", - "type": "string", - "minLength": 5, - "maxLength": 255 - } - }, - "required": ["title"] - }, - "variants": { - "description": "The product variants", - "type": "array", - "items": { - "type": "object", - "properties": { - "sku": { - "description": "The stock keeping unit for the product", - "type": "string", - "minLength": 3, - "maxLength": 50 - } - }, - "required": ["sku"] - } - } - }, - "required": ["name", "components", "variants"] -} diff --git a/components/crystallize-import/src/contracts/form-submission.ts b/components/crystallize-import/src/contracts/form-submission.ts index 6c4cf34..2124844 100644 --- a/components/crystallize-import/src/contracts/form-submission.ts +++ b/components/crystallize-import/src/contracts/form-submission.ts @@ -5,6 +5,7 @@ export interface FormSubmission { mapping: Record; groupProductsBy?: string; doPublish: boolean; + channel?: string; validFlowStage?: string; invalidFlowStage?: string; } diff --git a/components/crystallize-import/src/contracts/ui-types.ts b/components/crystallize-import/src/contracts/ui-types.ts index 53962a4..2e761c0 100644 --- a/components/crystallize-import/src/contracts/ui-types.ts +++ b/components/crystallize-import/src/contracts/ui-types.ts @@ -77,6 +77,7 @@ export type State = { errors?: string[]; loading?: boolean; done?: boolean; + channels: Record; preflight?: { validCount: number; errorCount: number; diff --git a/components/crystallize-import/src/core.server/use-cases/crystallize/fetch-validations-schema.ts b/components/crystallize-import/src/core.server/use-cases/crystallize/fetch-validations-schema.ts index f5345ff..1ed16ef 100644 --- a/components/crystallize-import/src/core.server/use-cases/crystallize/fetch-validations-schema.ts +++ b/components/crystallize-import/src/core.server/use-cases/crystallize/fetch-validations-schema.ts @@ -9,6 +9,7 @@ type Deps = { type ValidationSchema = { schema: JSONSchemaType; validate: ValidateFunction>; + channels: string[]; }; export const fetchValidationsSchema = async ({ apiClient }: Deps): Promise>> => { @@ -102,6 +103,7 @@ export const fetchValidationsSchema = async ({ apiClient }: Deps): Promise { const api = await CrystallizeAPI(request); - try { - const shapes = await api.fetchShapes(); - const [folders, flows] = await Promise.all([api.fetchFolders(shapes), api.fetchFlows()]); - return json({ - shapes, - flows, - folders, - }); - } catch (err) { - console.error(err); - return json({ shapes: [], folders: [], flows: [] }); - } + const [shapes, validationRules] = await Promise.all([api.fetchShapes(), api.fetchValidationsSchema()]); + const channels = shapes.reduce((memo: Record, shape) => { + const ch = validationRules?.[shape.identifier]?.channels ?? []; + return { + ...memo, + [shape.identifier]: ch, + }; + }, {}); + + const [folders, flows] = await Promise.all([api.fetchFolders(shapes), api.fetchFlows()]); + + return json({ + shapes, + flows, + channels, + folders, + }); }; export default function Index() { - const { shapes, folders, flows } = useLoaderData(); + const { shapes, folders, flows, channels } = useLoaderData(); const initialState: State = { shapes, folders, @@ -32,6 +37,7 @@ export default function Index() { headers: [], attributes: [], rows: [], + channels, mapping: {}, }; diff --git a/components/crystallize-import/src/routes/api.preflight.ts b/components/crystallize-import/src/routes/api.preflight.ts index 3a49152..a8f2d42 100644 --- a/components/crystallize-import/src/routes/api.preflight.ts +++ b/components/crystallize-import/src/routes/api.preflight.ts @@ -1,5 +1,6 @@ import { JSONItem } from '@crystallize/import-utilities'; import { type ActionFunctionArgs, json } from '@remix-run/node'; +import { FormSubmission } from '~/contracts/form-submission'; import { specFromFormSubmission } from '~/core.server/spec-from-form-submission.server'; import CrystallizeAPI from '~/core.server/use-cases/crystallize'; @@ -15,7 +16,8 @@ export const action = async ({ request }: ActionFunctionArgs) => { const api = await CrystallizeAPI(request); const [validationRules, shapes] = await Promise.all([api.fetchValidationsSchema(), api.fetchShapes()]); try { - const spec = await specFromFormSubmission(await request.json(), shapes); + const submission: FormSubmission = await request.json(); + const spec = await specFromFormSubmission(submission, shapes); const items = spec.items ?? []; const results = items.reduce( @@ -27,7 +29,10 @@ export const action = async ({ request }: ActionFunctionArgs) => { validCount: memo.validCount + 1, }; } - const valid = validate(item); + const valid = validate({ + ...item, + ...(submission.channel ? { channel: submission.channel } : {}), + }); if (!valid) { return { diff --git a/components/crystallize-import/src/ui/import/components/DataForm.tsx b/components/crystallize-import/src/ui/import/components/DataForm.tsx index 13da635..6fb3c65 100644 --- a/components/crystallize-import/src/ui/import/components/DataForm.tsx +++ b/components/crystallize-import/src/ui/import/components/DataForm.tsx @@ -5,10 +5,13 @@ import { PiAirplaneInFlightDuotone } from 'react-icons/pi'; import { useImport } from '../provider'; import { ColumnHeader } from './data-grid/ColumnHeader'; import { FormSubmission } from '~/contracts/form-submission'; +import { useRef } from 'react'; export const DataMatchingForm = () => { const { state, dispatch } = useImport(); + const channelRef = useRef(null); + const columnsFromRows: Column[] = state.headers.map( (header): Column => ({ ...keyColumn(header, textColumn), @@ -36,6 +39,19 @@ export const DataMatchingForm = () => {
+ {state.channels[state.selectedShape.identifier] && + state.channels[state.selectedShape.identifier].length > 0 && ( + + )}
{(state.preflight?.errorCount ?? 0) > 0 && (

You have {state.preflight?.errorCount ?? 0} errors.