added searching and sorting system

This commit is contained in:
2025-11-22 11:32:51 -05:00
parent 9f2948ac18
commit 712941458a
16 changed files with 452 additions and 16 deletions

View File

@@ -10,11 +10,17 @@ import {
TableHeader,
TableRow,
} from '@/components/ui/table'
import { Plus, X } from 'lucide-vue-next';
import { ArrowUpDown, Funnel, Plus, Search, X } from 'lucide-vue-next';
import Button from '@/components/ui/button/Button.vue';
import TrainingReportForm from '@/components/trainingReport/trainingReportForm.vue';
import Checkbox from '@/components/ui/checkbox/Checkbox.vue';
import { useRoute, useRouter } from 'vue-router';
import Select from '@/components/ui/select/Select.vue';
import SelectTrigger from '@/components/ui/select/SelectTrigger.vue';
import SelectValue from '@/components/ui/select/SelectValue.vue';
import SelectContent from '@/components/ui/select/SelectContent.vue';
import SelectItem from '@/components/ui/select/SelectItem.vue';
import Input from '@/components/ui/input/Input.vue';
enum sidePanelState { view, create, closed };
@@ -60,8 +66,24 @@ async function closeTrainingReport() {
focusedTrainingReport.value = null;
}
const sortMode = ref<string>("descending");
const searchString = ref<string>("");
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
watch(searchString, (newValue) => {
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
loadTrainingReports();
}, 300); // 300ms debounce
});
watch(() => sortMode.value, async (newSortMode) => {
loadTrainingReports();
})
async function loadTrainingReports() {
trainingReports.value = await getTrainingReports();
trainingReports.value = await getTrainingReports(sortMode.value, searchString.value);
}
onMounted(async () => {
@@ -69,7 +91,6 @@ onMounted(async () => {
if (route.params.id)
viewTrainingReport(Number(route.params.id))
loaded.value = true;
console.log("load")
})
</script>
@@ -77,12 +98,41 @@ onMounted(async () => {
<div class="px-20 mx-auto max-w-[100rem] flex mt-5">
<!-- training report list -->
<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 mb-4">
<p class="scroll-m-20 text-2xl font-semibold tracking-tight">Training Reports</p>
<Button @click="router.push('/trainingReport/new')">
<Plus></Plus> New Training Report
</Button>
</div>
<!-- search/filter -->
<div class="flex justify-between">
<!-- <Search></Search>
<Funnel></Funnel> -->
<div></div>
<div class="flex flex-row gap-5">
<div>
<label class="text-muted-foreground">Search</label>
<Input v-model="searchString"></Input>
</div>
<div class="flex flex-col">
<label class="text-muted-foreground">Sort By</label>
<Select v-model="sortMode">
<SelectTrigger>
<SelectValue placeholder="Sort By" />
<!-- <ArrowUpDown></ArrowUpDown> -->
</SelectTrigger>
<SelectContent>
<SelectItem value="ascending">
Date (Ascending)
</SelectItem>
<SelectItem value="descending">
Date (Descending)
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</div>
<div class="overflow-y-auto max-h-[70vh] mt-5 scrollbar-themed">
<Table>
<TableHeader class="">
@@ -120,12 +170,13 @@ onMounted(async () => {
</div>
<div class="max-h-[70vh] overflow-auto scrollbar-themed my-5">
<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>
<p class="scroll-m-20 text-xl font-semibold tracking-tight">{{ focusedTrainingReport.course_name }}
</p>
<div class="flex gap-10">
<p class="text-muted-foreground">{{ focusedTrainingReport.event_date }}</p>
<p class="">Created by {{ focusedTrainingReport.created_by_name === null ? "Unknown User" :
focusedTrainingReport.created_by_name
}}
}}
</p>
</div>
</div>
@@ -143,7 +194,9 @@ onMounted(async () => {
<p>{{ person.attendee_name }}</p>
<p class="">{{ person.role.name }}</p>
<p class="col-span-2 text-right px-2"
:class="person.remarks == '' ? 'text-muted-foreground' : ''">{{ person.remarks == "" ? '--'
:class="person.remarks == '' ? 'text-muted-foreground' : ''">
{{ person.remarks == "" ?
'--'
: person.remarks }}</p>
</div>
</div>
@@ -168,7 +221,9 @@ onMounted(async () => {
:model-value="person.passed_qual" class="pointer-events-none ml-1">
</Checkbox>
<p class="col-span-2 text-right px-2"
:class="person.remarks == '' ? 'text-muted-foreground' : ''">{{ person.remarks == "" ? '--'
:class="person.remarks == '' ? 'text-muted-foreground' : ''">
{{ person.remarks == "" ?
'--'
: person.remarks }}</p>
</div>
</div>
@@ -195,7 +250,9 @@ onMounted(async () => {
<div></div>
<div></div>
<p class="col-span-2 text-right px-2"
:class="person.remarks == '' ? 'text-muted-foreground' : ''">{{ person.remarks == "" ? '--'
:class="person.remarks == '' ? 'text-muted-foreground' : ''">
{{ person.remarks == "" ?
'--'
: person.remarks }}</p>
</div>
</div>
@@ -219,7 +276,8 @@ onMounted(async () => {
</div>
<div class="overflow-y-auto max-h-[70vh] mt-5 scrollbar-themed">
<TrainingReportForm class="w-full pl-2"
@submit="(newID) => { router.push(`/trainingReport/${newID}`); loadTrainingReports() }"></TrainingReportForm>
@submit="(newID) => { router.push(`/trainingReport/${newID}`); loadTrainingReports() }">
</TrainingReportForm>
</div>
</div>
</div>