edit repairs

This commit is contained in:
Julian Krauser 2025-07-22 13:10:04 +02:00
parent c79d5bb1cd
commit 789272dc37
9 changed files with 201 additions and 74 deletions

View file

@ -0,0 +1,92 @@
<template>
<div v-if="activeRepairObj != null" class="flex flex-col gap-2 h-full w-full">
<DamageReportSearchSelectMultipleWithRelated
title="verbundene Schadensmeldungen"
:related="activeRepairObj.assigned"
:relatedId="activeRepairObj.relatedId"
:model-value="activeRepairObj.reports.map((r) => r.id)"
:disabled="!!activeRepairObj.finishedAt"
@add:damage-report="handleReportAdd"
@remove:difference="handleReportRemove"
/>
<div class="flex flex-col gap-2 overflow-y-scroll h-full">
<div
v-for="damageReport in activeRepairObj.reports"
:key="damageReport.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>{{ damageReport.title }}</p>
<TrashIcon
v-if="!activeRepairObj.finishedAt"
class="w-5 h-5 cursor-pointer"
@click="handleReportRemove(damageReport.id)"
/>
</div>
<div class="p-2">
<p v-if="damageReport.description">Beschreibung: {{ damageReport.description }}</p>
</div>
</div>
</div>
<button primary class="w-fit! self-end" :disabled="!!activeRepairObj.finishedAt" @click="saveReports">
Änderungen speichern
</button>
</div>
</template>
<script setup lang="ts">
import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import { useAbilityStore } from "@/stores/ability";
import { useRepairStore } from "@/stores/admin/unit/repair";
import type { RepairViewModel, UpdateRepairViewModel } from "@/viewmodels/admin/unit/repair.models";
import DamageReportSearchSelectMultipleWithRelated from "@/components/search/DamageReportSearchSelectMultipleWithRelated.vue";
import { aC } from "node_modules/@fullcalendar/core/internal-common";
import type { DamageReportViewModel } from "@/viewmodels/admin/unit/damageReport.models";
import { TrashIcon } from "@heroicons/vue/24/outline";
</script>
<script lang="ts">
export default defineComponent({
props: {
repairId: String,
},
watch: {},
data() {
return {
loading: undefined as undefined | "loading" | "success" | "failed",
};
},
computed: {
...mapWritableState(useRepairStore, ["activeRepairObj"]),
...mapState(useAbilityStore, ["can"]),
},
methods: {
...mapActions(useRepairStore, ["loadRepairImage", "updateRepairReports"]),
handleReportAdd(report: DamageReportViewModel) {
if (!this.activeRepairObj) return;
this.activeRepairObj.reports.push(report);
},
handleReportRemove(reportId: string) {
if (!this.activeRepairObj) return;
this.activeRepairObj.reports = this.activeRepairObj.reports.filter((r) => r.id != reportId);
},
saveReports() {
if (this.activeRepairObj == null) return;
this.loading = "loading";
this.updateRepairReports(this.activeRepairObj.reports.map((r) => r.id))
.then((res) => {
this.loading = "success";
})
.catch((err) => {
this.loading = "failed";
})
.finally(() => {
setTimeout(() => {
this.loading = undefined;
}, 2000);
});
},
},
});
</script>

View file

