Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe("getNestedSchema", () => {
innerSchema.or(z.number()),
z.union([innerSchema, z.number(), z.boolean()]),
schemaOrEmptyString(innerSchema),
z.array(innerSchema),
];
outerSchemas.forEach((outerSchema) => {
it(`should unwrap ${outerSchema.def.type}`, () => {
Expand Down
65 changes: 64 additions & 1 deletion app/services/array/__test__/getArraySummaryData.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import z from "zod";
import { getArraySummaryData } from "~/services/array/getArraySummaryData";
import { ibanSchema } from "~/services/validation/iban";

describe("getArraySummaryData", () => {
it("returns undefined when array configuration is missing", () => {
const summaryData = getArraySummaryData([], undefined, {}, []);
const summaryData = getArraySummaryData([], undefined, {}, [], {});
expect(summaryData).toBeUndefined();
});

Expand Down Expand Up @@ -35,6 +37,10 @@ describe("getArraySummaryData", () => {
kraftfahrzeuge: [{ hasArbeitsweg: "no", wert: "under10000" }],
},
[],
{
bankkonten: z.array(z.object({})),
kraftfahrzeuge: z.array(z.object({})),
},
),
).toEqual({
bankkonten: {
Expand Down Expand Up @@ -66,6 +72,10 @@ describe("getArraySummaryData", () => {
},
{ hasBankkonto: "no" },
[],
{
bankkonten: z.array(z.object({})),
kraftfahrzeuge: z.array(z.object({})),
},
),
).toEqual({});
});
Expand All @@ -78,6 +88,9 @@ describe("getArraySummaryData", () => {
hasBankkonto: "yes",
},
[],
{
bankkonten: z.array(z.object({})),
},
);

expect(actual?.bankkonten?.configuration.disableAddButton).toBe(false);
Expand All @@ -96,6 +109,9 @@ describe("getArraySummaryData", () => {
hasBankkonto: "yes",
},
[],
{
bankkonten: z.array(z.object({})),
},
);

expect(actual?.bankkonten?.configuration.disableAddButton).toBe(false);
Expand All @@ -114,6 +130,9 @@ describe("getArraySummaryData", () => {
hasBankkonto: "yes",
},
[],
{
bankkonten: z.array(z.object({})),
},
);

expect(actual?.bankkonten?.configuration.disableAddButton).toBe(true);
Expand Down Expand Up @@ -156,6 +175,9 @@ describe("getArraySummaryData", () => {
id: 0,
},
],
{
bankkonten: z.array(z.object({})),
},
);

expect(actual).toEqual({
Expand Down Expand Up @@ -185,4 +207,45 @@ describe("getArraySummaryData", () => {
},
});
});

it("should process special display fields, leaving other fields unmodified", () => {
expect(
getArraySummaryData(
["bankkonten", "kraftfahrzeuge"],
{ bankkonten: bankkontenArrayConfig, kraftfahrzeuge: kfzArrayConfig },
{
hasBankkonto: "yes",
hasKraftfahrzeug: "yes",
bankkonten: [
{
iban: "DE02100100100006820101",
},
],
kraftfahrzeuge: [{ hasArbeitsweg: "no", wert: "under10000" }],
},
[],
{
bankkonten: z.array(
z.object({
iban: ibanSchema,
}),
),
kraftfahrzeuge: z.array(z.object({})),
},
),
).toEqual({
bankkonten: {
data: [{ iban: "DE02 1001 0010 0006 8201 01" }],
configuration: { ...bankkontenArrayConfig, disableAddButton: false },
itemLabels: {},
buttonLabel: "",
},
kraftfahrzeuge: {
data: [{ hasArbeitsweg: "no", wert: "under10000" }],
configuration: { ...kfzArrayConfig, disableAddButton: false },
itemLabels: {},
buttonLabel: "",
},
});
});
});
36 changes: 34 additions & 2 deletions app/services/array/getArraySummaryData.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { type HeadingProps } from "~/components/common/Heading";
import type { ArrayData, UserData } from "~/domains/userData";
import type {
AllowedUserTypes,
ArrayData,
SchemaObject,
UserData,
} from "~/domains/userData";
import type { ArrayConfigServer, ArrayConfigClient } from ".";
import { type StrapiContentComponent } from "../cms/models/formElements/StrapiContentComponent";
import { type ZodType } from "zod";
import { isZodObject } from "~/components/formElements/schemaToForm/renderZodObject";
import { getNestedSchema } from "~/components/formElements/schemaToForm/getNestedSchema";
import { processSpecialFieldDisplay } from "~/services/processSpecialFieldDisplay";

export type ItemLabels = Record<string, string>;

Expand All @@ -20,11 +29,32 @@ export type ArraySummaryData =
>
| undefined;

function encodeSpecialFields(
inputArray: ArrayData,
arraySchema: SchemaObject[string],
): ArrayData {
const innerSchema = getNestedSchema(arraySchema);
if (!isZodObject(innerSchema)) {
return inputArray;
}
let encodedData = inputArray;
Object.entries(innerSchema.shape).forEach(
([fieldName, schema]: [string, ZodType<AllowedUserTypes>]) => {
encodedData = encodedData.map((item) => ({
...item,
[fieldName]: processSpecialFieldDisplay(item[fieldName], schema),
}));
},
);
return encodedData;
}

export function getArraySummaryData(
categories: string[],
arrayConfigurations: Record<string, ArrayConfigServer> | undefined,
userData: UserData,
content: StrapiContentComponent[],
relevantPageSchemas: SchemaObject,
): ArraySummaryData {
if (!arrayConfigurations) {
return undefined;
Expand All @@ -42,7 +72,9 @@ export function getArraySummaryData(
const disableAddButton =
arrayConfiguration.shouldDisableAddButton?.(userData) ?? false;
const possibleArray = userData[category];
const data = Array.isArray(possibleArray) ? possibleArray : [];
const data = Array.isArray(possibleArray)
? encodeSpecialFields(possibleArray, relevantPageSchemas[category])
: [];

const arraySummaryCategoryContent = content
.filter((value) => value.__component === "page.array-summary")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const mockTranslations = {

const mockBuildFlowController = {
getRootMeta: vi.fn().mockReturnValue(undefined),
getConfig: vi.fn().mockReturnValue(""),
isFinal: vi.fn().mockReturnValue(false),
getPrevious: vi.fn().mockReturnValue(""),
stepStates: vi.fn().mockReturnValue(undefined),
Expand Down
9 changes: 8 additions & 1 deletion app/services/flow/formular/contentData/getContentData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getArraySummaryData } from "~/services/array/getArraySummaryData";
import pick from "lodash/pick";
import { type CMSContent } from "~/services/flow/formular/buildCmsContentAndTranslations";
import {
type StepState,
Expand All @@ -17,7 +18,8 @@ import {
stateIsCurrent,
} from "~/services/navigation/navState";
import { type StepStepper } from "~/components/navigation/types";
import { getPageSchema } from "~/domains/pageSchemas";
import { getAllPageSchemaByFlowId, getPageSchema } from "~/domains/pageSchemas";
import { type FlowId } from "~/domains/flowIds";

type ContentParameters = {
cmsContent: CMSContent;
Expand Down Expand Up @@ -60,12 +62,17 @@ export const getContentData = (
const arrayCategories = cmsContent.content
.filter((value) => value.__component === "page.array-summary")
.map((arraySummary) => arraySummary.category);
const relevantPageSchemas = pick(
getAllPageSchemaByFlowId(flowController.getConfig()?.id as FlowId),
arrayCategories,
);

return getArraySummaryData(
arrayCategories,
flowController.getRootMeta()?.arrays,
userDataWithPageData,
cmsContent.content,
relevantPageSchemas,
);
},
getFormElements: () => {
Expand Down
17 changes: 17 additions & 0 deletions app/services/processSpecialFieldDisplay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import z from "zod";
import { extractZodDescription } from "~/components/formElements/schemaToForm/renderSchemaBasedFormElement";
import { type AllowedUserTypes, type SchemaObject } from "~/domains/userData";
import { ibanZodDescription } from "~/services/validation/iban";

export const specialFieldsToEncode = new Set([ibanZodDescription]);

export function processSpecialFieldDisplay<T extends AllowedUserTypes>(
value: T,
schema?: SchemaObject[string],
): T {
const schemaDescription = extractZodDescription(schema ?? z.NEVER);
if (schemaDescription && specialFieldsToEncode.has(schemaDescription)) {
return schema?.encode(value) as T;
}
return value;
}
5 changes: 5 additions & 0 deletions app/services/summary/__test__/fieldEntryCreation.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, it, expect } from "vitest";
import { createFieldEntry, processBoxFields } from "../fieldEntryCreation";
import type { UserData } from "~/domains/userData";
import z from "zod";

describe("fieldEntryCreation", () => {
describe("createFieldEntry", () => {
Expand All @@ -24,6 +25,7 @@ describe("fieldEntryCreation", () => {
userData,
mockFieldQuestions,
"/beratungshilfe/antrag/persoenliche-daten/name",
z.string(),
);

expect(result).toEqual(
Expand All @@ -46,6 +48,7 @@ describe("fieldEntryCreation", () => {
userData,
mockFieldQuestions,
"/beratungshilfe/antrag/persoenliche-daten/name",
z.string(),
);

expect(result).toEqual(
Expand All @@ -70,6 +73,7 @@ describe("fieldEntryCreation", () => {
userData,
mockFieldQuestions,
"/beratungshilfe/antrag/finanzielle-angaben/kinder/kinder/name",
z.string(),
);

expect(result).toEqual(
Expand All @@ -95,6 +99,7 @@ describe("fieldEntryCreation", () => {
userData,
mockFieldQuestions,
"/beratungshilfe/antrag/finanzielle-angaben/einkommen/einkommen",
z.string(),
);

expect(result.question).toBe("Welche Berufsart haben Sie?");
Expand Down
26 changes: 26 additions & 0 deletions app/services/summary/__test__/processSpecialFieldDisplay.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import z from "zod";
import { processSpecialFieldDisplay } from "~/services/processSpecialFieldDisplay";
import { ibanZodDescription } from "~/services/validation/iban";
import { schemaOrEmptyString } from "~/services/validation/schemaOrEmptyString";

describe("processSpecialFieldDisplay", () => {
it("should call the encode method of a special field schema if present", () => {
const mockEncode = vi.fn((str) => str + " encoded");
const specialFieldSchema = schemaOrEmptyString(
z
.codec(z.string(), z.string(), {
encode: mockEncode,
decode: vi.fn(),
})
.describe(ibanZodDescription),
);
const result = processSpecialFieldDisplay("test", specialFieldSchema);
expect(mockEncode).toHaveBeenCalled();
expect(result).toBe("test encoded");
});

it("should return the unmodified value if the schema isn't a special field", () => {
const result = processSpecialFieldDisplay("unmodified", z.string());
expect(result).toBe("unmodified");
});
});
26 changes: 22 additions & 4 deletions app/services/summary/fieldEntryCreation.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import type { AllowedUserTypes, UserData } from "~/domains/userData";
import type {
AllowedUserTypes,
SchemaObject,
UserData,
} from "~/domains/userData";
import type { FieldItem } from "./types";
import { formatFieldValue } from "./formatFieldValue";
import { getUserDataFieldLabel } from "./templateReplacement";
import { createArrayEditUrl } from "./arrayFieldProcessing";
import { parseArrayField } from "./fieldParsingUtils";
import { findStepIdForField } from "./getFormQuestions";
import { getRelevantPageSchemasForStepId } from "~/domains/pageSchemas";
import { type FlowId } from "~/domains/flowIds";

export function createFieldEntry(
fieldName: string,
Expand All @@ -14,6 +20,7 @@ export function createFieldEntry(
{ question?: string; options?: Array<{ text: string; value: string }> }
>,
representativeStepId: string,
schema: SchemaObject[string],
): FieldItem {
const fieldInfo = parseArrayField(fieldName);
const isArrayItem = fieldInfo.isArrayField;
Expand Down Expand Up @@ -52,7 +59,7 @@ export function createFieldEntry(
const answer =
value == undefined || value === ""
? "Keine Angabe" // need to get this from CMS for translations
: formatFieldValue(value, fieldQuestion?.options);
: formatFieldValue(value, fieldQuestion?.options, schema);

let editUrl: string | undefined = undefined;
if (representativeStepId) {
Expand Down Expand Up @@ -80,12 +87,23 @@ export function processBoxFields(
{ question?: string; options?: Array<{ text: string; value: string }> }
>,
fieldToStepMapping: Record<string, string>,
flowId: string,
flowId: FlowId,
): FieldItem[] {
return fields.map((fieldName) => {
const stepId = findStepIdForField(fieldName, fieldToStepMapping);
const fullStepId = stepId ? `${flowId}${stepId}` : "";
const pageConfig = getRelevantPageSchemasForStepId(flowId, stepId ?? "");
const pageSchemas = Object.entries(pageConfig).reduce(
(prev, [, item]) => ({ ...prev, ...item.pageSchema }),
{} as SchemaObject,
);

return createFieldEntry(fieldName, userData, fieldQuestions, fullStepId);
return createFieldEntry(
fieldName,
userData,
fieldQuestions,
fullStepId,
pageSchemas[fieldName],
);
});
}
Loading
Loading