Skip to content

Commit

Permalink
Merge feature/full-create-edit-event-form and solve conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
anagperal committed Aug 20, 2024
2 parents a144be3 + 3eee7ef commit e05d8a2
Show file tree
Hide file tree
Showing 15 changed files with 552 additions and 500 deletions.
20 changes: 5 additions & 15 deletions src/data/repositories/consts/DiseaseOutbreakConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,21 +132,11 @@ export function getValueFromDiseaseOutbreak(
}

export function getHazardTypeByCode(hazardTypeCode: string): HazardType {
switch (hazardTypeCode) {
case "BIOLOGICAL_ANIMAL":
return "Biological:Animal";
case "BIOLOGICAL_HUMAN":
return "Biological:Human";
case "BIOLOGICAL_HUM_ANM":
return "Biological:HumanAndAnimal";
case "CHEMICAL":
return "Chemical";
case "ENVIRONMENTAL":
return "Environmental";
case "UNKNOWN":
default:
return "Unknown";
}
return (
(Object.keys(hazardTypeCodeMap) as HazardType[]).find(
key => hazardTypeCodeMap[key] === hazardTypeCode
) || "Unknown"
);
}

function getOUTextFromList(OUs: string[]): string {
Expand Down
2 changes: 1 addition & 1 deletion src/webapp/components/form/FieldWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { RadioButtonsGroup } from "../radio-buttons-group/RadioButtonsGroup";
import { TextArea } from "../text-input/TextArea";
import { DatePicker } from "../date-picker/DatePicker";
import { Checkbox } from "../checkbox/Checkbox";
import { FormFieldState, updateFieldState } from "./FormState";
import { FormFieldState, updateFieldState } from "./FormFieldsState";

export type FieldWidgetProps = {
onChange: (updatedField: FormFieldState) => void;
Expand Down
3 changes: 2 additions & 1 deletion src/webapp/components/form/Form.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from "react";

import { useForm } from "./useForm";
import { FormState, FormFieldState } from "./FormState";
import { FormState } from "./FormState";
import { FormLayout } from "./FormLayout";
import { FormSection } from "./FormSection";
import { Layout } from "../layout/Layout";
import { FormFieldState } from "./FormFieldsState";

export type FormProps = {
formState: FormState;
Expand Down
200 changes: 200 additions & 0 deletions src/webapp/components/form/FormFieldsState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { Maybe } from "../../../utils/ts-utils";
import { validateFieldRequired, validateFieldRequiredWithNotApplicable } from "./validations";
import { UserOption } from "../user-selector/UserSelector";
import { Option } from "../utils/option";
import { ValidationError, ValidationErrorKey } from "../../../domain/entities/ValidationError";
import { FormSectionState } from "./FormSectionsState";

export type FieldType = "text" | "boolean" | "select" | "radio" | "date" | "user";

type FormFieldStateBase<T> = {
id: string;
label?: string;
placeholder?: string;
helperText?: string;
errors: string[];
required?: boolean;
showIsRequired?: boolean;
disabled?: boolean;
isVisible?: boolean;
hasNotApplicable?: boolean;
notApplicableFieldId?: string;
width?: string;
maxWidth?: string;
value: T;
type: FieldType;
};

export type FormTextFieldState = FormFieldStateBase<string> & {
type: "text";
multiline?: boolean;
};

export type FormBooleanFieldState = FormFieldStateBase<boolean> & {
type: "boolean";
};

export type FormMultipleOptionsFieldState = FormFieldStateBase<string[]> & {
type: "select";
options: Option[];
multiple: true;
};

export type FormOptionsFieldState = FormFieldStateBase<string> & {
type: "select" | "radio";
options: Option[];
multiple: false;
};

export type FormDateFieldState = FormFieldStateBase<Date | null> & {
type: "date";
};

export type FormAvatarFieldState = FormFieldStateBase<Maybe<string>> & {
type: "user";
options: UserOption[];
};

export type FormFieldState =
| FormTextFieldState
| FormOptionsFieldState
| FormMultipleOptionsFieldState
| FormBooleanFieldState
| FormDateFieldState
| FormAvatarFieldState;

// HELPERS:

export function getAllFieldsFromSections(formSections: FormSectionState[]): FormFieldState[] {
return formSections.reduce(
(acc: FormFieldState[], section: FormSectionState): FormFieldState[] => {
if (section.subsections) {
return [...acc, ...getAllFieldsFromSections(section.subsections)];
} else {
return [...acc, ...section.fields];
}
},
[]
);
}

export function getStringFieldValue(id: string, allFields: FormFieldState[]): string {
return (
getFieldValueById<FormTextFieldState | FormOptionsFieldState | FormAvatarFieldState>(
id,
allFields
) || ""
);
}

export function getDateFieldValue(id: string, allFields: FormFieldState[]): Date | null {
return getFieldValueById<FormDateFieldState>(id, allFields) || null;
}

export function getBooleanFieldValue(id: string, allFields: FormFieldState[]): boolean {
return !!getFieldValueById<FormBooleanFieldState>(id, allFields);
}

export function getMultipleOptionsFieldValue(id: string, allFields: FormFieldState[]): string[] {
return getFieldValueById<FormMultipleOptionsFieldState>(id, allFields) || [];
}

export function getFieldValueById<F extends FormFieldState>(
id: string,
fields: FormFieldState[]
): F["value"] | undefined {
const field = fields.find(field => field.id === id);
if (field) {
return getFieldValue<F>(field);
}
}

export function getFieldIdFromIdsDictionary<T extends Record<string, string>>(
key: keyof T,
fieldIdsDictionary: T
): string {
return fieldIdsDictionary[key] as string;
}

export function getEmptyValueForField<F extends FormFieldState>(field: FormFieldState): F["value"] {
switch (field.type) {
case "text":
return "";
case "boolean":
return false;
case "select":
return field.multiple ? [] : "";
case "radio":
return "";
case "date":
return null;
case "user":
return undefined;
}
}

export function getFieldValue<F extends FormFieldState>(field: FormFieldState): F["value"] {
return field.value;
}

export function isFieldInSection(section: FormSectionState, field: FormFieldState): boolean {
return section.fields.some(f => f.id === field.id);
}

// UPDATES:

export function updateFields(
formFields: FormFieldState[],
updatedField: FormFieldState,
fieldValidationErrors?: ValidationError[]
): FormFieldState[] {
return formFields.map(field => {
if (field.id === updatedField.id) {
return {
...updatedField,
errors:
fieldValidationErrors?.find(error => error.property === updatedField.id)
?.errors || [],
};
} else {
return field;
}
});
}

export function updateFieldState<F extends FormFieldState>(
fieldToUpdate: F,
newValue: F["value"]
): F {
return { ...fieldToUpdate, value: newValue };
}

// VALIDATIONS:

export function validateField(
field: FormFieldState,
allFields: FormFieldState[] = []
): ValidationError | undefined {
if (field.notApplicableFieldId || !field.isVisible) return;

const errors: ValidationErrorKey[] = [
...(field.required && !field.hasNotApplicable
? validateFieldRequired(field.value, field.type)
: []),
...(field.hasNotApplicable
? validateFieldRequiredWithNotApplicable(
field.value,
field.type,
!!allFields.find(f => f.notApplicableFieldId === field.id)?.value
)
: []),
];

return errors.length > 0
? {
property: field.id,
errors: errors,
value: field.value,
}
: undefined;
}
3 changes: 2 additions & 1 deletion src/webapp/components/form/FormSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { IconInfo24 } from "@dhis2/ui";
import { Separator } from "../separator/Separator";
import { IconButton } from "../icon-button/IconButton";
import { FieldWidget } from "./FieldWidget";
import { FormFieldState, FormSectionState } from "./FormState";
import { FormFieldState } from "./FormFieldsState";
import { FormSectionState } from "./FormSectionsState";

type FormSectionProps = {
id: string;
Expand Down
Loading

0 comments on commit e05d8a2

Please sign in to comment.