fixed schema validation to support multi checkbox

This commit is contained in:
2025-11-20 14:55:43 -05:00
parent d9e4c1d6ff
commit 91b915fbcf
3 changed files with 36 additions and 17 deletions

View File

@@ -95,7 +95,7 @@ export async function insertCourseEvent(event: CourseEventDetails): Promise<numb
passed_qual, passed_qual,
remarks remarks
) )
VALUES (?, ?, ?, ?, ?);`, [attendee.attendee_id, eventID, attendee.attendee_role_id, attendee.passed_bookwork, attendee.passed_qual, attendee.remarks]); VALUES (?, ?, ?, ?, ?, ?);`, [attendee.attendee_id, eventID, attendee.attendee_role_id, attendee.passed_bookwork, attendee.passed_qual, attendee.remarks]);
} }
await con.commit(); await con.commit();
await con.release(); await con.release();

View File

@@ -2,7 +2,8 @@ import { z } from "zod";
export const courseEventAttendeeSchema = z.object({ export const courseEventAttendeeSchema = z.object({
attendee_id: z.number().int().positive(), attendee_id: z.number().int().positive(),
passed: z.boolean(), passed_bookwork: z.boolean(),
passed_qual: z.boolean(),
remarks: z.string(), remarks: z.string(),
attendee_role_id: z.number().int().positive() attendee_role_id: z.number().int().positive()
}) })

View File

