improved attendee remarks rendering

This commit is contained in:
2025-12-28 20:10:32 -05:00
parent 3c689fbb30
commit 4fbbed446e

View File

@@ -10,7 +10,7 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from '@/components/ui/table' } from '@/components/ui/table'
import { ArrowUpDown, Funnel, Plus, Search, X } from 'lucide-vue-next'; import { ArrowUpDown, ChevronDown, ChevronUp, Funnel, Plus, Search, 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';
@@ -54,6 +54,7 @@ watch(() => route.params.id, async (newID) => {
return; return;
} }
TRLoaded.value = false; TRLoaded.value = false;
expanded.value = null;
viewTrainingReport(Number(route.params.id)); viewTrainingReport(Number(route.params.id));
}) })
@@ -127,6 +128,8 @@ function setPage(pagenum: number) {
pageNum.value = pagenum; pageNum.value = pagenum;
loadTrainingReports(); loadTrainingReports();
} }
const expanded = ref<number>(null);
</script> </script>
<template> <template>
@@ -248,7 +251,7 @@ function setPage(pagenum: number) {
:member-id="focusedTrainingReport.created_by" /> :member-id="focusedTrainingReport.created_by" />
<p v-else>{{ focusedTrainingReport.created_by_name === null ? "Unknown User" : <p v-else>{{ focusedTrainingReport.created_by_name === null ? "Unknown User" :
focusedTrainingReport.created_by_name focusedTrainingReport.created_by_name
}}</p> }}</p>
</p> </p>
</div> </div>
</div> </div>
@@ -256,90 +259,180 @@ function setPage(pagenum: number) {
<!-- Trainers --> <!-- Trainers -->
<div> <div>
<label class="scroll-m-20 text-xl font-semibold tracking-tight">Trainers</label> <label class="scroll-m-20 text-xl font-semibold tracking-tight">Trainers</label>
<div class="grid grid-cols-4 py-2 text-sm font-medium text-muted-foreground border-b"> <div
class="grid grid-cols-[1fr_1fr_2fr_3rem] py-2 text-sm font-medium text-muted-foreground border-b">
<span>Name</span> <span>Name</span>
<span class="">Role</span> <span class="">Role</span>
<span class="text-right col-span-2">Remarks</span> <span class="text-right">Remarks</span>
<span></span>
</div> </div>
<div v-for="person in focusedTrainingTrainers" <div v-for="person in focusedTrainingTrainers" class=" items-center border-b last:border-none"
class="grid grid-cols-4 py-2 items-center border-b last:border-none"> :class="expanded === person.attendee_id && 'bg-muted/20'">
<div> <div class="grid grid-cols-[1fr_1fr_2fr_3rem] items-center py-2">
<MemberCard v-if="person.attendee_id" :member-id="person.attendee_id" <div>
class="justify-self-start"></MemberCard> <MemberCard v-if="person.attendee_id" :member-id="person.attendee_id"
<p v-else>{{ person.attendee_name }}</p> class="justify-self-start"></MemberCard>
<p v-else>{{ person.attendee_name }}</p>
</div>
<p class="">{{ person.role.name }}</p>
<p class="text-right px-2 truncate"
:class="person.remarks == '' ? 'text-muted-foreground' : ''">
{{ person.remarks == "" ?
'--'
: person.remarks }}</p>
<div class="w-min mx-auto">
<Button v-if="expanded === person.attendee_id" @click.stop="expanded = null"
variant="ghost" size="icon">
<ChevronUp class="size-6" />
</Button>
<Button v-else @click.stop="expanded = person.attendee_id" variant="ghost"
size="icon">
<ChevronDown class="size-6" />
</Button>
</div>
</div>
<div class="col-span-full" v-if="expanded === person.attendee_id">
<div class="px-6 pb-5 pt-2 space-y-2">
<p class="text-sm font-medium text-foreground">
Remarks
</p>
<p v-if="person.remarks"
class="text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto">
{{ person.remarks }}
</p>
<p v-else class="text-sm text-muted-foreground italic">
None provided
</p>
</div>
</div> </div>
<p class="">{{ person.role.name }}</p>
<p class="col-span-2 text-right px-2"
:class="person.remarks == '' ? 'text-muted-foreground' : ''">
{{ person.remarks == "" ?
'--'
: person.remarks }}</p>
</div> </div>
</div> </div>
<!-- trainees --> <!-- trainees -->
<div> <div>
<div class="flex flex-col"> <div class="flex flex-col">
<label class="scroll-m-20 text-xl font-semibold tracking-tight">Trainees</label> <label class="scroll-m-20 text-xl font-semibold tracking-tight">Trainees</label>
<div class="grid grid-cols-5 py-2 text-sm font-medium text-muted-foreground border-b"> <div
class="grid grid-cols-[1fr_5rem_5rem_2fr_3rem] py-2 text-sm font-medium text-muted-foreground border-b px-2">
<span>Name</span> <span>Name</span>
<span class="">Bookwork</span> <span class="text-center">Bookwork</span>
<span class="">Qual</span> <span class="text-center">Qual</span>
<span class="text-right col-span-2">Remarks</span> <span class="text-right">Remarks</span>
<span class="w-15"></span>
</div> </div>
</div> </div>
<div v-for="person in focusedTrainingTrainees" <div v-for="person in focusedTrainingTrainees" class="border-b last:border-none"
class="grid grid-cols-5 py-2 items-center border-b last:border-none"> :class="expanded === person.attendee_id && 'bg-muted/20'">
<div> <div class="grid grid-cols-[1fr_5rem_5rem_2fr_3rem] py-2 items-center mx-2">
<MemberCard v-if="person.attendee_id" :member-id="person.attendee_id" <div>
class="justify-self-start"></MemberCard> <MemberCard v-if="person.attendee_id" :member-id="person.attendee_id"
<p v-else>{{ person.attendee_name }}</p> class="justify-self-start"></MemberCard>
<p v-else>{{ person.attendee_name }}</p>
</div>
<Tooltip :open="!focusedTrainingReport.course.hasBookwork" class="mx-auto"
message="This course does not have bookwork">
<Checkbox :disabled="!focusedTrainingReport.course.hasBookwork"
:model-value="person.passed_bookwork" class="pointer-events-none">
</Checkbox>
</Tooltip>
<Tooltip :open="!focusedTrainingReport.course.hasQual" class="mx-auto"
message="This course does not have a qualification">
<Checkbox :disabled="!focusedTrainingReport.course.hasQual"
:model-value="person.passed_qual" class="pointer-events-none ml-1">
</Checkbox>
</Tooltip>
<p class="text-right truncate"
:class="person.remarks == '' ? 'text-muted-foreground' : ''">
{{ person.remarks == "" ?
'--'
: person.remarks }}</p>
<div class="w-min mx-auto">
<Button v-if="expanded === person.attendee_id" @click.stop="expanded = null"
variant="ghost" size="icon">
<ChevronUp class="size-6" />
</Button>
<Button v-else @click.stop="expanded = person.attendee_id" variant="ghost"
size="icon">
<ChevronDown class="size-6" />
</Button>
</div>
</div>
<div class="col-span-full" v-if="expanded === person.attendee_id">
<div class="px-6 pb-5 pt-2 space-y-2">
<p class="text-sm font-medium text-foreground">
Remarks
</p>
<p v-if="person.remarks"
class="text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto">
{{ person.remarks }}
</p>
<p v-else class="text-sm text-muted-foreground italic">
None provided
</p>
</div>
</div> </div>
<Tooltip :open="!focusedTrainingReport.course.hasBookwork"
message="This course does not have bookwork">
<Checkbox :disabled="!focusedTrainingReport.course.hasBookwork"
:model-value="person.passed_bookwork" class="pointer-events-none ml-5">
</Checkbox>
</Tooltip>
<Tooltip :open="!focusedTrainingReport.course.hasQual"
message="This course does not have a qualification">
<Checkbox :disabled="!focusedTrainingReport.course.hasQual"
:model-value="person.passed_qual" class="pointer-events-none ml-1">
</Checkbox>
</Tooltip>
<p class="col-span-2 text-right px-2"
:class="person.remarks == '' ? 'text-muted-foreground' : ''">
{{ person.remarks == "" ?
'--'
: person.remarks }}</p>
</div> </div>
</div> </div>
<!-- No Shows --> <!-- No Shows -->
<div v-if="focusedNoShows.length != 0"> <div v-if="focusedNoShows.length != 0">
<div class="flex flex-col"> <div class="flex flex-col">
<label class="scroll-m-20 text-xl font-semibold tracking-tight">No Shows</label> <label class="scroll-m-20 text-xl font-semibold tracking-tight">
<div class="grid grid-cols-5 py-2 text-sm font-medium text-muted-foreground border-b"> No Shows
</label>
<div
class="grid grid-cols-[1fr_5rem_5rem_2fr_3rem] py-2 text-sm font-medium text-muted-foreground border-b">
<span>Name</span> <span>Name</span>
<div></div> <span></span>
<div></div> <span></span>
<span class="text-right col-span-2">Remarks</span> <span class="text-right">Remarks</span>
<span></span>
</div> </div>
</div> </div>
<div v-for="person in focusedNoShows"
class="grid grid-cols-5 py-2 items-center border-b last:border-none"> <div v-for="person in focusedNoShows" :key="person.attendee_id ?? person.attendee_name"
<div> class="border-b last:border-none transition-colors"
<MemberCard v-if="person.attendee_id" :member-id="person.attendee_id" :class="expanded === person.attendee_id && 'bg-muted/20'">
class="justify-self-start"></MemberCard> <!-- Row -->
<p v-else>{{ person.attendee_name }}</p> <div class="grid grid-cols-[1fr_5rem_5rem_2fr_3rem] py-2 items-center">
<!-- Name -->
<div>
<MemberCard v-if="person.attendee_id" :member-id="person.attendee_id" />
<p v-else>{{ person.attendee_name }}</p>
</div>
<div></div>
<div></div>
<p class="text-right px-2 truncate" :class="!person.remarks && 'text-muted-foreground'">
{{ person.remarks || '--' }}
</p>
<Button variant="ghost" size="icon" @click.stop="expanded === person.attendee_id
? expanded = null
: expanded = person.attendee_id">
<ChevronDown v-if="expanded !== person.attendee_id" class="size-5" />
<ChevronUp v-else class="size-5" />
</Button>
</div>
<div v-if="expanded === person.attendee_id" class="col-span-full">
<div class="px-6 py-4 space-y-2">
<p class="text-sm font-medium text-foreground">
Remarks
</p>
<p v-if="person.remarks"
class="text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto">
{{ person.remarks }}
</p>
<p v-else class="text-sm text-muted-foreground italic">
None provided
</p>
</div>
</div> </div>
<div></div>
<div></div>
<p class="col-span-2 text-right px-2"
:class="person.remarks == '' ? 'text-muted-foreground' : ''">
{{ person.remarks == "" ?
'--'
: person.remarks }}</p>
</div> </div>
</div> </div>
<div> <div>
<label class="scroll-m-20 text-xl font-semibold tracking-tight">Remarks</label> <label class="scroll-m-20 text-xl font-semibold tracking-tight">Remarks</label>
@@ -370,33 +463,3 @@ function setPage(pagenum: number) {
</div> </div>
</div> </div>
</template> </template>
<style scoped>
/* Firefox */
.scrollbar-themed {
scrollbar-width: thin;
scrollbar-color: #555 #1f1f1f;
padding-right: 6px;
}
/* Chrome, Edge, Safari */
.scrollbar-themed::-webkit-scrollbar {
width: 10px;
/* slightly wider to allow padding look */
}
.scrollbar-themed::-webkit-scrollbar-track {
background: #1f1f1f;
margin-left: 6px;
/* ❗ adds space between content + scrollbar */
}
.scrollbar-themed::-webkit-scrollbar-thumb {
background: #555;
border-radius: 9999px;
}
.scrollbar-themed::-webkit-scrollbar-thumb:hover {
background: #777;
}
</style>