added support for optional checkboxes
This commit is contained in:
@@ -4,14 +4,21 @@ import { Course, CourseAttendee, CourseAttendeeRole, CourseEventDetails, CourseE
|
|||||||
export async function getAllCourses(): Promise<Course[]> {
|
export async function getAllCourses(): Promise<Course[]> {
|
||||||
const sql = "SELECT * FROM courses WHERE deleted = false;"
|
const sql = "SELECT * FROM courses WHERE deleted = false;"
|
||||||
|
|
||||||
const res = await pool.query(sql);
|
const res: Course[] = await pool.query(sql);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getCourseByID(id: number): Promise<Course> {
|
||||||
|
const sql = "SELECT * FROM courses WHERE id = ?;"
|
||||||
|
const res: Course[] = await pool.query(sql, [id]);
|
||||||
|
return res[0];
|
||||||
|
}
|
||||||
|
|
||||||
function buildAttendee(row: RawAttendeeRow): CourseAttendee {
|
function buildAttendee(row: RawAttendeeRow): CourseAttendee {
|
||||||
return {
|
return {
|
||||||
passed: !!row.passed,
|
passed_bookwork: !!row.passed_bookwork,
|
||||||
|
passed_qual: !!row.passed_qual,
|
||||||
attendee_id: row.attendee_id,
|
attendee_id: row.attendee_id,
|
||||||
course_event_id: row.course_event_id,
|
course_event_id: row.course_event_id,
|
||||||
created_at: row.created_at,
|
created_at: row.created_at,
|
||||||
@@ -67,6 +74,7 @@ export async function getCourseEventDetails(id: number): Promise<CourseEventDeta
|
|||||||
let rows: CourseEventDetails[] = await pool.query(sql, [id]);
|
let rows: CourseEventDetails[] = await pool.query(sql, [id]);
|
||||||
let event = rows[0];
|
let event = rows[0];
|
||||||
event.attendees = await getCourseEventAttendees(id);
|
event.attendees = await getCourseEventAttendees(id);
|
||||||
|
event.course = await getCourseByID(event.course_id);
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,10 +91,11 @@ export async function insertCourseEvent(event: CourseEventDetails): Promise<numb
|
|||||||
attendee_id,
|
attendee_id,
|
||||||
course_event_id,
|
course_event_id,
|
||||||
attendee_role_id,
|
attendee_role_id,
|
||||||
passed,
|
passed_bookwork,
|
||||||
|
passed_qual,
|
||||||
remarks
|
remarks
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?, ?, ?);`, [attendee.attendee_id, eventID, attendee.attendee_role_id, attendee.passed, 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();
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ export interface Course {
|
|||||||
updated_at: string;
|
updated_at: string;
|
||||||
deleted?: number | boolean;
|
deleted?: number | boolean;
|
||||||
prereq_id?: number | null;
|
prereq_id?: number | null;
|
||||||
|
hasBookwork: boolean;
|
||||||
|
hasQual: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CourseEventDetails {
|
export interface CourseEventDetails {
|
||||||
@@ -31,11 +33,13 @@ export interface CourseEventDetails {
|
|||||||
created_by: number | null;
|
created_by: number | null;
|
||||||
created_by_name: string | null;
|
created_by_name: string | null;
|
||||||
course_name: string | null;
|
course_name: string | null;
|
||||||
|
course: Course | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface CourseAttendee {
|
export interface CourseAttendee {
|
||||||
passed: boolean; // tinyint(1)
|
passed_bookwork: boolean; // tinyint(1)
|
||||||
|
passed_qual: boolean; // tinyint(1)
|
||||||
attendee_id: number; // PK
|
attendee_id: number; // PK
|
||||||
course_event_id: number; // PK
|
course_event_id: number; // PK
|
||||||
attendee_role_id: number | null;
|
attendee_role_id: number | null;
|
||||||
@@ -57,7 +61,8 @@ export interface CourseAttendeeRole {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RawAttendeeRow {
|
export interface RawAttendeeRow {
|
||||||
passed: number;
|
passed_bookwork: number; // tinyint(1)
|
||||||
|
passed_qual: number; // tinyint(1)
|
||||||
attendee_id: number;
|
attendee_id: number;
|
||||||
course_event_id: number;
|
course_event_id: number;
|
||||||
attendee_role_id: number | null;
|
attendee_role_id: number | null;
|
||||||
|
|||||||
@@ -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 { onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } 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'
|
||||||
@@ -19,7 +19,7 @@ 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'
|
||||||
|
|
||||||
const { handleSubmit, resetForm, errors } = useForm({
|
const { handleSubmit, resetForm, errors , values } = useForm({
|
||||||
validationSchema: toTypedSchema(trainingReportSchema),
|
validationSchema: toTypedSchema(trainingReportSchema),
|
||||||
initialValues: {
|
initialValues: {
|
||||||
course_id: null,
|
course_id: null,
|
||||||
@@ -55,6 +55,8 @@ function onSubmit(vals) {
|
|||||||
|
|
||||||
const { remove, push, fields } = useFieldArray('attendees');
|
const { remove, push, fields } = useFieldArray('attendees');
|
||||||
|
|
||||||
|
const selectedCourse = computed<Course | undefined>(() => {return trainings.value?.find(c => c.id == values.course_id)})
|
||||||
|
|
||||||
const trainings = ref<Course[] | null>(null);
|
const trainings = ref<Course[] | null>(null);
|
||||||
const members = ref<Member[] | null>(null);
|
const members = ref<Member[] | null>(null);
|
||||||
const eventRoles = ref<CourseAttendeeRole[] | null>(null);
|
const eventRoles = ref<CourseAttendeeRole[] | null>(null);
|
||||||
@@ -108,7 +110,8 @@ onMounted(async () => {
|
|||||||
class="grid grid-cols-[180px_140px_50px_1fr_auto] gap-3 font-medium text-sm text-muted-foreground px-1">
|
class="grid grid-cols-[180px_140px_50px_1fr_auto] gap-3 font-medium text-sm text-muted-foreground px-1">
|
||||||
<div>Member</div>
|
<div>Member</div>
|
||||||
<div>Role</div>
|
<div>Role</div>
|
||||||
<div>Passed</div>
|
<div v-if="selectedCourse?.hasBookwork">Bookwork</div>
|
||||||
|
<div v-if="selectedCourse?.hasQual">Qual</div>
|
||||||
<div>Remarks</div>
|
<div>Remarks</div>
|
||||||
<div></div> <!-- empty for remove button -->
|
<div></div> <!-- empty for remove button -->
|
||||||
</div>
|
</div>
|
||||||
@@ -146,7 +149,7 @@ onMounted(async () => {
|
|||||||
</VeeField>
|
</VeeField>
|
||||||
|
|
||||||
<!-- Passed Checkbox -->
|
<!-- Passed Checkbox -->
|
||||||
<VeeField v-slot="{ field }" :name="`attendees[${index}].passed`" type="checkbox"
|
<VeeField v-if="selectedCourse?.hasBookwork" v-slot="{ field }" :name="`attendees[${index}].passed`" type="checkbox"
|
||||||
:value="true" :unchecked-value="false">
|
:value="true" :unchecked-value="false">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" :name="`attendees[${index}].passed`" v-bind="field"
|
<input type="checkbox" :name="`attendees[${index}].passed`" v-bind="field"
|
||||||
@@ -154,6 +157,14 @@ onMounted(async () => {
|
|||||||
</label>
|
</label>
|
||||||
</VeeField>
|
</VeeField>
|
||||||
|
|
||||||
|
<!-- Passed Checkbox -->
|
||||||
|
<VeeField v-if="selectedCourse?.hasQual" v-slot="{ field }" :name="`attendees[${index}].passed`" type="checkbox"
|
||||||
|
:value="true" :unchecked-value="false">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" :name="`attendees[${index}].passed`" v-bind="field"
|
||||||
|
:value="true" />
|
||||||
|
</label>
|
||||||
|
</VeeField>
|
||||||
|
|
||||||
<!-- Remarks -->
|
<!-- Remarks -->
|
||||||
<VeeField :name="`attendees[${index}].remarks`" v-slot="{ field: f, errors: e }">
|
<VeeField :name="`attendees[${index}].remarks`" v-slot="{ field: f, errors: e }">
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import { X } from 'lucide-vue-next';
|
import { X } from 'lucide-vue-next';
|
||||||
import Button from '@/components/ui/button/Button.vue';
|
import Button from '@/components/ui/button/Button.vue';
|
||||||
import TrainingReportForm from '@/components/trainingReport/trainingReportForm.vue';
|
import TrainingReportForm from '@/components/trainingReport/trainingReportForm.vue';
|
||||||
|
import Checkbox from '@/components/ui/checkbox/Checkbox.vue';
|
||||||
|
|
||||||
enum sidePanelState { view, create, closed };
|
enum sidePanelState { view, create, closed };
|
||||||
|
|
||||||
@@ -113,10 +114,15 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="scroll-m-20 text-xl font-semibold tracking-tight">Trainees</label>
|
<div class="grid grid-cols-5"><label
|
||||||
<div v-for="person in focusedTrainingTrainees" class="grid grid-cols-4 my-2 items-center">
|
class="scroll-m-20 text-xl font-semibold tracking-tight">Trainees</label></div>
|
||||||
|
<div v-for="person in focusedTrainingTrainees" class="grid grid-cols-5 my-2 items-center">
|
||||||
<p>{{ person.attendee_name }}</p>
|
<p>{{ person.attendee_name }}</p>
|
||||||
<p class="text-right px-5">{{ person.passed ? "Passed" : "Failed" }}</p>
|
<!-- <p class="text-right px-5">{{ person.passed ? "Passed" : "Failed" }}</p> -->
|
||||||
|
<Checkbox v-if="focusedTrainingReport.course.hasBookwork" :default-value="person.passed_bookwork ? true : false" class="pointer-events-none">
|
||||||
|
</Checkbox>
|
||||||
|
<Checkbox v-if="focusedTrainingReport.course.hasQual" :default-value="person.passed_qual ? true : false" class="pointer-events-none">
|
||||||
|
</Checkbox>
|
||||||
<p class="bg-muted p-2 rounded-lg min-h-10 col-span-2 text-right">{{ person.remarks }}</p>
|
<p class="bg-muted p-2 rounded-lg min-h-10 col-span-2 text-right">{{ person.remarks }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -136,7 +142,8 @@ onMounted(async () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-y-auto max-h-[70vh] mt-5 scrollbar-themed">
|
<div class="overflow-y-auto max-h-[70vh] mt-5 scrollbar-themed">
|
||||||
<TrainingReportForm class="w-full" @submit="(newID) => {viewTrainingReport(newID); loadTrainingReports()}"></TrainingReportForm>
|
<TrainingReportForm class="w-full"
|
||||||
|
@submit="(newID) => { viewTrainingReport(newID); loadTrainingReports() }"></TrainingReportForm>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user