@@ -3,7 +3,7 @@ import { trainingReportSchema, courseEventAttendeeSchema } from '@shared/schemas
import { Course, CourseAttendee, CourseAttendeeRole, CourseEventDetails } from '@shared/types/course' import { Course, CourseAttendee, CourseAttendeeRole, CourseEventDetails } from '@shared/types/course'
import { useForm, useFieldArray, FieldArray as VeeFieldArray, ErrorMessage, Field as VeeField } from 'vee-validate' import { useForm, useFieldArray, FieldArray as VeeFieldArray, ErrorMessage, Field as VeeField } from 'vee-validate'
import { toTypedSchema } from '@vee-validate/zod' import { toTypedSchema } from '@vee-validate/zod'
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref, watch } from 'vue'
import { getAllAttendeeRoles, getAllTrainings, postTrainingReport } from '@/api/trainingReport' import { getAllAttendeeRoles, getAllTrainings, postTrainingReport } from '@/api/trainingReport'
import { getMembers, Member } from '@/api/member' import { getMembers, Member } from '@/api/member'
import FieldGroup from '../ui/field/FieldGroup.vue' import FieldGroup from '../ui/field/FieldGroup.vue'
@@ -18,6 +18,9 @@ import FieldSet from '../ui/field/FieldSet.vue'
import FieldLegend from '../ui/field/FieldLegend.vue' import FieldLegend from '../ui/field/FieldLegend.vue'
import FieldDescription from '../ui/field/FieldDescription.vue' import FieldDescription from '../ui/field/FieldDescription.vue'
import Checkbox from '../ui/checkbox/Checkbox.vue' import Checkbox from '../ui/checkbox/Checkbox.vue'
import Select from '../ui/select/Select.vue'
import { SelectContent, SelectItem, SelectTrigger, SelectValue } from 'reka-ui'
import FieldContent from '../ui/field/FieldContent.vue'
const { handleSubmit, resetForm, errors, values } = useForm({ const { handleSubmit, resetForm, errors, values } = useForm({
validationSchema: toTypedSchema(trainingReportSchema), validationSchema: toTypedSchema(trainingReportSchema),
@@ -29,6 +32,11 @@ const { handleSubmit, resetForm, errors , values } = useForm({
} }
}) })
watch(errors, (e) => {
console.warn("Validation errors:", e);
}, { deep: true });
const submitForm = handleSubmit(onSubmit); const submitForm = handleSubmit(onSubmit);
function toMySQLDateTime(date: Date): string { function toMySQLDateTime(date: Date): string {
@@ -70,14 +78,15 @@ onMounted(async () => {
}) })
</script> </script>
<template> <template>
<form id="trainingForm" @submit.prevent="submitForm" class="flex flex-col gap-5"> <form id="trainingForm" @submit.prevent="submitForm" class="flex flex-col gap-5 pl-2">
<FieldGroup> <FieldGroup>
<VeeField v-slot="{ field, errors }" name="course_id"> <VeeField v-slot="{ field, errors }" name="course_id">
<Field :data-invalid="!!errors.length"> <Field :data-invalid="!!errors.length">
<FieldLabel>Training Course</FieldLabel> <FieldLabel>Training Course</FieldLabel>
<select v-bind="field" class="border rounded p-2 w-full"> <select v-bind="field"
class="border rounded p-2 w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none">
<option value="" disabled>Select a course</option> <option value="" disabled>Select a course</option>
<option v-for="course in trainings" :key="course.id" :value="course.id"> <option v-for="course in trainings" :key="course.id" :value="course.id">
{{ course.name }} {{ course.name }}
@@ -92,7 +101,8 @@ onMounted(async () => {
<VeeField v-slot="{ field, errors }" name="event_date"> <VeeField v-slot="{ field, errors }" name="event_date">
<Field :data-invalid="!!errors.length"> <Field :data-invalid="!!errors.length">
<FieldLabel>Event Date</FieldLabel> <FieldLabel>Event Date</FieldLabel>
<input type="date" v-bind="field" class="border rounded p-2 w-full" /> <input type="date" v-bind="field"
class="border rounded p-2 w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none" />
<FieldError v-if="errors.length" :errors="errors" /> <FieldError v-if="errors.length" :errors="errors" />
</Field> </Field>
</VeeField> </VeeField>
@@ -125,7 +135,8 @@ onMounted(async () => {
<!-- Member Select --> <!-- Member Select -->
<VeeField :name="`attendees[${index}].attendee_id`" v-slot="{ field: f, errors: e }"> <VeeField :name="`attendees[${index}].attendee_id`" v-slot="{ field: f, errors: e }">
<div> <div>
<select v-bind="f" class="w-full border p-2 rounded-md"> <select v-bind="f"
class="border rounded p-2 w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none">
<option value="">Select member...</option> <option value="">Select member...</option>
<option v-for="m in members" :key="m.member_id" :value="m.member_id"> <option v-for="m in members" :key="m.member_id" :value="m.member_id">
{{ m.member_name }} {{ m.member_name }}
@@ -138,7 +149,8 @@ onMounted(async () => {
<!-- Role Select --> <!-- Role Select -->
<VeeField :name="`attendees[${index}].attendee_role_id`" v-slot="{ field: f, errors: e }"> <VeeField :name="`attendees[${index}].attendee_role_id`" v-slot="{ field: f, errors: e }">
<div> <div>
<select v-bind="f" class="w-full border p-2 rounded-md"> <select v-bind="f"
class="border rounded p-2 w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none">
<option value="">Select role...</option> <option value="">Select role...</option>
<option v-for="r in eventRoles" :key="r.id" :value="r.id"> <option v-for="r in eventRoles" :key="r.id" :value="r.id">
{{ r.name }} {{ r.name }}
@@ -149,20 +161,26 @@ onMounted(async () => {
</VeeField> </VeeField>
<!-- Passed Checkbox --> <!-- Passed Checkbox -->
<VeeField v-if="selectedCourse?.hasBookwork" v-slot="{ field }" :name="`attendees[${index}].passed`" type="checkbox" <VeeField v-if="selectedCourse?.hasBookwork" v-slot="{ field }"
:value="true" :unchecked-value="false"> :name="`attendees[${index}].passed_bookwork`" type="checkbox" :value="true"
:unchecked-value="false">
<label> <label>
<input type="checkbox" :name="`attendees[${index}].passed`" v-bind="field" <input type="checkbox" :name="`attendees[${index}].passed_bookwork`" v-bind="field"
:value="true" /> :value="true" />
<!-- <Checkbox :name="`attendees[${index}].passed_bookwork`" v-bind="field" :value="true">
</Checkbox> -->
</label> </label>
</VeeField> </VeeField>
<!-- Passed Checkbox --> <!-- Passed Checkbox -->
<VeeField v-if="selectedCourse?.hasQual" v-slot="{ field }" :name="`attendees[${index}].passed`" type="checkbox" <VeeField v-if="selectedCourse?.hasQual" v-slot="{ field }"
:value="true" :unchecked-value="false"> :name="`attendees[${index}].passed_qual`" type="checkbox" :value="true"
:unchecked-value="false">
<label> <label>
<input type="checkbox" :name="`attendees[${index}].passed`" v-bind="field" <input type="checkbox" :name="`attendees[${index}].passed_qual`" v-bind="field"
:value="true" /> :value="true" />
<!-- <Checkbox :name="`attendees[${index}].passed_qual`" v-bind="field"></Checkbox> -->
</label> </label>
</VeeField> </VeeField>
@@ -185,7 +203,7 @@ onMounted(async () => {
<Button type="button" size="sm" variant="outline" <Button type="button" size="sm" variant="outline"
@click="push({ attendee_id: null, attendee_role_id: null, passed: false, remarks: '' })"> @click="push({ attendee_id: null, attendee_role_id: null, passed_bookwork: false, passed_qual: false, remarks: '' })">
<Plus class="mr-1 h-4 w-4" /> <Plus class="mr-1 h-4 w-4" />
Add Attendee Add Attendee
</Button> </Button>