Skip to content

Commit

Permalink
Update UI to display form errors
Browse files Browse the repository at this point in the history
  • Loading branch information
wilwong89 committed May 21, 2024
1 parent c6e151d commit fa17281
Show file tree
Hide file tree
Showing 13 changed files with 280 additions and 145 deletions.
9 changes: 9 additions & 0 deletions frontend/src/assets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ label {
}
}

.app-error-color {
color: $app-error !important;
}

.app-error-message {
color: $app-error;
font-size: 0.8rem;
}

/* ---------- primevue overrides ------------ */

/* buttons */
Expand Down
1 change: 1 addition & 0 deletions frontend/src/assets/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ $app-primary: #036;
$app-link-text: #1a5a96;
$app-link-text-hover: #00f;
$app-outline-on-primary: #fff;
$app-error: #D8292F;

// highlighted sections, table rows
$app-highlight-background: #d9e1e8;
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/components/form/Dropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,11 @@ const emit = defineEmits(['onChange']);
/>

<small :id="`${name}-help`">{{ helpText }}</small>
<div>
<ErrorMessage :name="name" />
<div class="mt-2">
<ErrorMessage
:name="name"
class="app-error-message"
/>
</div>
</div>
</template>
7 changes: 5 additions & 2 deletions frontend/src/components/form/InputMask.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ const props = withDefaults(defineProps<Props>(), {
/>

<small :id="`${name}-help`">{{ helpText }}</small>
<div>
<ErrorMessage :name="name" />
<div class="mt-2">
<ErrorMessage
:name="name"
class="app-error-message"
/>
</div>
</div>
</template>
7 changes: 5 additions & 2 deletions frontend/src/components/form/InputNumber.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@ const fieldActive: Ref<boolean> = ref(false);
>
{{ helpText }}
</small>
<div>
<ErrorMessage :name="name" />
<div class="mt-2">
<ErrorMessage
:name="name"
class="app-error-message"
/>
</div>
</div>
</template>
7 changes: 5 additions & 2 deletions frontend/src/components/form/InputText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ const fieldActive: Ref<boolean> = ref(false);
>
{{ helpText }}
</small>
<div>
<ErrorMessage :name="name" />
<div class="mt-2">
<ErrorMessage
:name="name"
class="app-error-message"
/>
</div>
</div>
</template>
5 changes: 4 additions & 1 deletion frontend/src/components/form/Password.vue
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ const { errorMessage, value } = useField<string>(toRef(props, 'name'));
/>
<small :id="`${name}-help`">{{ helptext }}</small>
<div>
<ErrorMessage :name="props.name" />
<ErrorMessage
:name="props.name"
class="app-error-message"
/>
</div>
</div>
</template>
7 changes: 5 additions & 2 deletions frontend/src/components/form/RadioList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ watch(value, () => {
</div>

<small :id="`${name}-help`">{{ helpText }}</small>
<div>
<ErrorMessage :name="name" />
<div class="mt-2">
<ErrorMessage
:name="name"
class="app-error-message"
/>
</div>
</div>
</template>
99 changes: 83 additions & 16 deletions frontend/src/components/intake/ShasIntakeForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,23 @@ import { useTypeStore } from '@/store';
import {
ContactPreferenceList,
NumResidentialUnits,
ProjectLocation,
ProjectRelationshipList,
RouteNames,
YesNo,
YesNoUnsure
} from '@/utils/constants';
import { BASIC_RESPONSES } from '@/utils/enums';
import { BASIC_RESPONSES, INTAKE_FORM_CATEGORIES, PROJECT_LOCATION } from '@/utils/enums';
import { deepToRaw } from '@/utils/utils';
import { getIntakeSchema } from './ShasIntakeSchema';
import { intakeSchema } from './ShasIntakeSchema';
import type { Ref } from 'vue';
import Calendar from '../form/Calendar.vue';
// Constants
const VALIDATION_BANNER_TEXT =
'One or more of the required fields are missing or contains invalid data. Please check the highlighted fields.';
// Store
const typeStore = useTypeStore();
const { getPermitTypes } = storeToRefs(typeStore);
Expand All @@ -52,25 +57,27 @@ const { getPermitTypes } = storeToRefs(typeStore);
const activeStep: Ref<number> = ref(0);
const assignedActivityId: Ref<string | undefined> = ref(undefined);
const editable: Ref<boolean> = ref(true);
const formRef: Ref<InstanceType<typeof Form> | null> = ref(null);
const geomarkAccordionIndex: Ref<number | undefined> = ref(undefined);
const isSubmittable: Ref<boolean> = ref(false);
const initialFormValues: Ref<any | undefined> = ref(undefined);
const parcelAccordionIndex: Ref<number | undefined> = ref(undefined);
const spacialAccordionIndex: Ref<number | undefined> = ref(undefined);
// Enums
const enum PROJECT_LOCATION {
STREET_ADDRESS = 'Street address',
LOCATION_COORDINATES = 'Location coordinates'
}
// Constants
const ProjectLocation = [PROJECT_LOCATION.STREET_ADDRESS, PROJECT_LOCATION.LOCATION_COORDINATES];
const formSchema = getIntakeSchema(ProjectLocation);
const validationErrors: Ref<string[]> = ref([]);
// Actions
const confirm = useConfirm();
const toast = useToast();
const checkSubmittable = (stepNumber: number) => {
if (stepNumber === 3) isSubmittable.value = true;
};
function displayErrors(a: any) {
validationErrors.value = Array.from(new Set(a.errors ? Object.keys(a.errors).map((x) => x.split('.')[0]) : []));
document.getElementById('form')?.scrollIntoView({ behavior: 'smooth' });
}
function confirmSubmit(data: any) {
confirm.require({
message: 'Are you sure you wish to submit this form? Please review the form before submitting.',
Expand Down Expand Up @@ -134,21 +141,25 @@ onBeforeMount(async () => {
typeStore.setPermitTypes((await permitService.getPermitTypes()).data);
});
const formRef: Ref<InstanceType<typeof Form> | null> = ref(null);
</script>

<template>
<div v-if="!assignedActivityId">
<Form
v-if="initialFormValues"
id="form"
v-slot="{ setFieldValue, values }"
ref="formRef"
keep-values
:initial-values="initialFormValues"
:validation-schema="formSchema"
:validation-schema="intakeSchema"
@invalid-submit="(e) => displayErrors(e)"
@submit="confirmSubmit"
>
<Stepper v-model:activeStep="activeStep">
<Stepper
v-model:activeStep="activeStep"
@update:active-step="checkSubmittable"
>
<!--
Basic info
-->
Expand All @@ -160,9 +171,23 @@ const formRef: Ref<InstanceType<typeof Form> | null> = ref(null);
:click-callback="clickCallback"
title="Basic info"
icon="fa-user"
:class="{
'app-error-color':
validationErrors.includes(INTAKE_FORM_CATEGORIES.APPLICANT) ||
validationErrors.includes(INTAKE_FORM_CATEGORIES.BASIC)
}"
/>
</template>
<template #content="{ nextCallback }">
<Message
v-if="validationErrors.length"
severity="error"
icon="pi pi-exclamation-circle"
:closable="false"
class="text-center mt-0"
>
{{ VALIDATION_BANNER_TEXT }}
</Message>
<Card>
<template #title>
<span class="section-header">Applicant Info</span>
Expand Down Expand Up @@ -301,9 +326,19 @@ const formRef: Ref<InstanceType<typeof Form> | null> = ref(null);
:click-callback="clickCallback"
title="Housing"
icon="fa-house"
:class="{ 'app-error-color': validationErrors.includes(INTAKE_FORM_CATEGORIES.HOUSING) }"
/>
</template>
<template #content="{ prevCallback, nextCallback }">
<Message
v-if="validationErrors.length"
severity="error"
icon="pi pi-exclamation-circle"
:closable="false"
class="text-center mt-0"
>
{{ VALIDATION_BANNER_TEXT }}
</Message>
<Card>
<template #title>
<span class="section-header">Help us learn more about your housing project</span>
Expand Down Expand Up @@ -657,9 +692,21 @@ const formRef: Ref<InstanceType<typeof Form> | null> = ref(null);
:click-callback="clickCallback"
title="Location"
icon="fa-location-dot"
:class="{
'app-error-color': validationErrors.includes(INTAKE_FORM_CATEGORIES.LOCATION)
}"
/>
</template>
<template #content="{ prevCallback, nextCallback }">
<Message
v-if="validationErrors.length"
severity="error"
icon="pi pi-exclamation-circle"
:closable="false"
class="text-center mt-0"
>
{{ VALIDATION_BANNER_TEXT }}
</Message>
<Card>
<template #title>
<div class="flex">
Expand Down Expand Up @@ -908,9 +955,23 @@ const formRef: Ref<InstanceType<typeof Form> | null> = ref(null);
:click-callback="clickCallback"
title="Permits & Reports"
icon="fa-file"
:class="{
'app-error-color':
validationErrors.includes(INTAKE_FORM_CATEGORIES.PERMITS) ||
validationErrors.includes(INTAKE_FORM_CATEGORIES.APPLIED_PERMITS)
}"
/>
</template>
<template #content="{ prevCallback }">
<Message
v-if="validationErrors.length"
severity="error"
icon="pi pi-exclamation-circle"
:closable="false"
class="text-center mt-0"
>
{{ VALIDATION_BANNER_TEXT }}
</Message>
<Card>
<template #title>
<div class="flex">
Expand Down Expand Up @@ -1101,6 +1162,7 @@ const formRef: Ref<InstanceType<typeof Form> | null> = ref(null);
class="col-4"
:name="`investigatePermits[${idx}].permitTypeId`"
placeholder="Select Permit type"
`
:options="getPermitTypes"
:option-label="(e) => `${e.businessDomain}: ${e.name}`"
option-value="permitTypeId"
Expand Down Expand Up @@ -1169,7 +1231,7 @@ const formRef: Ref<InstanceType<typeof Form> | null> = ref(null);
label="Submit"
type="submit"
icon="pi pi-upload"
:disabled="!editable || activeStep !== 3"
:disabled="!editable || !isSubmittable"
/>
</div>
</Form>
Expand Down Expand Up @@ -1197,6 +1259,11 @@ const formRef: Ref<InstanceType<typeof Form> | null> = ref(null);
box-shadow: none;
}
:deep(.p-invalid),
:deep(.p-card.p-component:has(.p-invalid)) {
border-color: $app-error !important;
}
.p-card {
border-color: rgb(242, 241, 241);
border-radius: 8px;
Expand Down
Loading

0 comments on commit fa17281

Please sign in to comment.