inspection finish and print

This commit is contained in:
Julian Krauser 2025-07-11 14:02:39 +02:00
parent d96c73d5b1
commit 5d26885da3
14 changed files with 367 additions and 29 deletions

View file

@ -0,0 +1,25 @@
<template>
<div @click="showInfo" class="cursor-pointer">
<InformationCircleIcon class="w-5 h-5" />
</div>
</template>
<script setup lang="ts">
import { defineAsyncComponent, defineComponent, markRaw } from "vue";
import { mapState, mapActions } from "pinia";
import { useModalStore } from "@/stores/modal";
import { InformationCircleIcon } from "@heroicons/vue/24/outline";
</script>
<script lang="ts">
export default defineComponent({
methods: {
...mapActions(useModalStore, ["openModal"]),
showInfo() {
this.openModal(
markRaw(defineAsyncComponent(() => import("@/components/admin/unit/InspectionTimeFormatExplainModal.vue")))
);
},
},
});
</script>

View file

@ -0,0 +1,55 @@
<template>
<div class="relative w-full md:max-w-md">
<div class="flex flex-row gap-2 items-center justify-center">
<InformationCircleIcon class="text-gray-500 h-5 w-5" />
<p class="text-xl font-medium">Zeit Format für Erinnerung und Intervall</p>
</div>
<br />
<table class="min-w-full text-sm border border-gray-200 rounded">
<tbody>
<tr>
<td class="px-3 py-2 font-mono text-gray-700 border-b border-gray-100">&lt;zahl&gt;-(d|m|y)</td>
<td class="px-3 py-2 text-gray-600 border-b border-gray-100">
Ein Intervall, z.B. <span class="font-mono">7-d</span> für alle 7 Tage,
<span class="font-mono">1-m</span> für jeden Monat.
</td>
</tr>
<tr>
<td class="px-3 py-2 font-mono text-gray-700 border-b border-gray-100">DD/MM</td>
<td class="px-3 py-2 text-gray-600 border-b border-gray-100">
Ein bestimmtes Datum, z.B. <span class="font-mono">15/06</span> für den 15. Juni.
</td>
</tr>
<tr>
<td class="px-3 py-2 font-mono text-gray-700">DD/*</td>
<td class="px-3 py-2 text-gray-600">
Ein Tag jeden Monats, z.B. <span class="font-mono">01/*</span> für den ersten Tag jedes Monats.
</td>
</tr>
</tbody>
</table>
<p>Im Fall von Erinnerungen wird das Format als zeitliche Angabe vor einem Datum verwendet.</p>
<br />
<div class="flex flex-row justify-end">
<div class="flex flex-row gap-4 py-2">
<button primary-outline @click="closeModal">schließen</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from "vue";
import { mapState, mapActions } from "pinia";
import { useModalStore } from "@/stores/modal";
import { InformationCircleIcon } from "@heroicons/vue/24/outline";
</script>
<script lang="ts">
export default defineComponent({
methods: {
...mapActions(useModalStore, ["closeModal"]),
},
});
</script>

View file

@ -0,0 +1,85 @@
<template>
<div class="w-full md:max-w-md">
<div class="flex flex-col items-center">
<p class="text-xl font-medium">angefangene Prüfung löschen</p>
</div>
<br />
<p class="text-center">
{{ activeInspectionObj?.inspectionPlan.title }} zu {{ activeInspectionObj?.related.name }}
<small v-if="activeInspectionObj?.related.code">({{ activeInspectionObj?.related.code }})</small> begonnen am
{{ new Date(activeInspectionObj?.created ?? "").toLocaleDateString("de-de") }} löschen?
</p>
<br />
<div class="flex flex-row gap-2">
<button
primary
type="submit"
:disabled="status == 'loading' || status?.status == 'success'"
@click="triggerDelete"
>
löschen
</button>
<Spinner v-if="status == 'loading'" class="my-auto" />
<SuccessCheckmark v-else-if="status?.status == 'success'" />
<FailureXMark v-else-if="status?.status == 'failed'" />
</div>
<div class="flex flex-row justify-end">
<div class="flex flex-row gap-4 py-2">
<button primary-outline @click="closeModal" :disabled="status == 'loading' || status?.status == 'success'">
abbrechen
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from "vue";
import { mapState, mapActions } from "pinia";
import { useModalStore } from "@/stores/modal";
import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue";
import { useInspectionStore } from "@/stores/admin/unit/inspection/inspection";
</script>
<script lang="ts">
export default defineComponent({
data() {
return {
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
timeout: undefined as any,
};
},
computed: {
...mapState(useInspectionStore, ["activeInspectionObj"]),
},
beforeUnmount() {
try {
clearTimeout(this.timeout);
} catch (error) {}
},
methods: {
...mapActions(useModalStore, ["closeModal"]),
...mapActions(useInspectionStore, ["deleteInspection"]),
triggerDelete() {
if (!this.activeInspectionObj) return;
this.status = "loading";
this.deleteInspection(this.activeInspectionObj.id)
.then(() => {
this.status = { status: "success" };
this.timeout = setTimeout(() => {
this.$router.push({ name: "admin-unit-inspection" });
this.closeModal();
}, 1500);
})
.catch(() => {
this.status = { status: "failed" };
});
},
},
});
</script>

View file

@ -11,13 +11,22 @@
Es wird ein PDF ausgedruckt und ist dann zu dieser Prüfung verfügbar.
</p>
<br />
<button primary>Prüfung abschließen</button>
<div class="flex flex-row gap-2">
<button :disabled="status == 'loading' || status?.status == 'success'" primary @click="finishInspection">
Prüfung abschließen
</button>
<Spinner v-if="status == 'loading'" class="my-auto" />
<SuccessCheckmark v-else-if="status?.status == 'success'" />
<FailureXMark v-else-if="status?.status == 'failed'" />
</div>
</div>
<br />
<div class="flex flex-row justify-end">
<div class="flex flex-row gap-4 py-2">
<button primary-outline @click="closeModal">abbrechen</button>
<button primary-outline @click="closeModal" :disabled="status == 'loading' || status?.status == 'success'">
abbrechen
</button>
</div>
</div>
</div>
@ -28,12 +37,47 @@ import { defineComponent } from "vue";
import { mapState, mapActions } from "pinia";
import { useModalStore } from "@/stores/modal";
import { InformationCircleIcon } from "@heroicons/vue/24/outline";
import { useInspectionStore } from "@/stores/admin/unit/inspection/inspection";
import Spinner from "@/components/Spinner.vue";
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
import FailureXMark from "@/components/FailureXMark.vue";
</script>
<script lang="ts">
export default defineComponent({
data() {
return {
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
timeout: null as any,
};
},
beforeUnmount() {
try {
clearTimeout(this.timeout);
} catch (error) {}
},
methods: {
...mapActions(useModalStore, ["closeModal"]),
...mapActions(useInspectionStore, ["finishActiveInspection"]),
finishInspection(e: any) {
this.status = "loading";
this.finishActiveInspection()
.then(() => {
this.status = { status: "success" };
this.timeout = setTimeout(() => {
this.closeModal();
}, 2100);
})
.catch((err) => {
this.status = { status: "failed" };
})
.finally(() => {
this.timeout = setTimeout(() => {
this.status = null;
}, 2000);
});
},
},
});
</script>

View file

@ -0,0 +1,62 @@
<template>
<div class="w-full h-full flex flex-col gap-2">
<Spinner v-if="status == 'loading'" />
<div class="grow">
<iframe ref="viewer" class="w-full h-full" />
</div>
<div class="flex flex-row gap-2 justify-end">
<a ref="download" button primary class="w-fit!">download</a>
<button primary-outline class="w-fit!" @click="closeModal">schließen</button>
</div>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from "vue";
import { mapState, mapActions } from "pinia";
import { useModalStore } from "@/stores/modal";
import Spinner from "@/components/Spinner.vue";
import { useInspectionStore } from "@/stores/admin/unit/inspection/inspection";
</script>
<script lang="ts">
export default defineComponent({
data() {
return {
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
};
},
computed: {
...mapState(useModalStore, ["data"]),
...mapState(useInspectionStore, ["activeInspectionObj"]),
},
mounted() {
this.fetchItem();
},
methods: {
...mapActions(useModalStore, ["closeModal"]),
...mapActions(useInspectionStore, ["fetchInspectionPrintoutById"]),
fetchItem() {
this.status = "loading";
this.fetchInspectionPrintoutById()
.then((response) => {
this.status = { status: "success" };
const blob = new Blob([response.data], { type: "application/pdf" });
(this.$refs.viewer as HTMLIFrameElement).src = window.URL.createObjectURL(blob);
const fileURL = window.URL.createObjectURL(new Blob([response.data]));
const fileLink = this.$refs.download as HTMLAnchorElement;
fileLink.href = fileURL;
fileLink.setAttribute(
"download",
`Prüf-Ausdruck_${[this.activeInspectionObj?.related.code ?? "", this.activeInspectionObj?.related.name].join("_")}_${this.activeInspectionObj?.inspectionPlan.title}_${new Date(this.activeInspectionObj?.created ?? "").toLocaleDateString("de-de")}.pdf`
);
})
.catch(() => {
this.status = { status: "failed" };
});
},
},
});
</script>

View file

@ -1,12 +1,15 @@
<template>
<div class="flex flex-col h-fit w-full border border-primary rounded-md">
<RouterLink
:to="{ name: 'admin-unit-inspection_plan-overview', params: { inspectionPlanId: inspectionPlan.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 justify-between items-center">
<p>{{ inspectionPlan.title }}</p>
</div>
<div class="p-2">
<p>Interval: {{ inspectionPlan.inspectionInterval }}</p>
</div>
</div>
</RouterLink>
</template>
<script setup lang="ts">