Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Updates Repeating Sets, Pages and Rules to work together on the Review page and form output #4186

Merged
merged 30 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0025daa
Initial commit
thiessenp-cds Aug 7, 2024
7c801b1
Start on Review page formatting
thiessenp-cds Aug 7, 2024
1280aef
Updates comments and types
thiessenp-cds Aug 8, 2024
063ae3c
Gettings there...
thiessenp-cds Aug 8, 2024
2a7e658
Gettings there...
thiessenp-cds Aug 8, 2024
4272e02
Gettings there...
thiessenp-cds Aug 9, 2024
df14aab
Gettings there...
thiessenp-cds Aug 9, 2024
946ef20
Merge branch 'develop' into feat/4166
thiessenp-cds Aug 9, 2024
52eb5ca
Gettings there...
thiessenp-cds Aug 9, 2024
6725c6e
Gettings there...
thiessenp-cds Aug 9, 2024
6a2d6bf
Gettings there...
thiessenp-cds Aug 9, 2024
2a2be48
Gettings there...
thiessenp-cds Aug 9, 2024
67f1c28
Merge branch 'develop' into feat/4166
thiessenp-cds Aug 13, 2024
6333026
Merge branch 'develop' into feat/4166
thiessenp-cds Aug 13, 2024
071edb3
Merge branch 'develop' into feat/4166
thiessenp-cds Aug 13, 2024
9c7805b
Updates build form to handle pages and repeating sets
thiessenp-cds Aug 13, 2024
3e0fa93
Updates a comment
thiessenp-cds Aug 13, 2024
79fbbf9
Merge branch 'develop' into feat/4166
thiessenp-cds Aug 14, 2024
232a0aa
Updates a comment
thiessenp-cds Aug 14, 2024
64714a5
Start on files
thiessenp-cds Aug 14, 2024
2dac9bc
Adds Files
thiessenp-cds Aug 14, 2024
be8d3d1
Handles case of empty file
thiessenp-cds Aug 14, 2024
523e2d0
Updates comments
thiessenp-cds Aug 14, 2024
86f099f
Updates even more comments
thiessenp-cds Aug 14, 2024
e48b8a4
Merge branch 'develop' into feat/4166
thiessenp-cds Aug 14, 2024
04b4621
Updates type and comments
thiessenp-cds Aug 14, 2024
0839f71
Updates a comment
thiessenp-cds Aug 15, 2024
830dfa6
Merge branch 'develop' into feat/4166
ShadeWyrm Aug 15, 2024
880d9c1
Updates a comment
thiessenp-cds Aug 15, 2024
697e888
Updates build form types and simplifies logic a bit
thiessenp-cds Aug 15, 2024
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 @@ -33,11 +33,10 @@ export function buildFormDataObject(formRecord: PublicFormRecord, values: Respon

function _handleDynamicRowTypeIfNeeded(
element: FormElement,
value: Response
value: Response | Responses[]
): [string, string | FileInputResponse][] {
if (element.type === FormElementTypes.dynamicRow) {
if (element.type === FormElementTypes.dynamicRow && Array.isArray(value)) {
if (element.properties.subElements === undefined) return [];

const responses = value as Responses[];
const subElements = element.properties.subElements;

Expand Down Expand Up @@ -66,15 +65,15 @@ function _handleDynamicRowTypeIfNeeded(
// `flat` function is needed because we use a `map` in a `map`.
.flat()
);
} else {
const result = _handleFormDataType(element, value);
return result ? [result] : [];
}

const result = _handleFormDataType(element, value);
return result ? [result] : [];
}

function _handleFormDataType(
element: FormElement,
value: Response
value: Response | Responses[]
): [string, string | FileInputResponse] | undefined {
switch (element.type) {
case FormElementTypes.textField:
Expand Down
132 changes: 105 additions & 27 deletions components/clientComponents/forms/Review/Review.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Button } from "@clientComponents/globals";
import { Theme } from "@clientComponents/globals/Buttons/themes";
import { useFocusIt } from "@lib/hooks/useFocusIt";
import { useGCFormsContext } from "@lib/hooks/useGCFormContext";
import { FormElement } from "@lib/types";
import { FileInputResponse, FormElement, FormElementTypes } from "@lib/types";
import { Language } from "@lib/types/form-builder-types";
import { getLocalizedProperty } from "@lib/utils";
import {
Expand All @@ -14,31 +14,39 @@ import {
getElementIdsAsNumber,
Group,
} from "@lib/formContext";
import { randomId } from "@lib/client/clientHelpers";

type ReviewItem = {
id: string;
name: string;
title: string;
elements: {
elementId: number;
title: string;
values: string;
}[];
elements: Element[];
};

function getFormElementValues(elementName: number | null, formValues: void | FormValues) {
const value = formValues[elementName as keyof typeof formValues];
if (Array.isArray(value)) {
return (value as Array<string>).join(", ") || "-";
}
return value || "-";
}
type Element = {
title: string;
values: string | FileInputResponse | Element[];
};

function getFormElementTitle(formElementId: number, formElements: FormElement[], lang: string) {
const formElement = formElements.find((item) => item.id === formElementId);
return formElement
? (formElement.properties?.[getLocalizedProperty("title", lang)] as string)
: "-";
function formatElementValues(values: Element["values"]) {
if (!values) {
return "-";
}
// Case of a File upload
if ((values as FileInputResponse).based64EncodedFile !== undefined) {
const file = values as FileInputResponse;
if (!file.name || !file.size || file.size < 0) {
return "-";
}
const fileSizeInMB = (file.size / 1024 / 1024).toFixed(2);
return `${file.name} (${fileSizeInMB} MB)`;
}
// Case of an array like element e.g. checkbox
if (Array.isArray(values)) {
return values.join(", ") || "-";
}
// Case of a single value element e.g. input
return String(values);
}

function getReviewItemElements(
Expand All @@ -52,14 +60,48 @@ function getReviewItemElements(
const shownElementIds = getElementIdsAsNumber(
filterValuesForShownElements(groupElements, shownFormElements)
);
const result = shownElementIds.map((elementId) => {
return shownElementIds.map((elementId) => {
const element = formElements.find((item) => item.id === elementId);
let resultValues: string | Element[] = formatElementValues(
formValues[elementId as unknown as keyof typeof formValues] as Element["values"]
);
// Handle any Sub Elements. Note Sub Elements = Dynamic Rows = Repeating Sets
if (element?.type === FormElementTypes.dynamicRow) {
resultValues = [];
const parentId = element.id;
const parentTitle = element.properties?.[getLocalizedProperty("title", lang)];
const subElements = element.properties?.subElements;
// Use FormValues as the source of truth and for each FormValue value, map the related
// subElement title to the FormValue value
const subElementValues = (formValues[parentId] as string[]).map(
(valueRows, valueRowsIndex) => {
const subElementsTitle = `${parentTitle} - ${valueRowsIndex + 1}`;
const valueRowsAsArray = Object.keys(valueRows).map(
(key) => valueRows[key as keyof typeof valueRows]
);
// Match the FormValue index to the subElement index to assign the Element title
const titlesMappedToValues = valueRowsAsArray.map((formValue, valueRowIndex) => {
return {
title: subElements?.[valueRowIndex].properties?.[getLocalizedProperty("title", lang)],
values: formValue,
};
});
return {
title: subElementsTitle,
values: titlesMappedToValues,
} as Element;
}
);
resultValues.push({
title: parentTitle as string,
values: subElementValues,
});
}
return {
elementId,
title: getFormElementTitle(elementId, formElements, lang),
values: getFormElementValues(elementId, formValues),
title: (element?.properties?.[getLocalizedProperty("title", lang)] as string) || "-",
values: resultValues,
};
});
return result;
}

export const Review = ({ language }: { language: Language }): React.ReactElement => {
Expand All @@ -78,7 +120,7 @@ export const Review = ({ language }: { language: Language }): React.ReactElement
return groupHistory
.filter((key) => key !== "review") // Removed to avoid showing as a group
.map((groupId) => {
const group: Group = groups[groupId as keyof typeof groups];
const group: Group = groups[groupId as keyof typeof groups] || {};
return {
id: groupId,
name: group.name,
Expand Down Expand Up @@ -140,17 +182,53 @@ const QuestionsAnswersList = ({ reviewItem }: { reviewItem: ReviewItem }): React
<dl className="my-10">
{Array.isArray(reviewItem.elements) &&
reviewItem.elements.map((reviewElement) => {
if (Array.isArray(reviewElement.values)) {
return <SubElements key={randomId()} elements={reviewElement.values as Element[]} />;
}
return (
<div key={reviewElement.elementId} className="mb-8">
<dt className="mb-2 font-bold">{reviewElement.title}</dt>
<dd>{reviewElement.values}</dd>
<div key={randomId()} className="mb-8">
<dt className="font-bold mb-2">{reviewElement.title}</dt>
<dd>{formatElementValues(reviewElement.values)}</dd>
</div>
);
})}
</dl>
);
};

// Handle formatting Sub Elements. Note Sub Elements = Dynamic Rows = Repeating Sets.
const SubElements = ({ elements }: { elements: Element[] }) => {
return elements?.map((subElementItem) => {
return (subElementItem.values as Element[])?.map((element) => {
if (Array.isArray(element.values)) {
const dlId = randomId();
// Create a nested DL for each Sub Element list
return (
<dl key={dlId} aria-labelledby={dlId} className="my-10">
<h4 className="italic" id={dlId}>
{element.title}
</h4>
{(element.values as Element[]).map((elementValues) => {
return (
<div key={randomId()} className="mb-2">
<dt className="font-bold mb-2">{elementValues.title}</dt>
<dd>{formatElementValues(elementValues.values)}</dd>
</div>
);
})}
</dl>
);
}
return (
<div key={randomId()} className="mb-2">
<dt className="font-bold mb-2">{element.title}</dt>
<dd>{formatElementValues(element.values)}</dd>
</div>
);
});
});
};

const EditButton = ({
reviewItem,
theme,
Expand Down
Loading