Added mobile friendly display to the training reports page

This commit is contained in:
2026-03-22 10:14:53 -04:00
parent a05b48cd4c
commit b64891c2a2
2 changed files with 302 additions and 145 deletions

View File

@@ -6,8 +6,11 @@
import AlertDescription from './components/ui/alert/AlertDescription.vue'; import AlertDescription from './components/ui/alert/AlertDescription.vue';
import Navbar from './components/Navigation/Navbar.vue'; import Navbar from './components/Navigation/Navbar.vue';
import { cancelLOA } from './api/loa'; import { cancelLOA } from './api/loa';
import { onBeforeUnmount, onMounted, ref } from 'vue';
const userStore = useUserStore(); const userStore = useUserStore();
const headerRef = ref<HTMLDivElement | null>(null);
let resizeObserver: ResizeObserver | null = null;
function formatDate(dateStr) { function formatDate(dateStr) {
if (!dateStr) return ""; if (!dateStr) return "";
@@ -22,6 +25,27 @@
const environment = import.meta.env.VITE_ENVIRONMENT; const environment = import.meta.env.VITE_ENVIRONMENT;
//@ts-ignore //@ts-ignore
const version = import.meta.env.VITE_APPLICATION_VERSION; const version = import.meta.env.VITE_APPLICATION_VERSION;
function updateHeaderHeight() {
if (!headerRef.value) return;
const height = headerRef.value.offsetHeight;
document.documentElement.style.setProperty('--app-header-height', `${height}px`);
}
onMounted(() => {
updateHeaderHeight();
// Gracefully skip observer setup for environments that do not support ResizeObserver.
if (typeof ResizeObserver === 'undefined' || !headerRef.value) return;
resizeObserver = new ResizeObserver(updateHeaderHeight);
resizeObserver.observe(headerRef.value);
});
onBeforeUnmount(() => {
resizeObserver?.disconnect();
});
</script> </script>
<template> <template>
@@ -29,7 +53,7 @@
background-size: contain; background-size: contain;
background-attachment: fixed; background-attachment: fixed;
background-position: center;"> background-position: center;">
<div class="sticky top-0 bg-background z-50"> <div class="sticky top-0 bg-background z-50" ref="headerRef">
<Navbar class="flex"></Navbar> <Navbar class="flex"></Navbar>
<Alert v-if="environment == 'dev'" class="m-2 mx-auto max-w-5xl" variant="info"> <Alert v-if="environment == 'dev'" class="m-2 mx-auto max-w-5xl" variant="info">
<AlertDescription class="flex flex-row items-center text-wrap gap-5 mx-auto"> <AlertDescription class="flex flex-row items-center text-wrap gap-5 mx-auto">

View File

@@ -205,7 +205,7 @@ const expanded = ref<number>(null);
<div class="mx-auto mt-5 flex w-full max-w-[100rem] flex-col px-3 sm:px-4 lg:flex-row lg:px-8 xl:px-20"> <div class="mx-auto mt-5 flex w-full max-w-[100rem] flex-col px-3 sm:px-4 lg:flex-row lg:px-8 xl:px-20">
<!-- training report list --> <!-- training report list -->
<div v-show="!isMobile || effectivePanel === sidePanelState.closed" class="my-3 px-0 lg:px-4" <div v-show="!isMobile || effectivePanel === sidePanelState.closed" class="my-3 px-0 lg:px-4"
:class="effectivePanel == sidePanelState.closed ? 'w-full' : 'w-full lg:w-2/5'"> :class="effectivePanel == sidePanelState.closed ? 'w-full' : 'w-full lg:flex-[0_0_26rem] xl:flex-[0_0_30rem]'">
<div class="flex justify-between mb-4"> <div class="flex justify-between mb-4">
<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="openCreatePanel()"> <Button @click="openCreatePanel()">
@@ -323,9 +323,8 @@ const expanded = ref<number>(null);
</div> </div>
<!-- view training report section --> <!-- view training report section -->
<div v-if="focusedTrainingReport != null && effectivePanel == sidePanelState.view" <div v-if="focusedTrainingReport != null && effectivePanel == sidePanelState.view"
:class="isMobile :class="isMobile ? 'fixed inset-0 z-[60] overflow-y-auto px-3 py-3' : 'my-3 mt-2 w-full min-w-0 lg:mt-0 lg:flex-1 lg:border-l lg:pl-9'"
? 'fixed inset-0 z-[60] overflow-y-auto bg-background px-3 py-3' :style="isMobile ? { top: 'var(--app-header-height, 60px)' } : {}">
: 'my-3 mt-2 w-full lg:mt-0 lg:w-3/5 lg:border-l lg:pl-9'">
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<p class="scroll-m-20 text-2xl font-semibold tracking-tight">Training Report Details</p> <p class="scroll-m-20 text-2xl font-semibold tracking-tight">Training Report Details</p>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
@@ -340,11 +339,11 @@ const expanded = ref<number>(null);
</Button> </Button>
</div> </div>
</div> </div>
<div v-if="TRLoaded" :class="isMobile ? 'my-3 pb-8' : 'max-h-[70vh] overflow-auto scrollbar-themed my-5'"> <div v-if="TRLoaded" :class="isMobile ? 'my-3 pb-8' : 'my-5 max-h-[70vh] overflow-y-auto overflow-x-hidden scrollbar-themed'">
<div class="flex flex-col mb-5 border rounded-lg bg-muted/70 p-2 py-3 px-4"> <div class="flex flex-col mb-5 border rounded-lg bg-muted/70 p-2 py-3 px-4">
<p class="scroll-m-20 text-xl font-semibold tracking-tight">{{ focusedTrainingReport.course_name }} <p class="scroll-m-20 text-xl font-semibold tracking-tight">{{ focusedTrainingReport.course_name }}
</p> </p>
<div class="flex gap-10 items-center"> <div class="mt-2 flex flex-col gap-2 text-sm sm:flex-row sm:items-center sm:gap-10">
<p class="text-muted-foreground">{{ formatDate(focusedTrainingReport.event_date) }}</p> <p class="text-muted-foreground">{{ formatDate(focusedTrainingReport.event_date) }}</p>
<div class="flex gap-2 items-center">Created by: <div class="flex gap-2 items-center">Created by:
<MemberCard v-if="focusedTrainingReport.created_by" <MemberCard v-if="focusedTrainingReport.created_by"
@@ -357,51 +356,91 @@ const expanded = ref<number>(null);
<!-- 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 <div class="mt-3 space-y-2 md:hidden">
class="grid grid-cols-[1fr_1fr_2fr_3rem] py-2 text-sm font-medium text-muted-foreground border-b"> <article v-for="person in focusedTrainingTrainers" :key="person.attendee_id ?? person.attendee_name"
<span>Name</span> class="rounded-xl border bg-card p-3 shadow-sm" :class="expanded === person.attendee_id && 'ring-1 ring-primary/30'">
<span class="">Role</span> <div class="flex items-start justify-between gap-2">
<span class="text-right">Remarks</span> <div class="min-w-0">
<span></span> <MemberCard v-if="person.attendee_id" :member-id="person.attendee_id"
</div> class="max-w-full whitespace-nowrap overflow-hidden text-ellipsis"></MemberCard>
<div v-for="person in focusedTrainingTrainers" class=" items-center border-b last:border-none" <p v-else class="font-medium whitespace-nowrap overflow-hidden text-ellipsis">{{ person.attendee_name }}</p>
:class="expanded === person.attendee_id && 'bg-muted/20'"> </div>
<div class="grid grid-cols-[1fr_1fr_2fr_3rem] items-center py-2"> <Button @click.stop="expanded === person.attendee_id ? expanded = null : expanded = person.attendee_id"
<div> variant="ghost" size="icon" class="shrink-0">
<MemberCard v-if="person.attendee_id" :member-id="person.attendee_id" <ChevronDown v-if="expanded !== person.attendee_id" class="size-5" />
class="justify-self-start"></MemberCard> <ChevronUp v-else class="size-5" />
<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> </Button>
</div> </div>
</div> <div class="mt-3 grid grid-cols-1 gap-2 text-xs">
<div class="col-span-full" v-if="expanded === person.attendee_id"> <div class="rounded-lg bg-muted px-3 py-2">
<div class="px-6 pb-5 pt-2 space-y-2"> <p class="text-muted-foreground">Role</p>
<p class="text-sm font-medium text-foreground"> <p class="font-medium text-foreground">{{ person.role.name }}</p>
Remarks </div>
</p> <div class="rounded-lg bg-muted px-3 py-2">
<p class="text-muted-foreground">Remarks</p>
<p class="font-medium text-foreground truncate">{{ person.remarks || '--' }}</p>
</div>
</div>
<div v-if="expanded === person.attendee_id" class="mt-3 rounded-lg border bg-background px-3 py-2">
<p class="text-sm font-medium text-foreground">Full Remarks</p>
<p v-if="person.remarks" <p v-if="person.remarks"
class="text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto"> class="mt-1 text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto">
{{ person.remarks }} {{ person.remarks }}
</p> </p>
<p v-else class="text-sm text-muted-foreground italic"> <p v-else class="mt-1 text-sm text-muted-foreground italic">
None provided None provided
</p> </p>
</div> </div>
</article>
</div>
<div class="mt-2 hidden md:block">
<div
class="grid grid-cols-[minmax(0,1.6fr)_minmax(0,1fr)_minmax(0,1.4fr)_2.5rem] py-2 text-sm font-medium text-muted-foreground border-b gap-x-2">
<span>Name</span>
<span>Role</span>
<span class="text-right">Remarks</span>
<span></span>
</div>
<div v-for="person in focusedTrainingTrainers" :key="person.attendee_id ?? person.attendee_name"
class="items-center border-b last:border-none"
:class="expanded === person.attendee_id && 'bg-muted/20'">
<div class="grid grid-cols-[minmax(0,1.6fr)_minmax(0,1fr)_minmax(0,1.4fr)_2.5rem] items-center py-2 gap-x-2">
<div class="min-w-0">
<MemberCard v-if="person.attendee_id" :member-id="person.attendee_id"
class="justify-self-start max-w-full whitespace-nowrap overflow-hidden text-ellipsis"></MemberCard>
<p v-else class="whitespace-nowrap overflow-hidden text-ellipsis">{{ person.attendee_name }}</p>
</div>
<p class="truncate">{{ 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> </div>
</div> </div>
</div> </div>
@@ -409,63 +448,110 @@ const expanded = ref<number>(null);
<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 <div class="mt-3 space-y-2 md:hidden">
class="grid grid-cols-[1fr_5rem_5rem_2fr_3rem] py-2 text-sm font-medium text-muted-foreground border-b px-2"> <article v-for="person in focusedTrainingTrainees" :key="person.attendee_id ?? person.attendee_name"
<span>Name</span> class="rounded-xl border bg-card p-3 shadow-sm" :class="expanded === person.attendee_id && 'ring-1 ring-primary/30'">
<span class="text-center">Bookwork</span> <div class="flex items-start justify-between gap-2">
<span class="text-center">Qual</span> <div class="min-w-0">
<span class="text-right">Remarks</span> <MemberCard v-if="person.attendee_id" :member-id="person.attendee_id"
<span class="w-15"></span> class="max-w-full whitespace-nowrap overflow-hidden text-ellipsis"></MemberCard>
<p v-else class="font-medium whitespace-nowrap overflow-hidden text-ellipsis">{{ person.attendee_name }}</p>
</div>
<Button @click.stop="expanded === person.attendee_id ? expanded = null : expanded = person.attendee_id"
variant="ghost" size="icon" class="shrink-0">
<ChevronDown v-if="expanded !== person.attendee_id" class="size-5" />
<ChevronUp v-else class="size-5" />
</Button>
</div>
<div class="mt-3 grid grid-cols-2 gap-2 text-xs">
<div class="rounded-lg bg-muted px-3 py-2 text-center">
<p class="text-muted-foreground">Bookwork</p>
<p class="font-semibold text-foreground">{{ !focusedTrainingReport.course.hasBookwork ? 'N/A' : (person.passed_bookwork ? 'Pass' : 'Fail') }}</p>
</div>
<div class="rounded-lg bg-muted px-3 py-2 text-center">
<p class="text-muted-foreground">Qual</p>
<p class="font-semibold text-foreground">{{ !focusedTrainingReport.course.hasQual ? 'N/A' : (person.passed_qual ? 'Pass' : 'Fail') }}</p>
</div>
<div class="col-span-2 rounded-lg bg-muted px-3 py-2">
<p class="text-muted-foreground">Remarks</p>
<p class="font-medium text-foreground truncate">{{ person.remarks || '--' }}</p>
</div>
</div>
<div v-if="expanded === person.attendee_id" class="mt-3 rounded-lg border bg-background px-3 py-2">
<p class="text-sm font-medium text-foreground">Full Remarks</p>
<p v-if="person.remarks"
class="mt-1 text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto">
{{ person.remarks }}
</p>
<p v-else class="mt-1 text-sm text-muted-foreground italic">
None provided
</p>
</div>
</article>
</div> </div>
</div> <div class="hidden md:block">
<div v-for="person in focusedTrainingTrainees" class="border-b last:border-none" <div
:class="expanded === person.attendee_id && 'bg-muted/20'"> class="grid grid-cols-[minmax(0,1.6fr)_4.25rem_4.25rem_minmax(0,1.4fr)_2.5rem] py-2 text-sm font-medium text-muted-foreground border-b px-2 gap-x-2">
<div class="grid grid-cols-[1fr_5rem_5rem_2fr_3rem] py-2 items-center mx-2"> <span>Name</span>
<div> <span class="text-center">Bookwork</span>
<MemberCard v-if="person.attendee_id" :member-id="person.attendee_id" <span class="text-center">Qual</span>
class="justify-self-start"></MemberCard> <span class="text-right">Remarks</span>
<p v-else>{{ person.attendee_name }}</p> <span class="w-15"></span>
</div> </div>
<Tooltip :open="!focusedTrainingReport.course.hasBookwork" class="mx-auto" <div v-for="person in focusedTrainingTrainees" :key="person.attendee_id ?? person.attendee_name" class="border-b last:border-none"
message="This course does not have bookwork"> :class="expanded === person.attendee_id && 'bg-muted/20'">
<Checkbox :disabled="!focusedTrainingReport.course.hasBookwork" <div class="grid grid-cols-[minmax(0,1.6fr)_4.25rem_4.25rem_minmax(0,1.4fr)_2.5rem] py-2 items-center px-2 gap-x-2">
:model-value="person.passed_bookwork" class="pointer-events-none"> <div class="min-w-0">
</Checkbox> <MemberCard v-if="person.attendee_id" :member-id="person.attendee_id"
</Tooltip> class="justify-self-start max-w-full whitespace-nowrap overflow-hidden text-ellipsis"></MemberCard>
<Tooltip :open="!focusedTrainingReport.course.hasQual" class="mx-auto" <p v-else class="whitespace-nowrap overflow-hidden text-ellipsis">{{ person.attendee_name }}</p>
message="This course does not have a qualification"> </div>
<Checkbox :disabled="!focusedTrainingReport.course.hasQual" <div class="flex justify-center">
:model-value="person.passed_qual" class="pointer-events-none ml-1"> <Tooltip :open="!focusedTrainingReport.course.hasBookwork"
</Checkbox> message="This course does not have bookwork">
</Tooltip> <Checkbox :disabled="!focusedTrainingReport.course.hasBookwork"
<p class="text-right truncate" :model-value="person.passed_bookwork" class="pointer-events-none">
:class="person.remarks == '' ? 'text-muted-foreground' : ''"> </Checkbox>
{{ person.remarks == "" ? </Tooltip>
'--' </div>
: person.remarks }}</p> <div class="flex justify-center">
<div class="w-min mx-auto"> <Tooltip :open="!focusedTrainingReport.course.hasQual"
<Button v-if="expanded === person.attendee_id" @click.stop="expanded = null" message="This course does not have a qualification">
variant="ghost" size="icon"> <Checkbox :disabled="!focusedTrainingReport.course.hasQual"
<ChevronUp class="size-6" /> :model-value="person.passed_qual" class="pointer-events-none">
</Button> </Checkbox>
<Button v-else @click.stop="expanded = person.attendee_id" variant="ghost" </Tooltip>
size="icon"> </div>
<ChevronDown class="size-6" /> <p class="text-right truncate"
</Button> :class="person.remarks == '' ? 'text-muted-foreground' : ''">
</div> {{ person.remarks == "" ?
</div> '--'
<div class="col-span-full" v-if="expanded === person.attendee_id"> : person.remarks }}</p>
<div class="px-6 pb-5 pt-2 space-y-2"> <div class="w-min mx-auto">
<p class="text-sm font-medium text-foreground"> <Button v-if="expanded === person.attendee_id" @click.stop="expanded = null"
Remarks variant="ghost" size="icon">
</p> <ChevronUp class="size-6" />
<p v-if="person.remarks" </Button>
class="text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto"> <Button v-else @click.stop="expanded = person.attendee_id" variant="ghost"
{{ person.remarks }} size="icon">
</p> <ChevronDown class="size-6" />
<p v-else class="text-sm text-muted-foreground italic"> </Button>
None provided </div>
</p> </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> </div>
</div> </div>
</div> </div>
@@ -477,60 +563,108 @@ const expanded = ref<number>(null);
No Shows No Shows
</label> </label>
<div <div class="mt-3 space-y-2 md:hidden">
class="grid grid-cols-[1fr_5rem_5rem_2fr_3rem] py-2 text-sm font-medium text-muted-foreground border-b"> <article v-for="person in focusedNoShows" :key="person.attendee_id ?? person.attendee_name"
<span>Name</span> class="rounded-xl border bg-card p-3 shadow-sm" :class="expanded === person.attendee_id && 'ring-1 ring-primary/30'">
<span></span> <div class="flex items-start justify-between gap-2">
<span></span> <div class="min-w-0">
<span class="text-right">Remarks</span> <MemberCard v-if="person.attendee_id" :member-id="person.attendee_id"
<span></span> class="max-w-full whitespace-nowrap overflow-hidden text-ellipsis" />
</div> <p v-else class="font-medium whitespace-nowrap overflow-hidden text-ellipsis">{{ person.attendee_name }}</p>
</div> </div>
<Button variant="ghost" size="icon" class="shrink-0" @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 class="mt-3 grid grid-cols-2 gap-2 text-xs">
<div class="rounded-lg bg-muted px-3 py-2 text-center">
<p class="text-muted-foreground">Bookwork</p>
<p class="font-semibold text-foreground">N/A</p>
</div>
<div class="rounded-lg bg-muted px-3 py-2 text-center">
<p class="text-muted-foreground">Qual</p>
<p class="font-semibold text-foreground">N/A</p>
</div>
<div class="col-span-2 rounded-lg bg-muted px-3 py-2">
<p class="text-muted-foreground">Remarks</p>
<p class="font-medium text-foreground truncate">{{ person.remarks || '--' }}</p>
</div>
</div>
<div v-for="person in focusedNoShows" :key="person.attendee_id ?? person.attendee_name" <div v-if="expanded === person.attendee_id" class="mt-3 rounded-lg border bg-background px-3 py-2">
class="border-b last:border-none transition-colors" <p class="text-sm font-medium text-foreground">
:class="expanded === person.attendee_id && 'bg-muted/20'"> Full Remarks
<!-- Row --> </p>
<div class="grid grid-cols-[1fr_5rem_5rem_2fr_3rem] py-2 items-center">
<!-- Name --> <p v-if="person.remarks"
<div> class="mt-1 text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto">
<MemberCard v-if="person.attendee_id" :member-id="person.attendee_id" /> {{ person.remarks }}
<p v-else>{{ person.attendee_name }}</p> </p>
<p v-else class="mt-1 text-sm text-muted-foreground italic">
None provided
</p>
</div>
</article>
</div>
<div class="hidden md:block">
<div
class="grid grid-cols-[minmax(0,1.6fr)_4.25rem_4.25rem_minmax(0,1.4fr)_2.5rem] py-2 text-sm font-medium text-muted-foreground border-b gap-x-2">
<span>Name</span>
<span class="text-center">Bookwork</span>
<span class="text-center">Qual</span>
<span class="text-right">Remarks</span>
<span></span>
</div> </div>
<div></div> <div v-for="person in focusedNoShows" :key="person.attendee_id ?? person.attendee_name"
<div></div> class="border-b last:border-none transition-colors"
:class="expanded === person.attendee_id && 'bg-muted/20'">
<div class="grid grid-cols-[minmax(0,1.6fr)_4.25rem_4.25rem_minmax(0,1.4fr)_2.5rem] py-2 items-center gap-x-2">
<div class="min-w-0">
<MemberCard v-if="person.attendee_id" :member-id="person.attendee_id"
class="max-w-full whitespace-nowrap overflow-hidden text-ellipsis" />
<p v-else class="whitespace-nowrap overflow-hidden text-ellipsis">{{ person.attendee_name }}</p>
</div>
<p class="text-right px-2 truncate" :class="!person.remarks && 'text-muted-foreground'"> <p class="text-center text-muted-foreground">-</p>
{{ person.remarks || '--' }} <p class="text-center text-muted-foreground">-</p>
</p>
<Button variant="ghost" size="icon" @click.stop="expanded === person.attendee_id <p class="text-right px-2 truncate" :class="!person.remarks && 'text-muted-foreground'">
? expanded = null {{ person.remarks || '--' }}
: expanded = person.attendee_id"> </p>
<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"> <Button variant="ghost" size="icon" @click.stop="expanded === person.attendee_id
<div class="px-6 py-4 space-y-2"> ? expanded = null
<p class="text-sm font-medium text-foreground"> : expanded = person.attendee_id">
Remarks <ChevronDown v-if="expanded !== person.attendee_id" class="size-5" />
</p> <ChevronUp v-else class="size-5" />
</Button>
</div>
<p v-if="person.remarks" <div v-if="expanded === person.attendee_id" class="col-span-full">
class="text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto"> <div class="px-6 py-4 space-y-2">
{{ person.remarks }} <p class="text-sm font-medium text-foreground">
</p> Remarks
</p>
<p v-else class="text-sm text-muted-foreground italic"> <p v-if="person.remarks"
None provided class="text-sm text-muted-foreground leading-relaxed whitespace-pre-wrap max-h-72 overflow-y-auto">
</p> {{ person.remarks }}
</p>
<p v-else class="text-sm text-muted-foreground italic">
None provided
</p>
</div>
</div>
</div> </div>
</div> </div>
</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>
@@ -544,9 +678,8 @@ const expanded = ref<number>(null);
<Spinner class="size-8"></Spinner> <Spinner class="size-8"></Spinner>
</div> </div>
</div> </div>
<div v-if="effectivePanel == sidePanelState.create" :class="isMobile <div v-if="effectivePanel == sidePanelState.create" :class="isMobile ? 'fixed inset-0 z-[60] overflow-y-auto bg-background px-3 py-3' : 'mt-2 w-full max-w-5xl lg:mt-0 lg:w-3/5 lg:border-l lg:pl-7'"
? 'fixed inset-0 z-[60] overflow-y-auto bg-background px-3 py-3' :style="isMobile ? { top: 'var(--app-header-height, 60px)' } : {}">
: 'mt-2 w-full max-w-5xl lg:mt-0 lg:w-3/5 lg:border-l lg:pl-7'">
<div class="flex justify-between items-center my-3"> <div class="flex justify-between items-center my-3">
<div class="flex gap-5 lg:pl-2"> <div class="flex gap-5 lg:pl-2">
<p class="scroll-m-20 text-2xl font-semibold tracking-tight">New Training Report</p> <p class="scroll-m-20 text-2xl font-semibold tracking-tight">New Training Report</p>