inspection plan execute

This commit is contained in:
Julian Krauser 2025-05-16 10:27:08 +02:00
parent b359044cb5
commit 70e9b47483
17 changed files with 429 additions and 134 deletions

View file

@ -0,0 +1,63 @@
<template>
<div class="flex flex-col h-fit w-full border border-primary rounded-md">
<div class="bg-primary p-2 text-white flex flex-row justify-between items-center">
<p>{{ inspectionPoint.title }}</p>
</div>
<div class="p-2">
<p v-if="inspectionPoint.description" class="pb-2">Beschreibung: {{ inspectionPoint.description }}</p>
<hr v-if="inspectionPoint.description" />
<RadioGroup v-model="value" :name="inspectionPoint.id" class="flex flex-row gap-2">
<RadioGroupOption
v-for="option in options"
:key="option.key"
button
:primary="value == option.key"
:primary-outline="value != option.key"
:value="option.key"
>
{{ option.title }}
</RadioGroupOption>
</RadioGroup>
</div>
</div>
</template>
<script setup lang="ts">
import { defineComponent, type PropType } from "vue";
import { RadioGroup, RadioGroupLabel, RadioGroupOption } from "@headlessui/vue";
import type { InspectionPointViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
</script>
<script lang="ts">
export default defineComponent({
props: {
inspectionPoint: {
type: Object as PropType<InspectionPointViewModel>,
required: true,
},
modelValue: {
type: String as PropType<"true" | "false">,
default: "false",
},
},
emits: ["update:model-value"],
data() {
return {
options: [
{ key: "true", title: "OK" },
{ key: "false", title: "nicht OK" },
],
};
},
computed: {
value: {
get() {
return this.modelValue;
},
set(val: string) {
this.$emit("update:model-value", val);
},
},
},
});
</script>

View file

@ -0,0 +1,52 @@
<template>
<div class="flex flex-col h-fit w-full border border-primary rounded-md">
<div class="bg-primary p-2 text-white flex flex-row justify-between items-center">
<p>{{ inspectionPoint.title }}</p>
</div>
<div class="p-2">
<p v-if="inspectionPoint.description" class="pb-2">Beschreibung: {{ inspectionPoint.description }}</p>
<hr v-if="inspectionPoint.description" />
<label :for="inspectionPoint.id">{{ inspectionPoint.type == "number" ? "Zahl" : "Freitext" }}</label>
<input
v-if="inspectionPoint.type == 'number'"
:id="inspectionPoint.id"
:name="inspectionPoint.id"
type="number"
required
v-model="value"
/>
<textarea v-else :id="inspectionPoint.id" :name="inspectionPoint.id" required class="h-18" v-model="value" />
</div>
</div>
</template>
<script setup lang="ts">
import { defineComponent, type PropType } from "vue";
import type { InspectionPointViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
</script>
<script lang="ts">
export default defineComponent({
props: {
inspectionPoint: {
type: Object as PropType<InspectionPointViewModel>,
required: true,
},
modelValue: {
type: String,
default: "",
},
},
emits: ["update:model-value"],
computed: {
value: {
get() {
return this.modelValue;
},
set(val: string | number) {
this.$emit("update:model-value", val.toString());
},
},
},
});
</script>

View file

@ -1,10 +1,9 @@
import type { InspectionPlanViewModel } from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
import type {
InspectionPlanViewModel,
InspectionPointResultViewModel,
InspectionPointViewModel,
InspectionPointResultViewModel,
InspectionViewModel,
} from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
import { equipmentTypeDemoData } from "./equipmentType";
} from "@/viewmodels/admin/unit/inspection/inspection.models";
export const inspectionPointDemoData: Array<InspectionPointViewModel> = [
{
@ -72,25 +71,19 @@ export const inspectionPointResultDemoData: Array<InspectionPointResultViewModel
{
inspectionId: "jkvshdfg",
inspectionVersionedPlanId: inspectionPlanDemoData[0].id,
inspectionVersionedPlan: inspectionPlanDemoData[0],
inspectionPointId: inspectionPointDemoData[0].id,
inspectionPoint: inspectionPointDemoData[0],
value: "5",
},
{
inspectionId: "jkvshdfg",
inspectionVersionedPlanId: inspectionPlanDemoData[0].id,
inspectionPointId: inspectionPointDemoData[1].id,
value: "",
},
{
inspectionId: "jkvshdfg",
inspectionVersionedPlanId: inspectionPlanDemoData[0].id,
inspectionVersionedPlan: inspectionPlanDemoData[0],
inspectionPointId: inspectionPointDemoData[1].id,
inspectionPoint: inspectionPointDemoData[1],
value: "",
},
{
inspectionId: "jkvshdfg",
inspectionVersionedPlanId: inspectionPlanDemoData[0].id,
inspectionVersionedPlan: inspectionPlanDemoData[0],
inspectionPointId: inspectionPointDemoData[1].id,
inspectionPoint: inspectionPointDemoData[1],
value: "",
},
];

View file

@ -68,23 +68,23 @@ body {
/*:not([headlessui]):not([id*="headlessui"]):not([class*="headlessui"])*/
button:not([class*="ql"] *):not([class*="fc"]):not([id*="headlessui-combobox"]),
a[button] {
[button] {
@apply cursor-pointer relative box-border h-10 w-full flex justify-center py-2 px-4 text-sm font-medium rounded-md focus:outline-hidden focus:ring-0;
}
button[primary]:not([primary="false"]),
a[button][primary]:not([primary="false"]) {
[button][primary]:not([primary="false"]) {
@apply border-2 border-transparent text-white bg-primary hover:bg-primary;
}
button[primary-outline]:not([primary-outline="false"]),
a[button][primary-outline]:not([primary-outline="false"]) {
[button][primary-outline]:not([primary-outline="false"]) {
@apply border-2 border-primary text-black hover:bg-primary;
}
button:disabled,
a[button]:disabled,
a[button].disabled {
[button]:disabled,
[button].disabled {
@apply opacity-75 pointer-events-none;
}

View file

@ -18,6 +18,7 @@ import { resetRespiratoryMissionStores, setRespiratoryMissionId } from "./unit/r
import { resetWearableStores, setWearableId } from "./unit/wearable";
import { resetInspectionPlanStores, setInspectionPlanId } from "./unit/inspectionPlan";
import { setVehicleTypeId } from "./unit/vehicleType";
import { resetInspectionStores, setInspectionId } from "./unit/inspection";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@ -860,7 +861,7 @@ const router = createRouter({
name: "admin-unit-inspection-route",
component: () => import("@/views/RouterView.vue"),
meta: { type: "create", section: "unit", module: "inspection" },
beforeEnter: [abilityAndNavUpdate],
beforeEnter: [abilityAndNavUpdate, resetInspectionStores],
children: [
{
path: "",
@ -875,10 +876,10 @@ const router = createRouter({
props: true,
},
{
path: "execute/:inspection",
path: "execute/:inspectionId",
name: "admin-unit-inspection-execute",
component: () => import("@/views/admin/unit/inspection/InspectionExecute.vue"),
beforeEnter: [],
beforeEnter: [setInspectionId],
props: true,
},
],

View file

@ -0,0 +1,20 @@
import { useInspectionStore } from "@/stores/admin/unit/inspection/inspection";
export async function setInspectionId(to: any, from: any, next: any) {
const InspectionStore = useInspectionStore();
InspectionStore.activeInspection = to.params?.inspectionId ?? null;
//useXYStore().$reset();
next();
}
export async function resetInspectionStores(to: any, from: any, next: any) {
const InspectionStore = useInspectionStore();
InspectionStore.activeInspection = null;
InspectionStore.activeInspectionObj = null;
//useXYStore().$reset();
next();
}

View file

@ -1,6 +1,6 @@
import { defineStore } from "pinia";
import { http } from "@/serverCom";
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
import { inspectionDemoData } from "@/demodata/inspectionPlan";
import { useEquipmentStore } from "./equipment";

View file

@ -0,0 +1,55 @@
import { defineStore } from "pinia";
import { http } from "@/serverCom";
import type { AxiosResponse } from "axios";
import { inspectionDemoData } from "@/demodata/inspectionPlan";
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
export const useInspectionStore = defineStore("inspection", {
state: () => {
return {
activeInspection: null as string | null,
activeInspectionObj: null as InspectionViewModel | null,
loadingActive: "loading" as "loading" | "fetched" | "failed",
};
},
actions: {
fetchInspectionByActiveId() {
this.loadingActive = "loading";
this.activeInspectionObj = inspectionDemoData.find((e) => e.id == this.activeInspection) as InspectionViewModel;
this.loadingActive = "fetched";
return;
this.loadingActive = "loading";
http
.get(`/admin/inspection/${this.activeInspection}`)
.then((res) => {
this.activeInspectionObj = res.data;
this.loadingActive = "fetched";
})
.catch((err) => {
this.loadingActive = "failed";
});
},
fetchInspectionById(id: string) {
return http.get(`/admin/inspection/${id}`);
},
async createInspection(inspection: any): Promise<AxiosResponse<any, any>> {
const result = await http.post(`/admin/inspection`, {
// TODO: data
});
this.fetchInspectionByActiveId();
return result;
},
async updateActiveInspection(inspection: any): Promise<AxiosResponse<any, any>> {
const result = await http.patch(`/admin/inspection/${inspection.id}`, {
// TODO: data
});
this.fetchInspectionByActiveId();
return result;
},
async deleteInspection(inspection: number): Promise<AxiosResponse<any, any>> {
const result = await http.delete(`/admin/inspection/${inspection}`);
this.fetchInspectionByActiveId();
return result;
},
},
});

View file

@ -1,6 +1,6 @@
import { defineStore } from "pinia";
import { http } from "@/serverCom";
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
import { inspectionDemoData } from "@/demodata/inspectionPlan";
import { useVehicleStore } from "./vehicle";

View file

@ -1,5 +1,5 @@
import type { EquipmentTypeViewModel } from "../equipmentType/equipmentType.models";
import type { InspectionViewModel } from "../inspectionPlan/inspectionPlan.models";
import type { InspectionViewModel } from "../inspection/inspection.models";
export interface EquipmentViewModel {
id: string;

View file

@ -0,0 +1,36 @@
import type { EquipmentViewModel } from "../equipment/equipment.models";
import type {
InspectionPlanViewModel,
InspectionVersionedPlanViewModel,
} from "../inspectionPlan/inspectionPlan.models";
import type { VehicleViewModel } from "../vehicle/vehicle.models";
export interface InspectionViewModel {
id: string;
inspectionPlanId: string;
inspectionPlan: InspectionPlanViewModel;
inspectionVersionedPlanId: string;
inspectionVersionedPlan: InspectionVersionedPlanViewModel;
context: string;
created: Date;
finished?: Date;
isOpen: boolean;
nextInspection?: Date;
checks: Array<InspectionPointResultViewModel>;
relatedId: string;
related: EquipmentViewModel | VehicleViewModel;
}
export interface InspectionPointViewModel {
id: string;
title: string;
description: string;
type: "iO-niO" | "text" | "number";
}
export interface InspectionPointResultViewModel {
inspectionId: string;
inspectionVersionedPlanId: string;
inspectionPointId: string;
value: string;
}

View file

@ -1,5 +1,6 @@
import type { EquipmentViewModel } from "../equipment/equipment.models";
import type { EquipmentTypeViewModel } from "../equipmentType/equipmentType.models";
import type { InspectionPointViewModel } from "../inspection/inspection.models";
import type { VehicleViewModel } from "../vehicle/vehicle.models";
import type { VehicleTypeViewModel } from "../vehicleType/vehicleType.models";
@ -38,35 +39,3 @@ export interface UpdateInspectionPlanViewModel {
inspectionInterval: PlanTimeDefinition;
remindTime?: PlanTimeDefinition;
}
export interface InspectionPointViewModel {
id: string;
title: string;
description: string;
type: "iO-niO" | "text" | "number";
}
export interface InspectionViewModel {
id: string;
inspectionPlanId: string;
inspectionPlan: InspectionPlanViewModel;
inspectionVersionedPlanId: string;
inspectionVersionedPlan: InspectionVersionedPlanViewModel;
context: string;
created: Date;
finished?: Date;
isOpen: boolean;
nextInspection?: Date;
checks: Array<InspectionPointResultViewModel>;
relatedId: string;
related: EquipmentViewModel | VehicleViewModel;
}
export interface InspectionPointResultViewModel {
inspectionId: string;
inspectionVersionedPlanId: string;
inspectionVersionedPlan: InspectionVersionedPlanViewModel;
inspectionPointId: string;
inspectionPoint: InspectionPointViewModel;
value: string;
}

View file

@ -1,4 +1,4 @@
import type { InspectionViewModel } from "../inspectionPlan/inspectionPlan.models";
import type { InspectionViewModel } from "../inspection/inspection.models";
import type { VehicleTypeViewModel } from "../vehicleType/vehicleType.models";
export interface VehicleViewModel {

View file

@ -9,7 +9,7 @@
>
<template #pageRow="{ row }: { row: InspectionViewModel }">
<RouterLink
:to="{ name: 'admin-unit-inspection-execute', params: { inspection: row.id } }"
:to="{ name: 'admin-unit-inspection-execute', params: { inspectionId: row.id } }"
class="flex flex-col h-fit w-full border border-primary rounded-md"
>
<div class="bg-primary p-2 text-white flex flex-row gap-2 items-center">
@ -44,7 +44,7 @@ import { useAbilityStore } from "@/stores/ability";
import { useEquipmentInspectionStore } from "@/stores/admin/unit/equipment/inspection";
import { PencilSquareIcon } from "@heroicons/vue/24/outline";
import Pagination from "@/components/Pagination.vue";
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
</script>
<script lang="ts">

View file

@ -2,29 +2,45 @@
<MainTemplate>
<template #topBar>
<div class="flex flex-row items-center justify-between pt-5 pb-3 px-7">
<h1 class="font-bold text-xl h-8">Prüfung durchführen</h1>
<h1 class="font-bold text-xl min-h-8">
Prüfung durchführen: {{ activeInspectionObj?.related.name }} - {{ activeInspectionObj?.inspectionPlan.title }}
</h1>
</div>
</template>
<template #diffMain>
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
<form class="flex flex-col gap-4 py-2 w-full max-w-xl mx-auto" @submit.prevent="">
<div class="flex flex-row justify-end gap-2">
<RouterLink
:to="{ name: 'admin-unit-inspection_plan' }"
primary-outline
button
class="w-fit!"
:disabled="status == 'loading' || status?.status == 'success'"
>
abbrechen
</RouterLink>
<button primary type="submit" class="w-fit!" :disabled="status == 'loading'">speichern</button>
<Spinner v-if="status == 'loading'" class="my-auto" />
<SuccessCheckmark v-else-if="status?.status == 'success'" />
<FailureXMark v-else-if="status?.status == 'failed'" />
</div>
</form>
</div>
<template #main>
<Spinner v-if="loading == 'loading'" class="mx-auto" />
<p v-else-if="loading == 'failed'">laden fehlgeschlagen</p>
<form
v-else-if="activeInspectionObj != null"
class="flex flex-col gap-4 py-2 w-full max-w-xl mx-auto"
@submit.prevent="triggerUpdate"
>
<div v-for="point in points" :key="point.title">
<OkNotOk
v-if="point.type == 'iO-niO'"
:inspectionPoint="point"
:modelValue="boolPointResult(point.id)"
@update:model-value="(val) => updateCheckResult(point.id, val)"
/>
<ResultInput
v-else
:inspectionPoint="point"
:modelValue="pointResult(point.id)"
@update:model-value="(val) => updateCheckResult(point.id, val)"
/>
</div>
<div class="flex flex-row justify-end gap-2">
<button primary-outline type="reset" class="w-fit!" :disabled="canSaveOrReset" @click="resetForm">
verwerfen
</button>
<button primary type="submit" class="w-fit!" :disabled="status == 'loading' || canSaveOrReset">
speichern
</button>
<Spinner v-if="status == 'loading'" class="my-auto" />
<SuccessCheckmark v-else-if="status?.status == 'success'" />
<FailureXMark v-else-if="status?.status == 'failed'" />
</div>
</form>
</template>
</MainTemplate>
</template>
@ -36,22 +52,109 @@ import MainTemplate from "@/templates/Main.vue";
import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue";
import ScanInput from "@/components/ScanInput.vue";
import InspectionPlanSearchSelect from "@/components/search/InspectionPlanSearchSelect.vue";
import EquipmentSearchSelect from "@/components/search/EquipmentSearchSelect.vue";
import VehicleSearchSelect from "@/components/search/VehicleSearchSelect.vue";
import { useInspectionStore } from "@/stores/admin/unit/inspection/inspection";
import OkNotOk from "@/components/admin/unit/inspection/OkNotOk.vue";
import ResultInput from "@/components/admin/unit/inspection/ResultInput.vue";
import type { InspectionPointResultViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
import cloneDeep from "lodash.clonedeep";
import isEqual from "lodash.isequal";
</script>
<script lang="ts">
export default defineComponent({
props: {
inspection: String,
inspectionId: String,
},
watch: {
loadingActive() {
if (this.loading == "loading") {
this.loading = this.loadingActive;
}
if (this.loadingActive == "fetched") {
this.checks = cloneDeep(this.activeInspectionObj?.checks ?? []);
}
},
},
data() {
return {
loading: "loading" as "loading" | "fetched" | "failed",
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
timeout: null as any,
checks: [] as Array<InspectionPointResultViewModel>,
};
},
computed: {
canSaveOrReset(): boolean {
return isEqual(
(this.activeInspectionObj?.checks || []).sort((a, b) => a.inspectionPointId.localeCompare(b.inspectionPointId)),
this.checks.sort((a, b) => a.inspectionPointId.localeCompare(b.inspectionPointId))
);
},
...mapState(useInspectionStore, ["activeInspectionObj", "loadingActive"]),
points() {
return this.activeInspectionObj?.inspectionVersionedPlan.inspectionPoints ?? [];
},
pointResult() {
return (pointId: string) => {
return this.checks.find((c) => c.inspectionPointId == pointId)?.value ?? "";
};
},
boolPointResult() {
return (pointId: string): "true" | "false" => {
return this.pointResult(pointId) == "true" ? "true" : "false";
};
},
},
mounted() {
this.fetchItem();
},
beforeUnmount() {
try {
clearTimeout(this.timeout);
} catch (error) {}
},
methods: {
...mapActions(useInspectionStore, ["fetchInspectionByActiveId"]),
resetForm() {
this.checks = cloneDeep(this.activeInspectionObj?.checks ?? []);
},
fetchItem() {
this.fetchInspectionByActiveId();
},
updateCheckResult(id: string, value: string) {
if (this.activeInspectionObj == null) return;
let index = this.checks.findIndex((c) => c.inspectionPointId == id);
if (index == -1) {
this.checks.push({
inspectionId: this.activeInspectionObj.id,
inspectionVersionedPlanId: this.activeInspectionObj.inspectionVersionedPlanId,
inspectionPointId: id,
value: value,
});
} else {
this.checks[index].value = value;
}
},
triggerUpdate(e: any) {
if (this.activeInspectionObj == null) return;
this.status = "loading";
new Promise<void>((resolve, reject) => {
resolve();
})
.then(() => {
this.fetchItem();
this.status = { status: "success" };
})
.catch((err) => {
this.status = { status: "failed" };
})
.finally(() => {
this.timeout = setTimeout(() => {
this.status = null;
}, 2000);
});
},
},
});
</script>

View file

@ -5,55 +5,58 @@
<h1 class="font-bold text-xl h-8">Prüfung erstellen</h1>
</div>
</template>
<template #diffMain>
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
<form class="flex flex-col gap-4 py-2 w-full max-w-xl mx-auto" @submit.prevent="">
<div class="flex flex-row">
<div
v-for="tab in tabs"
:key="tab.key"
class="w-1/2 p-0.5 first:pl-0 last:pr-0 cursor-pointer"
@click="
active = tab.key;
related = '';
inspectionPlan = '';
"
<template #main>
<form class="flex flex-col gap-4 py-2 w-full max-w-xl mx-auto" @submit.prevent="">
<div class="flex flex-row">
<div
v-for="tab in tabs"
:key="tab.key"
class="w-1/2 p-0.5 first:pl-0 last:pr-0 cursor-pointer"
@click="
active = tab.key;
related = '';
inspectionPlan = '';
"
>
<p
:class="[
'w-full rounded-lg py-2.5 text-sm text-center font-medium leading-5 focus:ring-0 outline-hidden',
tab.key == active
? 'bg-red-200 shadow-sm border-b-2 border-primary rounded-b-none'
: ' hover:bg-red-200',
]"
>
<p
:class="[
'w-full rounded-lg py-2.5 text-sm text-center font-medium leading-5 focus:ring-0 outline-hidden',
tab.key == active
? 'bg-red-200 shadow-sm border-b-2 border-primary rounded-b-none'
: ' hover:bg-red-200',
]"
>
{{ tab.title }}
</p>
</div>
{{ tab.title }}
</p>
</div>
</div>
<EquipmentSearchSelect v-if="active == 'equipment'" title="Gerät" useScanner v-model="related" />
<VehicleSearchSelect v-else title="Fahrzeug" useScanner v-model="related" />
<EquipmentSearchSelect v-if="active == 'equipment'" title="Gerät" useScanner v-model="related" />
<VehicleSearchSelect v-else title="Fahrzeug" useScanner v-model="related" />
<InspectionPlanSearchSelect title="Prüfplan" :type="active" v-model="inspectionPlan" />
<InspectionPlanSearchSelect title="Prüfplan" :type="active" v-model="inspectionPlan" />
<div class="flex flex-row justify-end gap-2">
<RouterLink
:to="{ name: 'admin-unit-inspection_plan' }"
primary-outline
button
class="w-fit!"
:disabled="status == 'loading' || status?.status == 'success'"
>
abbrechen
</RouterLink>
<button primary type="submit" class="w-fit!" :disabled="status == 'loading'">speichern</button>
<Spinner v-if="status == 'loading'" class="my-auto" />
<SuccessCheckmark v-else-if="status?.status == 'success'" />
<FailureXMark v-else-if="status?.status == 'failed'" />
</div>
</form>
</div>
<div>
<label for="nextInspection">Nächste Prüfung (optional) {{ " - Intervall: xx-x" }}</label>
<input id="nextInspection" type="date" />
</div>
<div class="flex flex-row justify-end gap-2">
<RouterLink
:to="{ name: 'admin-unit-inspection_plan' }"
primary-outline
button
class="w-fit!"
:disabled="status == 'loading' || status?.status == 'success'"
>
abbrechen
</RouterLink>
<button primary type="submit" class="w-fit!" :disabled="status == 'loading'">speichern</button>
<Spinner v-if="status == 'loading'" class="my-auto" />
<SuccessCheckmark v-else-if="status?.status == 'success'" />
<FailureXMark v-else-if="status?.status == 'failed'" />
</div>
</form>
</template>
</MainTemplate>
</template>

View file

@ -9,7 +9,7 @@
>
<template #pageRow="{ row }: { row: InspectionViewModel }">
<RouterLink
:to="{ name: 'admin-unit-inspection-execute', params: { inspection: row.id } }"
:to="{ name: 'admin-unit-inspection-execute', params: { inspectionId: row.id } }"
class="flex flex-col h-fit w-full border border-primary rounded-md"
>
<div class="bg-primary p-2 text-white flex flex-row gap-2 items-center">
@ -44,7 +44,7 @@ import { useAbilityStore } from "@/stores/ability";
import { useVehicleInspectionStore } from "@/stores/admin/unit/vehicle/inspection";
import { PencilSquareIcon } from "@heroicons/vue/24/outline";
import Pagination from "@/components/Pagination.vue";
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
</script>
<script lang="ts">