@ -1,38 +1,39 @@
<template>
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
<div v-if="activeRepairObj != null" class="flex flex-col gap-2 w-full">
<div v-if="activeRepairObj != null" class="flex flex-col gap-2 h-full w-full overflow-y-auto">
<div>
<label for="status">Status</label>
<input id="status" ref="status" type="text" :readonly="!editStatus" :value="activeRepairObj.status" />
</div>
<button
v-if="!editStatus && !activeRepairObj.finishedAt"
primary
class="w-fit! self-end"
@click="editStatus = true"
>
Status ändern
</button>
<div v-else-if="!activeRepairObj.finishedAt" class="flex flex-row gap-2 justify-end">
<button primary-outline class="w-fit!" @click="saveStatus(true)">speichern und abschließen</button>
<button primary class="w-fit!" @click="saveStatus(false)">Status speichern</button>
</div>
<br />
<form class="flex flex-col gap-2" @submit.prevent="saveData">
<div>
<label for="status">Status</label>
<input id="status" ref="status" type="text" :readonly="!editStatus" :value="activeRepairObj.status" />
<label for="title">Kurzbeschreibung</label>
<input id="title" type="text" placeholder="---" :value="activeRepairObj.title" />
</div>
<button
v-if="!editStatus && !activeRepairObj.finishedAt"
primary
class="w-fit! self-end"
@click="editStatus = true"
>
Status ändern
</button>
<div v-else-if="!activeRepairObj.finishedAt" class="flex flex-row gap-2 justify-end">
<button primary-outline class="w-fit!" @click="saveStatus(true)">speichern und abschließen</button>
<button primary class="w-fit!" @click="saveStatus(false)">Status speichern</button>
</div>
<br />
<div>
<label for="description">Beschreibung des Schadens</label>
<textarea id="description" readonly :value="activeRepairObj.description"></textarea>
<label for="description">Beschreibung der Reparatur</label>
<textarea id="description" placeholder="---" :value="activeRepairObj.description"></textarea>
</div>
<div>
<label for="responsible">Verantwortlich</label>
<input id="responsible" type="text" readonly placeholder="---" :value="activeRepairObj.responsible" />
<input id="responsible" type="text" placeholder="---" :value="activeRepairObj.responsible" />
</div>
<div>
<label>Bild</label>
<div ref="imgs">
<small v-if="activeRepairObj.images.length == 0">Keine Bilder hochgeladen</small>
</div>
</div>
</div>
<button primary type="submit" class="w-fit! self-end" :disabled="!!activeRepairObj.finishedAt">
Änderungen speichern
</button>
</form>
</div>
</template>
@ -41,7 +42,7 @@ import { defineComponent } from "vue";
import { mapActions, mapState, mapWritableState } from "pinia";
import { useAbilityStore } from "@/stores/ability";
import { useRepairStore } from "@/stores/admin/unit/repair";
import type { RepairViewModel, UpdateRepairViewModel } from "@/viewmodels/admin/unit/repair.models";
import type { UpdateRepairStatusViewModel, UpdateRepairViewModel } from "@/viewmodels/admin/unit/repair.models";
</script>
<script lang="ts">
@ -49,11 +50,6 @@ export default defineComponent({
props: {
repairId: String,
},
watch: {
activeRepairObj(val: RepairViewModel, oldVal: RepairViewModel | null) {
if (val && oldVal == null) this.loadImages();
},
},
data() {
return {
editStatus: false as boolean,
@ -64,41 +60,47 @@ export default defineComponent({
...mapWritableState(useRepairStore, ["activeRepairObj"]),
...mapState(useAbilityStore, ["can"]),
},
mounted() {
this.loadImages();
},
methods: {
...mapActions(useRepairStore, ["loadRepairImage", "updateRepair"]),
loadImages() {
for (const i of this.activeRepairObj?.images ?? []) {
this.loadRepairImage(i)
.then((response) => {
const contentType =
response.headers && response.headers["content-type"] ? response.headers["content-type"] : "image/*";
const blob = new Blob([response.data], { type: contentType });
const img = document.createElement("img");
img.src = window.URL.createObjectURL(blob);
img.alt = "Schadensbild";
img.classList = "h-35 w-auto";
(this.$refs.imgs as HTMLElement).appendChild(img);
})
.catch((err) => {
console.log(err);
});
}
},
...mapActions(useRepairStore, ["loadRepairImage", "updateRepairStatus", "updateRepair"]),
saveStatus(finish: boolean) {
if (this.activeRepairObj == null) return;
this.loading = "loading";
let update: UpdateRepairViewModel = {
let update: UpdateRepairStatusViewModel = {
id: this.activeRepairObj.id,
status: (this.$refs.status as HTMLInputElement).value,
noteByWorker: (this.$refs.noteByWorker as HTMLInputElement).value,
done: finish,
};
this.updateRepairStatus(update)
.then((res) => {
this.activeRepairObj!.status = update.status;
this.activeRepairObj!.finishedAt = update.done ? new Date() : undefined;
this.loading = "success";
this.editStatus = false;
})
.catch((err) => {
this.loading = "failed";
})
.finally(() => {
setTimeout(() => {
this.loading = undefined;
}, 2000);
});
},
saveData(e: any) {
if (this.activeRepairObj == null) return;
this.loading = "loading";
const formData = e.target.elements;
let update: UpdateRepairViewModel = {
id: this.activeRepairObj.id,
title: formData.title.value,
description: formData.description.value,
responsible: formData.responsible.value,
};
this.updateRepair(update)
.then((res) => {
this.activeRepairObj!.status = update.status;
this.activeRepairObj!.title = update.title;
this.activeRepairObj!.description = update.description;
this.activeRepairObj!.responsible = update.responsible;
this.loading = "success";
this.editStatus = false;
})

View file

@ -23,7 +23,7 @@
<template #diffMain>
<div class="flex flex-col gap-2 grow px-7 overflow-hidden">
<div class="flex flex-col grow gap-2 overflow-hidden">
<div hidden class="w-full flex flex-row max-lg:flex-wrap justify-center">
<div class="w-full flex flex-row max-lg:flex-wrap justify-center">
<RouterLink
v-for="tab in tabs"
:key="tab.route"
@ -65,7 +65,11 @@ export default defineComponent({
},
data() {
return {
tabs: [{ route: "admin-unit-repair-overview", title: "Übersicht" }],
tabs: [
{ route: "admin-unit-repair-overview", title: "Übersicht" },
// { route: "admin-unit-repair-overview", title: "Bilder & Bericht" },
{ route: "admin-unit-repair-reports", title: "Schadensmeldungen" },
],
};
},
computed: {

View file

@ -1,5 +1,5 @@
<template>
<MainTemplate title="Schadensmeldungen">
<MainTemplate title="Reparaturen">
<template #diffMain>
<div class="flex flex-col gap-2 grow px-7 overflow-hidden">
<div class="w-full flex flex-row max-lg:flex-wrap justify-center">