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) KHP3-7059 : Add validation for regimen form #458

Merged
merged 1 commit into from
Nov 11, 2024
Merged
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 @@ -11,9 +11,10 @@ import { calculateAge, filterRegimenData } from './utils';
interface NonStandardRegimenProps {
category: string;
setNonStandardRegimens: (value: any) => void;
setStandardRegimenLine: (value: any) => void;
setStandardRegimenLine: (value: string) => void;
selectedRegimenType: string;
visitDate: Date;
errors: { [key: string]: string };
}

const NonStandardRegimen: React.FC<NonStandardRegimenProps> = ({
Expand All @@ -22,6 +23,7 @@ const NonStandardRegimen: React.FC<NonStandardRegimenProps> = ({
setNonStandardRegimens,
setStandardRegimenLine,
visitDate,
errors,
}) => {
const { t } = useTranslation();
const { standardRegimen } = useStandardRegimen();
Expand Down Expand Up @@ -76,7 +78,8 @@ const NonStandardRegimen: React.FC<NonStandardRegimenProps> = ({
{selectedRegimenType === 'nonStandardUuid' ? (
<Select
id="regimenLine"
invalidText="Required"
invalid={!!errors?.standardRegimenLine}
invalidText={errors?.standardRegimenLine}
labelText={t('selectRegimenLine', 'Select Regimen Line')}
className={styles.inputContainer}
value={selectedRegimenLine}
Expand All @@ -94,6 +97,7 @@ const NonStandardRegimen: React.FC<NonStandardRegimenProps> = ({

{selectedRegimenLine && (
<div>
{errors?.nonStandardRegimens && <div className={styles.errorText}>{errors.nonStandardRegimens}</div>}
{selectedRegimens?.map((selectedRegimen, index) => {
const availableRegimens = nonStandardRegimen.filter(
(regimen) => !selectedRegimens.includes(regimen.uuid) || regimen.uuid === selectedRegimen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { CarePanelConfig } from '../config-schema';
import { mutate } from 'swr';
import NonStandardRegimen from './non-standard-regimen.component';
import { addOrUpdateObsObject } from './utils';
import { z } from 'zod';

interface RegimenFormProps {
patientUuid: string;
Expand All @@ -44,6 +45,58 @@ interface RegimenFormProps {
};
closeWorkspace: () => void;
}
// Base schema with common fields
const baseSchema = z.object({
regimenEvent: z.string().min(1, { message: 'Please select a regimen event' }),
visitDate: z.date({ required_error: 'Please select a visit date' }),
});

// Schema for standard regimen fields
const standardRegimenSchema = z.object({
standardRegimenLine: z.string().min(1, { message: 'Please select a regimen line' }),
standardRegimen: z.string().min(1, { message: 'Please select a regimen' }),
});

// Schema for regimen type selection
const regimenTypeSchema = z.object({
selectedRegimenType: z.string().min(1, 'Please select a regimen type'),
});

// Schema for regimen reason
const regimenReasonSchema = z.object({
regimenReason: z.string().min(1, { message: 'Please provide a reason for regimen change/stop' }),
});

// Schema for non standard regimen fields
const nonStandardRegimenSchema = z.object({
standardRegimenLine: z.string().min(1, { message: 'Please select a regimen line' }),
nonStandardRegimens: z.array(z.string()).min(1, { message: 'Please select at least one drug regimen' }),
});

// Function to get dynamic schema based on regimen event and type
const getRegimenFormSchema = (regimenEvent: string, selectedRegimenType: string) => {
let schema = baseSchema;

if (regimenEvent === Regimen.stopRegimenConcept) {
schema = schema.merge(regimenReasonSchema);
} else if (regimenEvent === Regimen.changeRegimenConcept) {
schema = schema.merge(regimenReasonSchema).merge(regimenTypeSchema);
if (selectedRegimenType === 'standardUuid') {
schema = schema.merge(standardRegimenSchema);
} else if (selectedRegimenType === 'nonStandardUuid') {
schema = schema.merge(nonStandardRegimenSchema);
}
} else if (regimenEvent === Regimen.startOrRestartConcept) {
schema = schema.merge(regimenTypeSchema);
if (selectedRegimenType === 'standardUuid') {
schema = schema.merge(standardRegimenSchema);
} else if (selectedRegimenType === 'nonStandardUuid') {
schema = schema.merge(nonStandardRegimenSchema);
}
}

return schema;
};

const RegimenForm: React.FC<RegimenFormProps> = ({
patientUuid,
Expand All @@ -53,6 +106,7 @@ const RegimenForm: React.FC<RegimenFormProps> = ({
closeWorkspace,
}) => {
const { t } = useTranslation();
const [errors, setErrors] = useState<Record<string, string>>({});
const isTablet = useLayoutType() === 'tablet';
const sessionUser = useSession();
const config = useConfig() as CarePanelConfig;
Expand Down Expand Up @@ -149,9 +203,46 @@ const RegimenForm: React.FC<RegimenFormProps> = ({
}
}, [selectedRegimenType, nonStandardRegimens, regimenEvent]);

const validateForm = useCallback(() => {
const formData = {
regimenEvent,
selectedRegimenType,
standardRegimenLine,
standardRegimen,
regimenReason,
visitDate,
nonStandardRegimens: nonStandardRegimens.map((reg) => reg.value),
};

try {
const schema = getRegimenFormSchema(regimenEvent, selectedRegimenType);
schema.parse(formData);
setErrors({});
return true;
} catch (validationError) {
const errorMap = validationError.errors.reduce((acc: Record<string, string>, err) => {
acc[err.path[0]] = err.message;
return acc;
}, {});
setErrors(errorMap);
return false;
}
}, [
regimenEvent,
selectedRegimenType,
standardRegimenLine,
standardRegimen,
regimenReason,
visitDate,
nonStandardRegimens,
]);

const handleSubmit = useCallback(
(event) => {
event.preventDefault();
if (!validateForm()) {
return;
}
setIsSubmitting(true);

const encounterToSave: Encounter = {
Expand Down Expand Up @@ -215,16 +306,18 @@ const RegimenForm: React.FC<RegimenFormProps> = ({
}
},
[
patientUuid,
t,
category,
validateForm,
visitDate,
patientUuid,
sessionUser?.sessionLocation?.uuid,
sessionUser?.currentProvider?.uuid,
config.regimenObs.encounterProviderRoleUuid,
obsArray,
obsArrayForPrevEncounter,
sessionUser,
config,
regimenEncounter.uuid,
closeWorkspace,
t,
category,
],
);

Expand Down Expand Up @@ -253,10 +346,12 @@ const RegimenForm: React.FC<RegimenFormProps> = ({
labelText={t('date', 'Date')}
placeholder="dd/mm/yyyy"
style={{ width: '100%' }}
invalid={!!errors.visitDate}
invalidText={errors.visitDate}
/>
</DatePicker>
),
[visitDate, t],
[visitDate, t, errors.visitDate],
);

return (
Expand Down Expand Up @@ -302,6 +397,7 @@ const RegimenForm: React.FC<RegimenFormProps> = ({
onClick={launchDeleteRegimenDialog}
/>
</RadioButtonGroup>
{errors.regimenEvent && <div className={styles.errorText}>{errors.regimenEvent}</div>}
{regimenEvent ? (
<>
{regimenEvent !== 'undo' && regimenDatePicker}
Expand All @@ -319,13 +415,15 @@ const RegimenForm: React.FC<RegimenFormProps> = ({
disabled={category !== 'ARV'}
/>
</RadioButtonGroup>
{errors.selectedRegimenType && <div className={styles.errorText}>{errors.selectedRegimenType}</div>}
{selectedRegimenType === 'standardUuid' ? (
<StandardRegimen
category={category}
setStandardRegimen={setStandardRegimen}
setStandardRegimenLine={setStandardRegimenLine}
selectedRegimenType={selectedRegimenType}
visitDate={visitDate}
errors={errors}
/>
) : (
<NonStandardRegimen
Expand All @@ -334,13 +432,14 @@ const RegimenForm: React.FC<RegimenFormProps> = ({
setStandardRegimenLine={setStandardRegimenLine}
selectedRegimenType={selectedRegimenType}
visitDate={visitDate}
errors={errors}
/>
)}
</>
) : null}
{(regimenEvent === Regimen.stopRegimenConcept ||
(regimenEvent === Regimen.changeRegimenConcept && selectedRegimenType)) && (
<RegimenReason category={category} setRegimenReason={setRegimenReason} />
<RegimenReason category={category} setRegimenReason={setRegimenReason} errors={errors} />
)}
</>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { useRegimenReason } from '../hooks/useRegimenReason';

interface RegimenReasonProps {
category: string;
setRegimenReason: (value: any) => void;
setRegimenReason: (value: string) => void;
errors: { [key: string]: string };
}

const RegimenReason: React.FC<RegimenReasonProps> = ({ category, setRegimenReason }) => {
const RegimenReason: React.FC<RegimenReasonProps> = ({ category, setRegimenReason, errors }) => {
const { t } = useTranslation();
const { regimenReason, isLoading, error } = useRegimenReason();

Expand All @@ -26,7 +27,8 @@ const RegimenReason: React.FC<RegimenReasonProps> = ({ category, setRegimenReaso
<>
<Select
id="regimenReason"
invalidText="Required"
invalid={!!errors?.regimenReason}
invalidText={errors?.regimenReason}
labelText={t('selectReason', 'Select Reason')}
className={styles.inputContainer}
value={selectedRegimenReason}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { calculateAge, filterRegimenData } from './utils';

interface StandardRegimenProps {
category: string;
setStandardRegimen: (value: any) => void;
setStandardRegimenLine: (value: any) => void;
setStandardRegimen: (value: string) => void;
setStandardRegimenLine: (value: string) => void;
selectedRegimenType: string;
visitDate: Date;
errors: { [key: string]: string };
}

const StandardRegimen: React.FC<StandardRegimenProps> = ({
Expand All @@ -20,6 +21,7 @@ const StandardRegimen: React.FC<StandardRegimenProps> = ({
setStandardRegimenLine,
selectedRegimenType,
visitDate,
errors,
}) => {
const { t } = useTranslation();
const { standardRegimen, isLoading, error } = useStandardRegimen();
Expand Down Expand Up @@ -60,7 +62,8 @@ const StandardRegimen: React.FC<StandardRegimenProps> = ({
{selectedRegimenType === 'standardUuid' ? (
<Select
id="regimenLine"
invalidText="Required"
invalid={!!errors?.standardRegimenLine}
invalidText={errors?.standardRegimenLine}
labelText={t('selectRegimenLine', 'Select Regimen Line')}
className={styles.inputContainer}
value={selectedRegimenLine}
Expand All @@ -79,7 +82,8 @@ const StandardRegimen: React.FC<StandardRegimenProps> = ({
{selectedRegimenLine && (
<Select
id="regimen"
invalidText="Required"
invalid={!!errors?.standardRegimen}
invalidText={errors?.standardRegimen}
labelText={t('selectRegimen', 'Select Regimen')}
className={styles.inputContainer}
value={selectedRegimen}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
padding: spacing.$spacing-06 spacing.$spacing-05;
background-color: $ui-02;
}
.errorText {
color: colors.$red-60;
font-size: 0.875rem;
margin-top: 0.313rem;
}

.desktop {
padding: 0rem;
Expand Down
Loading