Major style pass to the form

This commit is contained in:
2025-11-20 19:39:29 -05:00
parent 03a8eee409
commit 9eb815cde5
2 changed files with 76 additions and 53 deletions

View File

@@ -73,46 +73,54 @@ onMounted(async () => {
}) })
</script> </script>
<template> <template>
<form id="trainingForm" @submit.prevent="submitForm" class="flex flex-col gap-5 pl-2"> <form id="trainingForm" @submit.prevent="submitForm" class="flex flex-col gap-5">
<FieldGroup> <div class="flex gap-5">
<VeeField v-slot="{ field, errors }" name="course_id"> <div class="flex-1">
<Field :data-invalid="!!errors.length"> <FieldGroup>
<FieldLabel>Training Course</FieldLabel> <VeeField v-slot="{ field, errors }" name="course_id">
<Field :data-invalid="!!errors.length">
<select v-bind="field" <FieldLabel class="scroll-m-20 text-lg tracking-tight">Training Course</FieldLabel>
class="border rounded p-2 w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none"> <select v-bind="field"
<option value="" disabled>Select a course</option> class="h-9 border rounded p-2 w-auto focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none">
<option v-for="course in trainings" :key="course.id" :value="course.id"> <option value="" disabled>Select a course</option>
{{ course.name }} <option v-for="course in trainings" :key="course.id" :value="course.id">
</option> {{ course.name }}
</select> </option>
</select>
<FieldError v-if="errors.length" :errors="errors" /> <div class="h-4">
</Field> <FieldError v-if="errors.length" :errors="errors" />
</VeeField> </div>
</FieldGroup> </Field>
<FieldGroup> </VeeField>
<VeeField v-slot="{ field, errors }" name="event_date"> </FieldGroup>
<Field :data-invalid="!!errors.length"> </div>
<FieldLabel>Event Date</FieldLabel> <div class="w-[150px]">
<input type="date" v-bind="field" <FieldGroup>
class="border rounded p-2 w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none" /> <VeeField v-slot="{ field, errors }" name="event_date">
<FieldError v-if="errors.length" :errors="errors" /> <Field :data-invalid="!!errors.length">
</Field> <FieldLabel class="scroll-m-20 text-lg tracking-tight">Event Date</FieldLabel>
</VeeField> <input type="date" v-bind="field"
</FieldGroup> class="h-9 border rounded p-2 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none" />
<div class="h-4">
<FieldError v-if="errors.length" :errors="errors" />
</div>
</Field>
</VeeField>
</FieldGroup>
</div>
</div>
<VeeFieldArray name="attendees" v-slot="{ fields, push, remove }"> <VeeFieldArray name="attendees" v-slot="{ fields, push, remove }">
<FieldSet class="gap-4"> <FieldSet class="gap-4">
<FieldLegend>Attendees</FieldLegend> <FieldLegend class="scroll-m-20 text-lg tracking-tight">Attendees</FieldLegend>
<FieldDescription>Add members who attended this session.</FieldDescription> <FieldDescription>Add members who attended this session.</FieldDescription>
<FieldGroup class="gap-4"> <FieldGroup class="gap-4">
<!-- Column Headers --> <!-- Column Headers -->
<div <div
class="grid grid-cols-[180px_160px_50px_50px_1fr_auto] gap-3 font-medium text-sm text-muted-foreground px-1"> class="grid grid-cols-[180px_150px_65px_45px_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>Bookwork</div> <div>Bookwork</div>
@@ -137,7 +145,9 @@ onMounted(async () => {
{{ m.member_name }} {{ m.member_name }}
</option> </option>
</select> </select>
<FieldError v-if="e.length" :errors="e" /> <div class="h-4">
<FieldError v-if="e.length" :errors="e" />
</div>
</div> </div>
</VeeField> </VeeField>
@@ -151,14 +161,16 @@ onMounted(async () => {
{{ r.name }} {{ r.name }}
</option> </option>
</select> </select>
<FieldError v-if="e.length" :errors="e" /> <div class="h-4">
<FieldError v-if="e.length" :errors="e" />
</div>
</div> </div>
</VeeField> </VeeField>
<!-- Passed Checkbox --> <!-- Passed Checkbox -->
<VeeField v-slot="{ field }" :name="`attendees[${index}].passed_bookwork`" type="checkbox" <VeeField v-slot="{ field }" :name="`attendees[${index}].passed_bookwork`" type="checkbox"
:value="true" :unchecked-value="false"> :value="true" :unchecked-value="false">
<div class="flex justify-center"> <div class="flex flex-col items-center">
<div class="relative inline-flex items-center group"> <div class="relative inline-flex items-center group">
<Checkbox :disabled="!selectedCourse?.hasBookwork" <Checkbox :disabled="!selectedCourse?.hasBookwork"
@@ -174,13 +186,15 @@ onMounted(async () => {
This course does not have bookwork This course does not have bookwork
</div> </div>
</div> </div>
<div class="h-4">
</div>
</div> </div>
</VeeField> </VeeField>
<!-- Passed Checkbox --> <!-- Passed Checkbox -->
<VeeField v-slot="{ field }" :name="`attendees[${index}].passed_qual`" type="checkbox" <VeeField v-slot="{ field }" :name="`attendees[${index}].passed_qual`" type="checkbox"
:value="true" :unchecked-value="false"> :value="true" :unchecked-value="false">
<div class="flex justify-center"> <div class="flex flex-col items-center">
<div class="relative inline-flex items-center group"> <div class="relative inline-flex items-center group">
<Checkbox :disabled="!selectedCourse?.hasQual" <Checkbox :disabled="!selectedCourse?.hasQual"
:name="`attendees[${index}].passed_qual`" v-bind="field"></Checkbox> :name="`attendees[${index}].passed_qual`" v-bind="field"></Checkbox>
@@ -194,6 +208,8 @@ onMounted(async () => {
This course does not have a qualification This course does not have a qualification
</div> </div>
</div> </div>
<div class="h-4">
</div>
</div> </div>
</VeeField> </VeeField>
@@ -203,14 +219,21 @@ onMounted(async () => {
<textarea v-bind="f" <textarea v-bind="f"
class="h-[38px] resize-none border rounded p-2 w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none" class="h-[38px] resize-none border rounded p-2 w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] bg-background outline-none"
placeholder="Optional remarks"></textarea> placeholder="Optional remarks"></textarea>
<FieldError v-if="e.length" :errors="e" /> <div class="h-4">
<FieldError v-if="e.length" :errors="e" />
</div>
</div> </div>
</VeeField> </VeeField>
<!-- Remove button --> <div>
<Button type="button" variant="ghost" size="sm" @click="remove(index)" class="self-center"> <!-- Remove button -->
<X :size="10" /> <Button type="button" variant="ghost" size="sm" @click="remove(index)"
</Button> class="self-center">
<X :size="10" />
</Button>
<div class="h-4">
</div>
</div>
</div> </div>
</template> </template>
</FieldGroup> </FieldGroup>
@@ -224,19 +247,19 @@ onMounted(async () => {
</FieldSet> </FieldSet>
</VeeFieldArray> </VeeFieldArray>
<FieldGroup> <FieldGroup class="pt-3">
<VeeField v-slot="{ field, errors }" name="remarks"> <VeeField v-slot="{ field, errors }" name="remarks">
<Field :data-invalid="!!errors.length"> <Field :data-invalid="!!errors.length">
<FieldLabel>Remarks</FieldLabel> <FieldLabel class="scroll-m-20 text-lg tracking-tight">Remarks</FieldLabel>
<Textarea v-bind="field" placeholder="Any remarks about this training event..." <Textarea v-bind="field" placeholder="Any remarks about this training event..."
autocomplete="off" /> autocomplete="off" />
<FieldError v-if="errors.length" :errors="errors" /> <FieldError v-if="errors.length" :errors="errors" />
</Field> </Field>
</VeeField> </VeeField>
</FieldGroup> </FieldGroup>
<Field orientation="horizontal"> <div class="flex gap-3 justify-end">
<Button type="button" variant="outline" @click="resetForm">Reset</Button> <Button type="button" variant="outline" @click="resetForm">Reset</Button>
<Button type="submit" form="trainingForm">Submit</Button> <Button type="submit" form="trainingForm">Submit</Button>
</Field> </div>
</form> </form>
</template> </template>

View File

@@ -10,7 +10,7 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from '@/components/ui/table' } from '@/components/ui/table'
import { X } from 'lucide-vue-next'; import { Plus, 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'; import Checkbox from '@/components/ui/checkbox/Checkbox.vue';
@@ -56,12 +56,12 @@ onMounted(async () => {
</script> </script>
<template> <template>
<div class="max-w-7xl mx-auto flex mt-5"> <div class="px-20 mx-auto max-w-[100rem] flex mt-5">
<!-- training report list --> <!-- training report list -->
<div class="px-4" :class="sidePanel == sidePanelState.closed ? 'w-full' : 'w-2/5'"> <div class="px-4 my-3" :class="sidePanel == sidePanelState.closed ? 'w-full' : 'w-2/5'">
<div class="flex justify-between"> <div class="flex justify-between">
<p class="scroll-m-20 text-2xl font-semibold tracking-tight">Training Reports</p> <p class="scroll-m-20 text-2xl font-semibold tracking-tight">Training Reports</p>
<Button @click="createTrainingReport">New Training Report</Button> <Button @click="createTrainingReport"><Plus></Plus> New Training Report</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">
<Table> <Table>
@@ -90,7 +90,7 @@ onMounted(async () => {
</div> </div>
</div> </div>
<!-- view training report section --> <!-- view training report section -->
<div v-if="sidePanel == sidePanelState.view" class="px-4 border-l w-3/5"> <div v-if="sidePanel == sidePanelState.view" class="px-4 my-3 border-l w-3/5">
<div class="flex justify-between my-3"> <div class="flex justify-between my-3">
<div class="flex gap-5"> <div class="flex gap-5">
<p>{{ focusedTrainingReport.course_name }}</p> <p>{{ focusedTrainingReport.course_name }}</p>
@@ -132,17 +132,17 @@ onMounted(async () => {
</div> </div>
</div> </div>
</div> </div>
<div v-if="sidePanel == sidePanelState.create" class="px-4 border-l w-3/5"> <div v-if="sidePanel == sidePanelState.create" class="pl-7 border-l w-3/5 max-w-5xl">
<div class="flex justify-between my-3"> <div class="flex justify-between my-3">
<div class="flex gap-5"> <div class="flex pl-2 gap-5">
<p>New Training Report</p> <p class="scroll-m-20 text-2xl font-semibold tracking-tight">New Training Report</p>
</div> </div>
<button @click="closeTrainingReport" class="cursor-pointer"> <button @click="closeTrainingReport" class="cursor-pointer">
<X></X> <X></X>
</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" <TrainingReportForm class="w-full pl-2"
@submit="(newID) => { viewTrainingReport(newID); loadTrainingReports() }"></TrainingReportForm> @submit="(newID) => { viewTrainingReport(newID); loadTrainingReports() }"></TrainingReportForm>
</div> </div>
</div> </div>