Compare commits
20 commits
main
...
unit/#70-b
Author | SHA1 | Date | |
---|---|---|---|
835e6ef8db | |||
c4a67fd11a | |||
46432fbf7d | |||
e25d91802c | |||
00fad29b25 | |||
b6c68d2205 | |||
553eeb7bfb | |||
716823f536 | |||
5641dbb57f | |||
8be88a5245 | |||
f951a1cd4c | |||
4faf93c3ce | |||
36ca3d90a7 | |||
5faa4b7906 | |||
45fe7b34c3 | |||
3e87bbc267 | |||
b6d6dd0796 | |||
2a77a950f5 | |||
2b3231e26c | |||
4338f58dea |
114 changed files with 7061 additions and 109 deletions
64
package-lock.json
generated
64
package-lock.json
generated
|
@ -40,6 +40,7 @@
|
||||||
"unplugin-vue-markdown": "^28.3.1",
|
"unplugin-vue-markdown": "^28.3.1",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"vue": "^3.4.29",
|
"vue": "^3.4.29",
|
||||||
|
"vue-qrcode-reader": "^5.7.1",
|
||||||
"vue-router": "^4.3.3"
|
"vue-router": "^4.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -3691,6 +3692,18 @@
|
||||||
"@types/underscore": "*"
|
"@types/underscore": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/dom-webcodecs": {
|
||||||
|
"version": "0.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.14.tgz",
|
||||||
|
"integrity": "sha512-ba9aF0qARLLQpLihONIRbj8VvAdUxO+5jIxlscVcDAQTcJmq5qVr781+ino5qbQUJUmO21cLP2eLeXYWzao5Vg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/emscripten": {
|
||||||
|
"version": "1.40.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.40.1.tgz",
|
||||||
|
"integrity": "sha512-sr53lnYkQNhjHNN0oJDdUm5564biioI5DuOpycufDVK7D3y+GR3oUswe2rlwY1nPNyusHbrJ9WoTyIHl4/Bpwg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/eslint": {
|
"node_modules/@types/eslint": {
|
||||||
"version": "9.6.1",
|
"version": "9.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||||
|
@ -4706,6 +4719,16 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/barcode-detector": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/barcode-detector/-/barcode-detector-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-JcSekql+EV93evfzF9zBr+Y6aRfkR+QFvgyzbwQ0dbymZXoAI9+WgT7H1E429f+3RKNncHz2CW98VQtaaKpmfQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/dom-webcodecs": "^0.1.11",
|
||||||
|
"zxing-wasm": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/birpc": {
|
"node_modules/birpc": {
|
||||||
"version": "0.2.19",
|
"version": "0.2.19",
|
||||||
"resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz",
|
"resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz",
|
||||||
|
@ -9241,6 +9264,12 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/sdp": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/section-matter": {
|
"node_modules/section-matter": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
|
||||||
|
@ -10758,6 +10787,19 @@
|
||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-qrcode-reader": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-qrcode-reader/-/vue-qrcode-reader-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-7QBu3PqaPJHxobiDLqgcrp6wsjdTk9GJWhRCd4CgQYi93gBw/sIXNNWtbjeKz8d3QYj13n9dyPvcPMUcGOsBHw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"barcode-detector": "2.2.2",
|
||||||
|
"webrtc-adapter": "8.2.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-router": {
|
"node_modules/vue-router": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz",
|
||||||
|
@ -10803,6 +10845,19 @@
|
||||||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/webrtc-adapter": {
|
||||||
|
"version": "8.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.2.3.tgz",
|
||||||
|
"integrity": "sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"sdp": "^3.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0",
|
||||||
|
"npm": ">=3.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/whatwg-url": {
|
"node_modules/whatwg-url": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
|
||||||
|
@ -11509,6 +11564,15 @@
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/zxing-wasm": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/zxing-wasm/-/zxing-wasm-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-MYm9k/5YVs4ZOTIFwlRjfFKD0crhefgbnt1+6TEpmKUDFp3E2uwqGSKwQOd2hOIsta/7Usq4hnpNRYTLoljnfA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/emscripten": "^1.39.10"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
"unplugin-vue-markdown": "^28.3.1",
|
"unplugin-vue-markdown": "^28.3.1",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"vue": "^3.4.29",
|
"vue": "^3.4.29",
|
||||||
|
"vue-qrcode-reader": "^5.7.1",
|
||||||
"vue-router": "^4.3.3"
|
"vue-router": "^4.3.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
77
src/components/CodeDetector.vue
Normal file
77
src/components/CodeDetector.vue
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<XMarkIcon class="ml-auto mb-2 w-5 h-5 cursor-pointer" @click="closeModal" />
|
||||||
|
<qrcode-stream
|
||||||
|
:constraints="selectedCamera?.constraints"
|
||||||
|
:track="trackFunctionOptions[4].value"
|
||||||
|
:formats="barcodeFormats"
|
||||||
|
:paused="paused"
|
||||||
|
@error="onError"
|
||||||
|
@detect="onDetect"
|
||||||
|
@camera-on="onCameraReady"
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<select v-model="selectedCamera">
|
||||||
|
<option v-for="c in selecteableCameras" :value="c">{{ c.label }}</option>
|
||||||
|
</select>
|
||||||
|
<div class="flex flex-row justify-end gap-4 py-2">
|
||||||
|
<button primary-outline @click="paused = false" :disabled="!paused">weiter scannen</button>
|
||||||
|
<button primary-outline @click="commit">bestätigen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useModalStore } from "../stores/modal";
|
||||||
|
import {
|
||||||
|
barcodeFormats,
|
||||||
|
defaultConstraintOptions,
|
||||||
|
getAvailableCameras,
|
||||||
|
handleScannerError,
|
||||||
|
trackFunctionOptions,
|
||||||
|
type Camera,
|
||||||
|
} from "../helpers/codeScanner";
|
||||||
|
import { QrcodeStream, type DetectedBarcode } from "vue-qrcode-reader";
|
||||||
|
import { XMarkIcon } from "@heroicons/vue/24/outline";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
callback: {
|
||||||
|
type: Function,
|
||||||
|
default: (result: string) => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selecteableCameras: defaultConstraintOptions,
|
||||||
|
selectedCamera: undefined as undefined | Camera,
|
||||||
|
paused: false,
|
||||||
|
detected: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
async onCameraReady() {
|
||||||
|
this.selecteableCameras = await getAvailableCameras();
|
||||||
|
if (!this.selectedCamera) {
|
||||||
|
this.selectedCamera = this.selecteableCameras[0];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDetect(result: Array<DetectedBarcode>) {
|
||||||
|
this.paused = true;
|
||||||
|
this.detected = result.map((r) => r.rawValue)[0];
|
||||||
|
},
|
||||||
|
onError(err: Error) {
|
||||||
|
console.log(handleScannerError(err));
|
||||||
|
},
|
||||||
|
commit() {
|
||||||
|
this.callback(this.detected);
|
||||||
|
this.closeModal();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -9,6 +9,7 @@
|
||||||
<component
|
<component
|
||||||
:is="component_ref"
|
:is="component_ref"
|
||||||
:data="data"
|
:data="data"
|
||||||
|
:callback="callback"
|
||||||
@click.stop
|
@click.stop
|
||||||
class="p-4 bg-white rounded-lg max-h-[95%] overflow-y-auto"
|
class="p-4 bg-white rounded-lg max-h-[95%] overflow-y-auto"
|
||||||
/>
|
/>
|
||||||
|
@ -23,7 +24,7 @@ import { useModalStore } from "@/stores/modal";
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(useModalStore, ["show", "component_ref", "data"]),
|
...mapState(useModalStore, ["show", "component_ref", "data", "callback"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(useModalStore, ["closeModal"]),
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<div class="grow flex flex-col gap-2 overflow-hidden">
|
<div class="grow flex flex-col gap-2 overflow-hidden">
|
||||||
<div v-if="useSearch" class="relative self-end flex flex-row items-center gap-2">
|
<div v-if="useSearch" class="relative self-end flex flex-row items-center gap-2">
|
||||||
<Spinner v-if="deferingSearch" />
|
<Spinner v-if="deferingSearch" />
|
||||||
|
<QrCodeIcon v-if="useScanner" class="h-7 cursor-pointer" @click="scanCode" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="max-w-64! w-64! rounded-md shadow-xs relative block px-3 py-2 pr-5 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-hidden focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
|
class="max-w-64! w-64! rounded-md shadow-xs relative block px-3 py-2 pr-5 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-hidden focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
|
||||||
|
@ -66,10 +67,12 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" generic="T extends { id: FieldType }">
|
<script setup lang="ts" generic="T extends { id: FieldType }">
|
||||||
import { computed, ref, watch } from "vue";
|
import { computed, defineAsyncComponent, markRaw, ref, watch } from "vue";
|
||||||
import { ChevronRightIcon, ChevronLeftIcon, XMarkIcon } from "@heroicons/vue/20/solid";
|
import { ChevronRightIcon, ChevronLeftIcon, XMarkIcon } from "@heroicons/vue/20/solid";
|
||||||
import Spinner from "./Spinner.vue";
|
import Spinner from "./Spinner.vue";
|
||||||
import type { FieldType } from "@/types/dynamicQueries";
|
import type { FieldType } from "@/types/dynamicQueries";
|
||||||
|
import { QrCodeIcon } from "@heroicons/vue/24/outline";
|
||||||
|
import { useModalStore } from "../stores/modal";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
items: { type: Array<T>, default: [] },
|
items: { type: Array<T>, default: [] },
|
||||||
|
@ -79,6 +82,7 @@ const props = defineProps({
|
||||||
useSearch: { type: Boolean, default: false },
|
useSearch: { type: Boolean, default: false },
|
||||||
enablePreSearch: { type: Boolean, default: false },
|
enablePreSearch: { type: Boolean, default: false },
|
||||||
indicateLoading: { type: Boolean, default: false },
|
indicateLoading: { type: Boolean, default: false },
|
||||||
|
useScanner: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
const slots = defineSlots<{
|
const slots = defineSlots<{
|
||||||
|
@ -183,84 +187,14 @@ const filterData = (array: Array<any>, searchString: string, start: number, end:
|
||||||
)
|
)
|
||||||
.filter((elem, index) => (elem?.tab_pos ?? index) >= start && (elem?.tab_pos ?? index) < end);
|
.filter((elem, index) => (elem?.tab_pos ?? index) >= start && (elem?.tab_pos ?? index) < end);
|
||||||
};
|
};
|
||||||
</script>
|
|
||||||
|
|
||||||
<!--
|
function scanCode() {
|
||||||
<script lang="ts">
|
useModalStore().openModal(
|
||||||
export default defineComponent({
|
markRaw(defineAsyncComponent(() => import("@/components/CodeDetector.vue"))),
|
||||||
computed: {
|
"pagination",
|
||||||
entryCount() {
|
(result: string) => {
|
||||||
return this.totalCount ?? this.items.length;
|
searchString.value = result;
|
||||||
},
|
|
||||||
showingStart() {
|
|
||||||
return this.currentPage * this.maxEntriesPerPage;
|
|
||||||
},
|
|
||||||
showingEnd() {
|
|
||||||
let max = this.currentPage * this.maxEntriesPerPage + this.maxEntriesPerPage;
|
|
||||||
if (max > this.entryCount) max = this.entryCount;
|
|
||||||
return max;
|
|
||||||
},
|
|
||||||
showingText() {
|
|
||||||
return `${this.entryCount != 0 ? this.showingStart + 1 : 0} - ${this.showingEnd}`;
|
|
||||||
},
|
|
||||||
countOfPages() {
|
|
||||||
return Math.ceil(this.entryCount / this.maxEntriesPerPage);
|
|
||||||
},
|
|
||||||
displayedPagesNumbers(): Array<number | "."> {
|
|
||||||
//indicate if "." or page number gets pushed
|
|
||||||
let stateOfPush = false;
|
|
||||||
|
|
||||||
return [...new Array(this.countOfPages)].reduce((acc, curr, index) => {
|
|
||||||
if (
|
|
||||||
// always display first 2 pages
|
|
||||||
index <= 1 ||
|
|
||||||
// always display last 2 pages
|
|
||||||
index >= this.countOfPages - 2 ||
|
|
||||||
// always display 1 pages around current page
|
|
||||||
(this.currentPage - 1 <= index && index <= this.currentPage + 1)
|
|
||||||
) {
|
|
||||||
acc.push(index);
|
|
||||||
stateOfPush = false;
|
|
||||||
return acc;
|
|
||||||
}
|
}
|
||||||
// abort if placeholder already added to array
|
);
|
||||||
if (stateOfPush == true) return acc;
|
}
|
||||||
// show placeholder if pagenumber is not actively rendered
|
</script>
|
||||||
acc.push(".");
|
|
||||||
stateOfPush = true;
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
},
|
|
||||||
visibleRows() {
|
|
||||||
return this.filterData(this.items, this.searchString, this.showingStart, this.showingEnd);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
loadPage(newPage: number | ".") {
|
|
||||||
if (newPage == ".") return;
|
|
||||||
if (newPage < 0 || newPage >= this.countOfPages) return;
|
|
||||||
|
|
||||||
let pageStart = newPage * this.maxEntriesPerPage;
|
|
||||||
let pageEnd = newPage * this.maxEntriesPerPage + this.maxEntriesPerPage;
|
|
||||||
if (pageEnd > this.entryCount) pageEnd = this.entryCount;
|
|
||||||
|
|
||||||
let loadedElementCount = this.filterData(this.items, this.searchString, pageStart, pageEnd).length;
|
|
||||||
if (loadedElementCount < this.maxEntriesPerPage)
|
|
||||||
this.$emit("loadData", { offset: pageStart, count: this.maxEntriesPerPage, search: this.searchString });
|
|
||||||
|
|
||||||
this.currentPage = newPage;
|
|
||||||
},
|
|
||||||
filterData(array: Array<any>, searchString: string, start: number, end: number): Array<any> {
|
|
||||||
return array
|
|
||||||
.filter(
|
|
||||||
(elem) =>
|
|
||||||
!this.enablePreSearch ||
|
|
||||||
searchString.trim() == "" ||
|
|
||||||
this.config.some((col) => typeof elem?.[col.key] == "string" && elem[col.key].includes(searchString.trim()))
|
|
||||||
)
|
|
||||||
.filter((elem, index) => (elem?.tab_pos ?? index) >= start && (elem?.tab_pos ?? index) < end);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script> -->
|
|
||||||
|
|
57
src/components/ScanInput.vue
Normal file
57
src/components/ScanInput.vue
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<label :for="name">{{ label }}{{ required ? "" : " (optional)" }}</label>
|
||||||
|
<div class="relative flex flex-row items-center gap-2">
|
||||||
|
<input ref="resultInput" class="pl-9!" :id="name" type="text" v-model="value" :required="required" />
|
||||||
|
<QrCodeIcon class="absolute h-6 stroke-1 left-2 top-1/2 -translate-y-1/2 cursor-pointer z-10" @click="scanCode" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, markRaw, defineAsyncComponent } from "vue";
|
||||||
|
import { QrCodeIcon } from "@heroicons/vue/24/outline";
|
||||||
|
import { useModalStore } from "../stores/modal";
|
||||||
|
import { mapActions } from "pinia";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
label: String,
|
||||||
|
name: String,
|
||||||
|
required: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ["update:model-value"],
|
||||||
|
computed: {
|
||||||
|
value: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue;
|
||||||
|
},
|
||||||
|
set(val: String) {
|
||||||
|
this.$emit("update:model-value", val);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["openModal"]),
|
||||||
|
scanCode() {
|
||||||
|
this.openModal(
|
||||||
|
markRaw(defineAsyncComponent(() => import("@/components/CodeDetector.vue"))),
|
||||||
|
"codeScanInput",
|
||||||
|
(result: string) => {
|
||||||
|
(this.$refs.resultInput as HTMLInputElement).value = result;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<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>
|
||||||
|
{{ damageReport.affectedEquipment.name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-2">
|
||||||
|
<p v-if="damageReport.affectedEquipment">Code: {{ damageReport.affectedEquipment.code }}</p>
|
||||||
|
<p v-if="damageReport.description">Beschreibung: {{ damageReport.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { DamageReportViewModel } from "@/viewmodels/admin/unit/damageReport/damageReport.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
damageReport: { type: Object as PropType<DamageReportViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
33
src/components/admin/unit/equipment/EquipmentListItem.vue
Normal file
33
src/components/admin/unit/equipment/EquipmentListItem.vue
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-equipment-overview', params: { equipmentId: equipment.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>
|
||||||
|
{{ equipment.name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-2">
|
||||||
|
<p v-if="equipment.code">Code: {{ equipment.code }}</p>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { EquipmentViewModel } from "@/viewmodels/admin/unit/equipment/equipment.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
equipment: { type: Object as PropType<EquipmentViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Typ erstellen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<form class="flex flex-col gap-4 py-2" @submit.prevent="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<label for="type">Typ</label>
|
||||||
|
<input type="text" id="type" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="description">Beschreibung (optional)</label>
|
||||||
|
<textarea id="description" class="h-18"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button primary type="submit" :disabled="status == 'loading' || status?.status == 'success'">erstellen</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 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 { useEquipmentTypeStore } from "@/stores/admin/unit/equipmentType/equipmentType";
|
||||||
|
import type { CreateEquipmentTypeViewModel } from "@/viewmodels/admin/unit/equipmentType/equipmentType.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
timeout: undefined as any,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useEquipmentTypeStore, ["createEquipmentType"]),
|
||||||
|
triggerCreate(e: any) {
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let createEquipmentType: CreateEquipmentTypeViewModel = {
|
||||||
|
type: formData.type.value,
|
||||||
|
description: formData.description.value,
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.createEquipmentType(createEquipmentType)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,84 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Ausrüstung-Type löschen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<p class="text-center">Type {{ equipmentType?.type }} 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 { useEquipmentTypeStore } from "@/stores/admin/unit/equipmentType/equipmentType";
|
||||||
|
import type { CreateEquipmentTypeViewModel } from "@/viewmodels/admin/unit/equipmentType/equipmentType.models";
|
||||||
|
</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(useModalStore, ["data"]),
|
||||||
|
...mapState(useEquipmentTypeStore, ["equipmentTypes"]),
|
||||||
|
equipmentType() {
|
||||||
|
return this.equipmentTypes.find((m) => m.id == this.data);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useEquipmentTypeStore, ["deleteEquipmentType"]),
|
||||||
|
triggerDelete() {
|
||||||
|
this.status = "loading";
|
||||||
|
this.deleteEquipmentType(this.data)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.$router.push({ name: "admin-unit-equipment_type" });
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-equipment_type-overview', params: { equipmentTypeId: equipmentType.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>
|
||||||
|
{{ equipmentType.type }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { EquipmentTypeViewModel } from "@/viewmodels/admin/unit/equipmentType/equipmentType.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
equipmentType: { type: Object as PropType<EquipmentTypeViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<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 v-if="inspectionPlan">Code: {{ inspectionPlan }}</p>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { InspectionPlanViewModel } from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
inspectionPlan: { type: Object as PropType<InspectionPlanViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,155 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Mitglied erstellen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<form class="flex flex-col gap-4 py-2" @submit.prevent="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<Listbox v-model="selectedSalutation" name="salutation" by="id">
|
||||||
|
<ListboxLabel>Anrede</ListboxLabel>
|
||||||
|
<div class="relative mt-1">
|
||||||
|
<ListboxButton
|
||||||
|
class="rounded-md shadow-xs relative block w-full px-3 py-2 border border-gray-300 focus:border-primary placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-hidden focus:ring-0 focus:z-10 sm:text-sm resize-none"
|
||||||
|
>
|
||||||
|
<span class="block truncate w-full text-start"> {{ selectedSalutation?.salutation }}</span>
|
||||||
|
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
|
<ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</ListboxButton>
|
||||||
|
|
||||||
|
<transition
|
||||||
|
leave-active-class="transition duration-100 ease-in"
|
||||||
|
leave-from-class="opacity-100"
|
||||||
|
leave-to-class="opacity-0"
|
||||||
|
>
|
||||||
|
<ListboxOptions
|
||||||
|
class="absolute mt-1 max-h-60 z-20 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black/5 focus:outline-hidden sm:text-sm h-32 overflow-y-auto"
|
||||||
|
>
|
||||||
|
<ListboxOption
|
||||||
|
v-slot="{ active, selected }"
|
||||||
|
v-for="salutation in salutations"
|
||||||
|
:key="salutation.id"
|
||||||
|
:value="salutation"
|
||||||
|
as="template"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
:class="[
|
||||||
|
active ? 'bg-red-200 text-amber-900' : 'text-gray-900',
|
||||||
|
'relative cursor-default select-none py-2 pl-10 pr-4',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span :class="[selected ? 'font-medium' : 'font-normal', 'block truncate']">{{
|
||||||
|
salutation.salutation
|
||||||
|
}}</span>
|
||||||
|
<span v-if="selected" class="absolute inset-y-0 left-0 flex items-center pl-3 text-primary">
|
||||||
|
<CheckIcon class="h-5 w-5" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ListboxOption>
|
||||||
|
</ListboxOptions>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</Listbox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="firstname">Vorname</label>
|
||||||
|
<input type="text" id="firstname" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="lastname">Nachname</label>
|
||||||
|
<input type="text" id="lastname" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="nameaffix">Nameaffix (optional)</label>
|
||||||
|
<input type="text" id="nameaffix" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="birthdate">Geburtsdatum</label>
|
||||||
|
<input type="date" id="birthdate" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="internalId">Interne ID (optional)</label>
|
||||||
|
<input type="text" id="internalId" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button primary type="submit" :disabled="status == 'loading' || status?.status == 'success'">erstellen</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 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 { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
||||||
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
import type { CreateEquipmentViewModel } from "@/viewmodels/admin/unit/equipment/equipment.models";
|
||||||
|
import { useSalutationStore } from "../../../../stores/admin/configuration/salutation";
|
||||||
|
import type { SalutationViewModel } from "../../../../viewmodels/admin/configuration/salutation.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
timeout: undefined as any,
|
||||||
|
selectedSalutation: null as null | SalutationViewModel,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useSalutationStore, ["salutations"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchSalutations();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useEquipmentStore, ["createEquipment"]),
|
||||||
|
...mapActions(useSalutationStore, ["fetchSalutations"]),
|
||||||
|
triggerCreate(e: any) {
|
||||||
|
if (!this.selectedSalutation) return;
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let createEquipment: CreateEquipmentViewModel = {
|
||||||
|
code: "",
|
||||||
|
name: "",
|
||||||
|
location: "",
|
||||||
|
equipmentTypeId: "",
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.createEquipment(createEquipment)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-respiratory_gear-overview', params: { respiratoryGearId: respiratoryGear.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>
|
||||||
|
{{ respiratoryGear.equipment.name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-2">
|
||||||
|
<p v-if="respiratoryGear.equipment">Code: {{ respiratoryGear.equipment.code }}</p>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { RespiratoryGearViewModel } from "@/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
respiratoryGear: { type: Object as PropType<RespiratoryGearViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,155 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Mitglied erstellen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<form class="flex flex-col gap-4 py-2" @submit.prevent="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<Listbox v-model="selectedSalutation" name="salutation" by="id">
|
||||||
|
<ListboxLabel>Anrede</ListboxLabel>
|
||||||
|
<div class="relative mt-1">
|
||||||
|
<ListboxButton
|
||||||
|
class="rounded-md shadow-xs relative block w-full px-3 py-2 border border-gray-300 focus:border-primary placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-hidden focus:ring-0 focus:z-10 sm:text-sm resize-none"
|
||||||
|
>
|
||||||
|
<span class="block truncate w-full text-start"> {{ selectedSalutation?.salutation }}</span>
|
||||||
|
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
|
<ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</ListboxButton>
|
||||||
|
|
||||||
|
<transition
|
||||||
|
leave-active-class="transition duration-100 ease-in"
|
||||||
|
leave-from-class="opacity-100"
|
||||||
|
leave-to-class="opacity-0"
|
||||||
|
>
|
||||||
|
<ListboxOptions
|
||||||
|
class="absolute mt-1 max-h-60 z-20 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black/5 focus:outline-hidden sm:text-sm h-32 overflow-y-auto"
|
||||||
|
>
|
||||||
|
<ListboxOption
|
||||||
|
v-slot="{ active, selected }"
|
||||||
|
v-for="salutation in salutations"
|
||||||
|
:key="salutation.id"
|
||||||
|
:value="salutation"
|
||||||
|
as="template"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
:class="[
|
||||||
|
active ? 'bg-red-200 text-amber-900' : 'text-gray-900',
|
||||||
|
'relative cursor-default select-none py-2 pl-10 pr-4',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span :class="[selected ? 'font-medium' : 'font-normal', 'block truncate']">{{
|
||||||
|
salutation.salutation
|
||||||
|
}}</span>
|
||||||
|
<span v-if="selected" class="absolute inset-y-0 left-0 flex items-center pl-3 text-primary">
|
||||||
|
<CheckIcon class="h-5 w-5" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ListboxOption>
|
||||||
|
</ListboxOptions>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</Listbox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="firstname">Vorname</label>
|
||||||
|
<input type="text" id="firstname" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="lastname">Nachname</label>
|
||||||
|
<input type="text" id="lastname" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="nameaffix">Nameaffix (optional)</label>
|
||||||
|
<input type="text" id="nameaffix" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="birthdate">Geburtsdatum</label>
|
||||||
|
<input type="date" id="birthdate" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="internalId">Interne ID (optional)</label>
|
||||||
|
<input type="text" id="internalId" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button primary type="submit" :disabled="status == 'loading' || status?.status == 'success'">erstellen</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 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 { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
||||||
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
import type { CreateEquipmentViewModel } from "@/viewmodels/admin/unit/equipment/equipment.models";
|
||||||
|
import { useSalutationStore } from "../../../../stores/admin/configuration/salutation";
|
||||||
|
import type { SalutationViewModel } from "../../../../viewmodels/admin/configuration/salutation.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
timeout: undefined as any,
|
||||||
|
selectedSalutation: null as null | SalutationViewModel,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useSalutationStore, ["salutations"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchSalutations();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useEquipmentStore, ["createEquipment"]),
|
||||||
|
...mapActions(useSalutationStore, ["fetchSalutations"]),
|
||||||
|
triggerCreate(e: any) {
|
||||||
|
if (!this.selectedSalutation) return;
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let createEquipment: CreateEquipmentViewModel = {
|
||||||
|
code: "",
|
||||||
|
name: "",
|
||||||
|
location: "",
|
||||||
|
equipmentTypeId: "",
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.createEquipment(createEquipment)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<template>
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-respiratory_mission-overview', params: { respiratoryMissionId: respiratoryMission.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>{{ respiratoryMission.title }} - {{ respiratoryMission.date }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-2">
|
||||||
|
<p v-if="respiratoryMission.description">{{ respiratoryMission.description }}</p>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { RespiratoryMissionViewModel } from "@/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
respiratoryMission: { type: Object as PropType<RespiratoryMissionViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,155 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Mitglied erstellen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<form class="flex flex-col gap-4 py-2" @submit.prevent="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<Listbox v-model="selectedSalutation" name="salutation" by="id">
|
||||||
|
<ListboxLabel>Anrede</ListboxLabel>
|
||||||
|
<div class="relative mt-1">
|
||||||
|
<ListboxButton
|
||||||
|
class="rounded-md shadow-xs relative block w-full px-3 py-2 border border-gray-300 focus:border-primary placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-hidden focus:ring-0 focus:z-10 sm:text-sm resize-none"
|
||||||
|
>
|
||||||
|
<span class="block truncate w-full text-start"> {{ selectedSalutation?.salutation }}</span>
|
||||||
|
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
|
<ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</ListboxButton>
|
||||||
|
|
||||||
|
<transition
|
||||||
|
leave-active-class="transition duration-100 ease-in"
|
||||||
|
leave-from-class="opacity-100"
|
||||||
|
leave-to-class="opacity-0"
|
||||||
|
>
|
||||||
|
<ListboxOptions
|
||||||
|
class="absolute mt-1 max-h-60 z-20 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black/5 focus:outline-hidden sm:text-sm h-32 overflow-y-auto"
|
||||||
|
>
|
||||||
|
<ListboxOption
|
||||||
|
v-slot="{ active, selected }"
|
||||||
|
v-for="salutation in salutations"
|
||||||
|
:key="salutation.id"
|
||||||
|
:value="salutation"
|
||||||
|
as="template"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
:class="[
|
||||||
|
active ? 'bg-red-200 text-amber-900' : 'text-gray-900',
|
||||||
|
'relative cursor-default select-none py-2 pl-10 pr-4',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span :class="[selected ? 'font-medium' : 'font-normal', 'block truncate']">{{
|
||||||
|
salutation.salutation
|
||||||
|
}}</span>
|
||||||
|
<span v-if="selected" class="absolute inset-y-0 left-0 flex items-center pl-3 text-primary">
|
||||||
|
<CheckIcon class="h-5 w-5" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ListboxOption>
|
||||||
|
</ListboxOptions>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</Listbox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="firstname">Vorname</label>
|
||||||
|
<input type="text" id="firstname" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="lastname">Nachname</label>
|
||||||
|
<input type="text" id="lastname" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="nameaffix">Nameaffix (optional)</label>
|
||||||
|
<input type="text" id="nameaffix" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="birthdate">Geburtsdatum</label>
|
||||||
|
<input type="date" id="birthdate" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="internalId">Interne ID (optional)</label>
|
||||||
|
<input type="text" id="internalId" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button primary type="submit" :disabled="status == 'loading' || status?.status == 'success'">erstellen</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 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 { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
||||||
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
import type { CreateEquipmentViewModel } from "@/viewmodels/admin/unit/equipment/equipment.models";
|
||||||
|
import { useSalutationStore } from "../../../../stores/admin/configuration/salutation";
|
||||||
|
import type { SalutationViewModel } from "../../../../viewmodels/admin/configuration/salutation.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
timeout: undefined as any,
|
||||||
|
selectedSalutation: null as null | SalutationViewModel,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useSalutationStore, ["salutations"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchSalutations();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useEquipmentStore, ["createEquipment"]),
|
||||||
|
...mapActions(useSalutationStore, ["fetchSalutations"]),
|
||||||
|
triggerCreate(e: any) {
|
||||||
|
if (!this.selectedSalutation) return;
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let createEquipment: CreateEquipmentViewModel = {
|
||||||
|
code: "",
|
||||||
|
name: "",
|
||||||
|
location: "",
|
||||||
|
equipmentTypeId: "",
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.createEquipment(createEquipment)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<template>
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-respiratory_wearer-overview', params: { respiratoryWearerId: respiratoryWearer.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>
|
||||||
|
{{ respiratoryWearer.member.lastname }}, {{ respiratoryWearer.member.firstname }}
|
||||||
|
{{ respiratoryWearer.member.nameaffix ? `- ${respiratoryWearer.member.nameaffix}` : "" }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-2">
|
||||||
|
<p v-if="respiratoryWearer.internalId">ID: {{ respiratoryWearer.member.internalId }}</p>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { RespiratoryWearerViewModel } from "@/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
respiratoryWearer: { type: Object as PropType<RespiratoryWearerViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
103
src/components/admin/unit/vehicle/CreateVehicleModal.vue
Normal file
103
src/components/admin/unit/vehicle/CreateVehicleModal.vue
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Mitglied erstellen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<form class="flex flex-col gap-4 py-2" @submit.prevent="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<label for="firstname">Vorname</label>
|
||||||
|
<input type="text" id="firstname" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="lastname">Nachname</label>
|
||||||
|
<input type="text" id="lastname" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="nameaffix">Nameaffix (optional)</label>
|
||||||
|
<input type="text" id="nameaffix" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="birthdate">Geburtsdatum</label>
|
||||||
|
<input type="date" id="birthdate" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="internalId">Interne ID (optional)</label>
|
||||||
|
<input type="text" id="internalId" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button primary type="submit" :disabled="status == 'loading' || status?.status == 'success'">erstellen</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 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 { Listbox, ListboxButton, ListboxOptions, ListboxOption, ListboxLabel } from "@headlessui/vue";
|
||||||
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
|
import { useVehicleStore } from "@/stores/admin/unit/vehicle/vehicle";
|
||||||
|
import type { CreateVehicleViewModel } from "@/viewmodels/admin/unit/vehicle/vehicle.models";
|
||||||
|
import { useSalutationStore } from "../../../../stores/admin/configuration/salutation";
|
||||||
|
import type { SalutationViewModel } from "../../../../viewmodels/admin/configuration/salutation.models";
|
||||||
|
</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(useSalutationStore, ["salutations"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchSalutations();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useVehicleStore, ["createVehicle"]),
|
||||||
|
...mapActions(useSalutationStore, ["fetchSalutations"]),
|
||||||
|
triggerCreate(e: any) {
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let createVehicle: CreateVehicleViewModel = {
|
||||||
|
name: "",
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.createVehicle(createVehicle)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
28
src/components/admin/unit/vehicle/VehicleListItem.vue
Normal file
28
src/components/admin/unit/vehicle/VehicleListItem.vue
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<template>
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-vehicle-overview', params: { vehicleId: vehicle.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>{{ vehicle.name }}</p>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { VehicleViewModel } from "@/viewmodels/admin/unit/vehicle/vehicle.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
vehicle: { type: Object as PropType<VehicleViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Typ erstellen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<form class="flex flex-col gap-4 py-2" @submit.prevent="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<label for="type">Typ</label>
|
||||||
|
<input type="text" id="type" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="description">Beschreibung (optional)</label>
|
||||||
|
<textarea id="description" class="h-18"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button primary type="submit" :disabled="status == 'loading' || status?.status == 'success'">erstellen</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 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 { useVehicleTypeStore } from "@/stores/admin/unit/vehicleType/vehicleType";
|
||||||
|
import type { CreateVehicleTypeViewModel } from "@/viewmodels/admin/unit/vehicleType/vehicleType.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
timeout: undefined as any,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useVehicleTypeStore, ["createVehicleType"]),
|
||||||
|
triggerCreate(e: any) {
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let createVehicleType: CreateVehicleTypeViewModel = {
|
||||||
|
type: formData.type.value,
|
||||||
|
description: formData.description.value,
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.createVehicleType(createVehicleType)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,83 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Ausrüstung-Type löschen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<p class="text-center">Type {{ vehicleType?.type }} 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 { useVehicleTypeStore } from "@/stores/admin/unit/vehicleType/vehicleType";
|
||||||
|
</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(useModalStore, ["data"]),
|
||||||
|
...mapState(useVehicleTypeStore, ["vehicleTypes"]),
|
||||||
|
vehicleType() {
|
||||||
|
return this.vehicleTypes.find((m) => m.id == this.data);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useVehicleTypeStore, ["deleteVehicleType"]),
|
||||||
|
triggerDelete() {
|
||||||
|
this.status = "loading";
|
||||||
|
this.deleteVehicleType(this.data)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.$router.push({ name: "admin-unit-vehicle_type" });
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,30 @@
|
||||||
|
<template>
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-vehicle_type-overview', params: { vehicleTypeId: vehicleType.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>
|
||||||
|
{{ vehicleType.type }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { VehicleTypeViewModel } from "@/viewmodels/admin/unit/vehicleType/vehicleType.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
vehicleType: { type: Object as PropType<VehicleTypeViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
33
src/components/admin/unit/wearable/WearableListItem.vue
Normal file
33
src/components/admin/unit/wearable/WearableListItem.vue
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<template>
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-wearable-overview', params: { wearableId: wearable.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>
|
||||||
|
{{ wearable.name }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="p-2">
|
||||||
|
<p v-if="wearable.code">Code: {{ wearable.code }}</p>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { WearableViewModel } from "@/viewmodels/admin/unit/wearable/wearable.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
wearable: { type: Object as PropType<WearableViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,82 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Typ erstellen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<form class="flex flex-col gap-4 py-2" @submit.prevent="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<label for="type">Typ</label>
|
||||||
|
<input type="text" id="type" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="description">Beschreibung (optional)</label>
|
||||||
|
<textarea id="description" class="h-18"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button primary type="submit" :disabled="status == 'loading' || status?.status == 'success'">erstellen</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 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 { useWearableTypeStore } from "@/stores/admin/unit/wearableType/wearableType";
|
||||||
|
import type { CreateWearableTypeViewModel } from "@/viewmodels/admin/unit/wearableType/wearableType.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
timeout: undefined as any,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useWearableTypeStore, ["createWearableType"]),
|
||||||
|
triggerCreate(e: any) {
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let createWearableType: CreateWearableTypeViewModel = {
|
||||||
|
type: formData.type.value,
|
||||||
|
description: formData.description.value,
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.createWearableType(createWearableType)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,84 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Ausrüstung-Type löschen</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<p class="text-center">Type {{ wearableType?.type }} 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 { useWearableTypeStore } from "@/stores/admin/unit/wearableType/wearableType";
|
||||||
|
import type { CreateWearableTypeViewModel } from "@/viewmodels/admin/unit/wearableType/wearableType.models";
|
||||||
|
</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(useModalStore, ["data"]),
|
||||||
|
...mapState(useWearableTypeStore, ["wearableTypes"]),
|
||||||
|
wearableType() {
|
||||||
|
return this.wearableTypes.find((m) => m.id == this.data);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useWearableTypeStore, ["deleteWearableType"]),
|
||||||
|
triggerDelete() {
|
||||||
|
this.status = "loading";
|
||||||
|
this.deleteWearableType(this.data)
|
||||||
|
.then(() => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.$router.push({ name: "admin-unit-wearable_type" });
|
||||||
|
this.closeModal();
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,27 @@
|
||||||
|
<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>
|
||||||
|
{{ wearableType.type }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, type PropType } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { WearableTypeViewModel } from "@/viewmodels/admin/unit/wearableType/wearableType.models";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
wearableType: { type: Object as PropType<WearableTypeViewModel>, default: {} },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
14
src/demodata/damageReport.ts
Normal file
14
src/demodata/damageReport.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import type { DamageReportViewModel } from "../viewmodels/admin/unit/damageReport/damageReport.models";
|
||||||
|
import { equipmentDemoData } from "./equipment";
|
||||||
|
|
||||||
|
export const damageReportDemoData: Array<DamageReportViewModel> = [
|
||||||
|
{
|
||||||
|
id: "sdfgh",
|
||||||
|
reported: new Date(),
|
||||||
|
status: "",
|
||||||
|
done: false,
|
||||||
|
description: "knjgljna g",
|
||||||
|
affectedEquipmentId: equipmentDemoData[0].id,
|
||||||
|
affectedEquipment: equipmentDemoData[0],
|
||||||
|
},
|
||||||
|
];
|
13
src/demodata/equipment.ts
Normal file
13
src/demodata/equipment.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import type { EquipmentViewModel } from "../viewmodels/admin/unit/equipment/equipment.models";
|
||||||
|
import { equipmentTypeDemoData } from "./equipmentType";
|
||||||
|
|
||||||
|
export const equipmentDemoData: Array<EquipmentViewModel> = [
|
||||||
|
{
|
||||||
|
id: "abc",
|
||||||
|
code: "0456984224498",
|
||||||
|
name: "B-Schlauch",
|
||||||
|
location: "HLF",
|
||||||
|
equipmentTypeId: equipmentTypeDemoData[0].id,
|
||||||
|
equipmentType: equipmentTypeDemoData[0],
|
||||||
|
},
|
||||||
|
];
|
9
src/demodata/equipmentType.ts
Normal file
9
src/demodata/equipmentType.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import type { EquipmentTypeViewModel } from "../viewmodels/admin/unit/equipmentType/equipmentType.models";
|
||||||
|
|
||||||
|
export const equipmentTypeDemoData: Array<EquipmentTypeViewModel> = [
|
||||||
|
{
|
||||||
|
id: "xyz",
|
||||||
|
type: "B-Schlauch",
|
||||||
|
description: "Shläuche vom Typ B",
|
||||||
|
},
|
||||||
|
];
|
12
src/demodata/inspectionPlan.ts
Normal file
12
src/demodata/inspectionPlan.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import type { InspectionPlanViewModel } from "../viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
|
||||||
|
import { equipmentTypeDemoData } from "./equipmentType";
|
||||||
|
|
||||||
|
export const inspectionPlanDemoData: Array<InspectionPlanViewModel> = [
|
||||||
|
{
|
||||||
|
id: "abc",
|
||||||
|
title: "Sichtprüfung",
|
||||||
|
inspectionInterval: "1-m",
|
||||||
|
equipmentTypeId: equipmentTypeDemoData[0].id,
|
||||||
|
equipmentType: equipmentTypeDemoData[0],
|
||||||
|
},
|
||||||
|
];
|
10
src/demodata/respiratoryGear.ts
Normal file
10
src/demodata/respiratoryGear.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import type { RespiratoryGearViewModel } from "../viewmodels/admin/unit/respiratoryGear/respiratoryGear.models";
|
||||||
|
import { equipmentDemoData } from "./equipment";
|
||||||
|
|
||||||
|
export const respiratoryGearDemoData: Array<RespiratoryGearViewModel> = [
|
||||||
|
{
|
||||||
|
id: "adfsg",
|
||||||
|
equipmentId: equipmentDemoData[0].id,
|
||||||
|
equipment: equipmentDemoData[0],
|
||||||
|
},
|
||||||
|
];
|
11
src/demodata/respiratoryMission.ts
Normal file
11
src/demodata/respiratoryMission.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import type { RespiratoryMissionViewModel } from "../viewmodels/admin/unit/respiratoryMission/respiratoryMission.models";
|
||||||
|
import { equipmentDemoData } from "./equipment";
|
||||||
|
|
||||||
|
export const respiratoryMissionDemoData: Array<RespiratoryMissionViewModel> = [
|
||||||
|
{
|
||||||
|
id: "adfsg",
|
||||||
|
date: new Date(),
|
||||||
|
title: "B5",
|
||||||
|
description: "B5 Einsatz",
|
||||||
|
},
|
||||||
|
];
|
17
src/demodata/respiratoryWearer.ts
Normal file
17
src/demodata/respiratoryWearer.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import type { RespiratoryWearerViewModel } from "../viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models";
|
||||||
|
|
||||||
|
export const respiratoryWearerDemoData: Array<RespiratoryWearerViewModel> = [
|
||||||
|
{
|
||||||
|
id: "dfghj",
|
||||||
|
memberId: "9469991d-fa22-4899-82ce-b1ba5de990dc",
|
||||||
|
member: {
|
||||||
|
id: "9469991d-fa22-4899-82ce-b1ba5de990dc",
|
||||||
|
salutation: { id: 3, salutation: "Herr" },
|
||||||
|
firstname: "Julian",
|
||||||
|
lastname: "Krauser",
|
||||||
|
nameaffix: "",
|
||||||
|
birthdate: new Date("2003-09-20"),
|
||||||
|
internalId: "1312",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
14
src/demodata/vehicle.ts
Normal file
14
src/demodata/vehicle.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import type { VehicleViewModel } from "../viewmodels/admin/unit/vehicle/vehicle.models";
|
||||||
|
|
||||||
|
export const vehicleDemoData: Array<VehicleViewModel> = [
|
||||||
|
{
|
||||||
|
id: "kjhb",
|
||||||
|
name: "HLF",
|
||||||
|
type: "HLF 20/10",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "kjhb",
|
||||||
|
name: "LF",
|
||||||
|
type: "LF 8/6",
|
||||||
|
},
|
||||||
|
];
|
9
src/demodata/vehicleType.ts
Normal file
9
src/demodata/vehicleType.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import type { VehicleTypeViewModel } from "../viewmodels/admin/unit/vehicleType/vehicleType.models";
|
||||||
|
|
||||||
|
export const vehicleTypeDemoData: Array<VehicleTypeViewModel> = [
|
||||||
|
{
|
||||||
|
id: "xyz",
|
||||||
|
type: "HLF",
|
||||||
|
description: "HLF",
|
||||||
|
},
|
||||||
|
];
|
22
src/demodata/wearable.ts
Normal file
22
src/demodata/wearable.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import type { WearableViewModel } from "../viewmodels/admin/unit/wearable/wearable.models";
|
||||||
|
import { wearableTypeDemoData } from "./wearableType";
|
||||||
|
|
||||||
|
export const wearableDemoData: Array<WearableViewModel> = [
|
||||||
|
{
|
||||||
|
id: "abc",
|
||||||
|
code: "0456984224498",
|
||||||
|
name: "B-Schlauch",
|
||||||
|
wearerId: "9469991d-fa22-4899-82ce-b1ba5de990dc",
|
||||||
|
wearer: {
|
||||||
|
id: "9469991d-fa22-4899-82ce-b1ba5de990dc",
|
||||||
|
salutation: { id: 3, salutation: "Herr" },
|
||||||
|
firstname: "Julian",
|
||||||
|
lastname: "Krauser",
|
||||||
|
nameaffix: "",
|
||||||
|
birthdate: new Date("2003-09-20"),
|
||||||
|
internalId: "1312",
|
||||||
|
},
|
||||||
|
wearableTypeId: wearableTypeDemoData[0].id,
|
||||||
|
wearableType: wearableTypeDemoData[0],
|
||||||
|
},
|
||||||
|
];
|
9
src/demodata/wearableType.ts
Normal file
9
src/demodata/wearableType.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import type { WearableTypeViewModel } from "../viewmodels/admin/unit/wearableType/wearableType.models";
|
||||||
|
|
||||||
|
export const wearableTypeDemoData: Array<WearableTypeViewModel> = [
|
||||||
|
{
|
||||||
|
id: "xyz",
|
||||||
|
type: "Jacke",
|
||||||
|
description: "Bayern 2000",
|
||||||
|
},
|
||||||
|
];
|
143
src/helpers/codeScanner.ts
Normal file
143
src/helpers/codeScanner.ts
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
import type { BarcodeFormat, DetectedBarcode } from "barcode-detector/pure";
|
||||||
|
|
||||||
|
/*** select camera ***/
|
||||||
|
export interface Camera {
|
||||||
|
label: string;
|
||||||
|
constraints: {
|
||||||
|
deviceId?: string;
|
||||||
|
facingMode: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export const defaultConstraintOptions: Array<Camera> = [
|
||||||
|
{ label: "rear camera", constraints: { facingMode: "environment" } },
|
||||||
|
{ label: "front camera", constraints: { facingMode: "user" } },
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function getAvailableCameras(useDefault: boolean = false): Promise<Array<Camera>> {
|
||||||
|
// NOTE: on iOS we can't invoke `enumerateDevices` before the user has given
|
||||||
|
// camera access permission. `QrcodeStream` internally takes care of
|
||||||
|
// requesting the permissions. The `camera-on` event should guarantee that this
|
||||||
|
// has happened.
|
||||||
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
|
const videoDevices = devices.filter(({ kind }) => kind === "videoinput");
|
||||||
|
|
||||||
|
return [
|
||||||
|
...(useDefault ? defaultConstraintOptions : []),
|
||||||
|
...videoDevices.map(({ deviceId, label }) => ({
|
||||||
|
label: `${label}`, //(ID: ${deviceId})
|
||||||
|
constraints: { deviceId, facingMode: "custom" },
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** track functons ***/
|
||||||
|
export function paintOutline(detectedCodes: DetectedBarcode[], ctx: CanvasRenderingContext2D) {
|
||||||
|
for (const detectedCode of detectedCodes) {
|
||||||
|
const [firstPoint, ...otherPoints] = detectedCode.cornerPoints;
|
||||||
|
|
||||||
|
ctx.strokeStyle = "red";
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(firstPoint.x, firstPoint.y);
|
||||||
|
for (const { x, y } of otherPoints) {
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
|
ctx.lineTo(firstPoint.x, firstPoint.y);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function paintBoundingBox(detectedCodes: DetectedBarcode[], ctx: CanvasRenderingContext2D) {
|
||||||
|
for (const detectedCode of detectedCodes) {
|
||||||
|
const {
|
||||||
|
boundingBox: { x, y, width, height },
|
||||||
|
} = detectedCode;
|
||||||
|
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeStyle = "#007bff";
|
||||||
|
ctx.strokeRect(x, y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function paintCenterText(detectedCodes: DetectedBarcode[], ctx: CanvasRenderingContext2D) {
|
||||||
|
for (const detectedCode of detectedCodes) {
|
||||||
|
const { boundingBox, rawValue } = detectedCode;
|
||||||
|
|
||||||
|
const centerX = boundingBox.x + boundingBox.width / 2;
|
||||||
|
const centerY = boundingBox.y + boundingBox.height / 2;
|
||||||
|
|
||||||
|
const fontSize = Math.max(12, (50 * boundingBox.width) / ctx.canvas.width);
|
||||||
|
|
||||||
|
ctx.font = `bold ${fontSize}px sans-serif`;
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.strokeStyle = "#35495e";
|
||||||
|
ctx.strokeText(detectedCode.rawValue, centerX, centerY);
|
||||||
|
|
||||||
|
ctx.fillStyle = "#5cb984";
|
||||||
|
ctx.fillText(rawValue, centerX, centerY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const trackFunctionOptions = [
|
||||||
|
{ text: "nothing (default)", value: undefined },
|
||||||
|
{ text: "outline", value: paintOutline },
|
||||||
|
{ text: "centered text", value: paintCenterText },
|
||||||
|
{ text: "bounding box", value: paintBoundingBox },
|
||||||
|
{
|
||||||
|
text: "mixed",
|
||||||
|
value: (detectedCodes: DetectedBarcode[], ctx: CanvasRenderingContext2D) => {
|
||||||
|
paintOutline(detectedCodes, ctx);
|
||||||
|
paintCenterText(detectedCodes, ctx);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/*** barcode formats ***/
|
||||||
|
export const barcodeFormats: Array<BarcodeFormat> = [
|
||||||
|
"aztec",
|
||||||
|
"code_128",
|
||||||
|
"code_39",
|
||||||
|
"code_93",
|
||||||
|
"codabar",
|
||||||
|
"databar",
|
||||||
|
"databar_expanded",
|
||||||
|
"data_matrix",
|
||||||
|
"dx_film_edge",
|
||||||
|
"ean_13",
|
||||||
|
"ean_8",
|
||||||
|
"itf",
|
||||||
|
"maxi_code",
|
||||||
|
"micro_qr_code",
|
||||||
|
"pdf417",
|
||||||
|
"qr_code",
|
||||||
|
"rm_qr_code",
|
||||||
|
"upc_a",
|
||||||
|
"upc_e",
|
||||||
|
"linear_codes",
|
||||||
|
"matrix_codes",
|
||||||
|
];
|
||||||
|
|
||||||
|
/*** error handling ***/
|
||||||
|
export function handleScannerError(err: Error) {
|
||||||
|
let error = `[${err.name}]: `;
|
||||||
|
|
||||||
|
if (err.name === "NotAllowedError") {
|
||||||
|
error += "you need to grant camera access permission";
|
||||||
|
} else if (err.name === "NotFoundError") {
|
||||||
|
error += "no camera on this device";
|
||||||
|
} else if (err.name === "NotSupportedError") {
|
||||||
|
error += "secure context required (HTTPS, localhost)";
|
||||||
|
} else if (err.name === "NotReadableError") {
|
||||||
|
error += "is the camera already in use?";
|
||||||
|
} else if (err.name === "OverconstrainedError") {
|
||||||
|
error += "installed cameras are not suitable";
|
||||||
|
} else if (err.name === "StreamApiNotSupportedError") {
|
||||||
|
error += "Stream API is not supported in this browser";
|
||||||
|
} else if (err.name === "InsecureContextError") {
|
||||||
|
error += "Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.";
|
||||||
|
} else {
|
||||||
|
error += err.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
|
@ -95,7 +95,7 @@ select {
|
||||||
input[readonly],
|
input[readonly],
|
||||||
textarea[readonly],
|
textarea[readonly],
|
||||||
select[readonly] {
|
select[readonly] {
|
||||||
@apply select-none;
|
@apply select-none focus:border-gray-300 cursor-default;
|
||||||
/* pointer-events-none; */
|
/* pointer-events-none; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
|
import { useNewsletterStore } from "@/stores/admin/club/newsletter/newsletter";
|
||||||
import { useNewsletterDatesStore } from "@/stores/admin/club/newsletter/newsletterDates";
|
import { useNewsletterDatesStore } from "@/stores/admin/club/newsletter/newsletterDates";
|
||||||
import { useNewsletterRecipientsStore } from "@/stores/admin/club/newsletter/newsletterRecipients";
|
import { useNewsletterRecipientsStore } from "@/stores/admin/club/newsletter/newsletterRecipients";
|
||||||
import { useNewsletterPrintoutStore } from "../stores/admin/club/newsletter/newsletterPrintout";
|
import { useNewsletterPrintoutStore } from "../../stores/admin/club/newsletter/newsletterPrintout";
|
||||||
|
|
||||||
export async function setNewsletterId(to: any, from: any, next: any) {
|
export async function setNewsletterId(to: any, from: any, next: any) {
|
||||||
const newsletter = useNewsletterStore();
|
const newsletter = useNewsletterStore();
|
|
@ -3,7 +3,7 @@ import { useProtocolAgendaStore } from "@/stores/admin/club/protocol/protocolAge
|
||||||
import { useProtocolDecisionStore } from "@/stores/admin/club/protocol/protocolDecision";
|
import { useProtocolDecisionStore } from "@/stores/admin/club/protocol/protocolDecision";
|
||||||
import { useProtocolPresenceStore } from "@/stores/admin/club/protocol/protocolPresence";
|
import { useProtocolPresenceStore } from "@/stores/admin/club/protocol/protocolPresence";
|
||||||
import { useProtocolVotingStore } from "@/stores/admin/club/protocol/protocolVoting";
|
import { useProtocolVotingStore } from "@/stores/admin/club/protocol/protocolVoting";
|
||||||
import { useProtocolPrintoutStore } from "../stores/admin/club/protocol/protocolPrintout";
|
import { useProtocolPrintoutStore } from "../../stores/admin/club/protocol/protocolPrintout";
|
||||||
|
|
||||||
export async function setProtocolId(to: any, from: any, next: any) {
|
export async function setProtocolId(to: any, from: any, next: any) {
|
||||||
const protocol = useProtocolStore();
|
const protocol = useProtocolStore();
|
|
@ -6,11 +6,19 @@ import { loadAccountData } from "./accountGuard";
|
||||||
import { isSetup } from "./setupGuard";
|
import { isSetup } from "./setupGuard";
|
||||||
import { abilityAndNavUpdate } from "./adminGuard";
|
import { abilityAndNavUpdate } from "./adminGuard";
|
||||||
import type { PermissionType, PermissionSection, PermissionModule } from "@/types/permissionTypes";
|
import type { PermissionType, PermissionSection, PermissionModule } from "@/types/permissionTypes";
|
||||||
import { resetMemberStores, setMemberId } from "./memberGuard";
|
import { resetMemberStores, setMemberId } from "./club/memberGuard";
|
||||||
import { resetProtocolStores, setProtocolId } from "./protocolGuard";
|
import { resetProtocolStores, setProtocolId } from "./club/protocolGuard";
|
||||||
import { resetNewsletterStores, setNewsletterId } from "./newsletterGuard";
|
import { resetNewsletterStores, setNewsletterId } from "./club/newsletterGuard";
|
||||||
import { config } from "../config";
|
import { config } from "../config";
|
||||||
import { setBackupPage } from "./backupGuard";
|
import { setBackupPage } from "./management/backupGuard";
|
||||||
|
import { resetEquipmentTypeStores, setEquipmentTypeId } from "./unit/equipmentType";
|
||||||
|
import { resetEquipmentStores, setEquipmentId } from "./unit/equipment";
|
||||||
|
import { resetVehicleStores, setVehicleId } from "./unit/vehicle";
|
||||||
|
import { resetRespiratoryGearStores, setRespiratoryGearId } from "./unit/respiratoryGear";
|
||||||
|
import { resetRespiratoryWearerStores, setRespiratoryWearerId } from "./unit/respiratoryWearer";
|
||||||
|
import { resetRespiratoryMissionStores, setRespiratoryMissionId } from "./unit/respiratoryMission";
|
||||||
|
import { resetWearableStores, setWearableId } from "./unit/wearable";
|
||||||
|
import { resetInspectionPlanStores, setInspectionPlanId } from "./unit/inspectionPlan";
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
@ -304,6 +312,546 @@ const router = createRouter({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "unit",
|
||||||
|
name: "admin-unit",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-default",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
meta: { type: "read", section: "unit" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "equipment",
|
||||||
|
name: "admin-unit-equipment-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "equipment" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-equipment",
|
||||||
|
component: () => import("@/views/admin/unit/equipment/Equipment.vue"),
|
||||||
|
beforeEnter: [resetEquipmentStores],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "create",
|
||||||
|
name: "admin-unit-equipment-create",
|
||||||
|
component: () => import("@/views/admin/unit/equipment/CreateEquipment.vue"),
|
||||||
|
meta: { type: "create", section: "unit", module: "equipment" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":equipmentId",
|
||||||
|
name: "admin-unit-equipment-routing",
|
||||||
|
component: () => import("@/views/admin/unit/equipment/EquipmentRouting.vue"),
|
||||||
|
beforeEnter: [setEquipmentId],
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "overview",
|
||||||
|
name: "admin-unit-equipment-overview",
|
||||||
|
component: () => import("@/views/admin/unit/equipment/Overview.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "maintenance",
|
||||||
|
name: "admin-unit-equipment-maintenance",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "inspection",
|
||||||
|
name: "admin-unit-equipment-inspection",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "report",
|
||||||
|
name: "admin-unit-equipment-damage_report",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
name: "admin-unit-equipment-edit",
|
||||||
|
component: () => import("@/views/admin/unit/equipment/UpdateEquipment.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "equipment" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "vehicle",
|
||||||
|
name: "admin-unit-vehicle-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "vehicle" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-vehicle",
|
||||||
|
component: () => import("@/views/admin/unit/vehicle/Vehicle.vue"),
|
||||||
|
beforeEnter: [resetVehicleStores],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "create",
|
||||||
|
name: "admin-unit-vehicle-create",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
meta: { type: "create", section: "unit", module: "vehicle" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":vehicleId",
|
||||||
|
name: "admin-unit-vehicle-routing",
|
||||||
|
component: () => import("@/views/admin/unit/vehicle/VehicleRouting.vue"),
|
||||||
|
beforeEnter: [setVehicleId],
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "overview",
|
||||||
|
name: "admin-unit-vehicle-overview",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "maintenance",
|
||||||
|
name: "admin-unit-vehicle-maintenance",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "inspection",
|
||||||
|
name: "admin-unit-vehicle-inspection",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "report",
|
||||||
|
name: "admin-unit-vehicle-damage_report",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
name: "admin-unit-vehicle-edit",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "vehicle" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "wearable",
|
||||||
|
name: "admin-unit-wearable-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "wearable" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-wearable",
|
||||||
|
component: () => import("@/views/admin/unit/wearable/Wearable.vue"),
|
||||||
|
beforeEnter: [resetWearableStores],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "create",
|
||||||
|
name: "admin-unit-wearable-create",
|
||||||
|
component: () => import("@/views/admin/unit/wearable/CreateWearable.vue"),
|
||||||
|
meta: { type: "create", section: "unit", module: "wearable" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":wearableId",
|
||||||
|
name: "admin-unit-wearable-routing",
|
||||||
|
component: () => import("@/views/admin/unit/wearable/WearableRouting.vue"),
|
||||||
|
beforeEnter: [setWearableId],
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "overview",
|
||||||
|
name: "admin-unit-wearable-overview",
|
||||||
|
component: () => import("@/views/admin/unit/wearable/Overview.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "report",
|
||||||
|
name: "admin-unit-wearable-damage_report",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
name: "admin-unit-wearable-edit",
|
||||||
|
component: () => import("@/views/admin/unit/wearable/UpdateWearable.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "wearable" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "respiratory-gear",
|
||||||
|
name: "admin-unit-respiratory_gear-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "respiratory_gear" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-respiratory_gear",
|
||||||
|
component: () => import("@/views/admin/unit/respiratoryGear/RespiratoryGear.vue"),
|
||||||
|
beforeEnter: [resetRespiratoryGearStores],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":respiratoryGearId",
|
||||||
|
name: "admin-unit-respiratory_gear-routing",
|
||||||
|
component: () => import("@/views/admin/unit/respiratoryGear/RespiratoryGearRouting.vue"),
|
||||||
|
beforeEnter: [setRespiratoryGearId],
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "overview",
|
||||||
|
name: "admin-unit-respiratory_gear-overview",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "maintenance",
|
||||||
|
name: "admin-unit-respiratory_gear-maintenance",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "inspection",
|
||||||
|
name: "admin-unit-respiratory_gear-inspection",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "mission",
|
||||||
|
name: "admin-unit-respiratory_gear-mission",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
name: "admin-unit-respiratory_gear-edit",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "respiratory_gear" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "respiratory-wearer",
|
||||||
|
name: "admin-unit-respiratory_wearer-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "respiratory_wearer" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-respiratory_wearer",
|
||||||
|
component: () => import("@/views/admin/unit/respiratoryWearer/RespiratoryWearer.vue"),
|
||||||
|
beforeEnter: [resetRespiratoryWearerStores],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":respiratoryWearerId",
|
||||||
|
name: "admin-unit-respiratory_wearer-routing",
|
||||||
|
component: () => import("@/views/admin/unit/respiratoryWearer/RespiratoryWearerRouting.vue"),
|
||||||
|
beforeEnter: [setRespiratoryWearerId],
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "overview",
|
||||||
|
name: "admin-unit-respiratory_wearer-overview",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "mission",
|
||||||
|
name: "admin-unit-respiratory_wearer-mission",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "education",
|
||||||
|
name: "admin-unit-respiratory_wearer-education",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "instruction",
|
||||||
|
name: "admin-unit-respiratory_wearer-instruction",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "screening",
|
||||||
|
name: "admin-unit-respiratory_wearer-screening",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "strain",
|
||||||
|
name: "admin-unit-respiratory_wearer-strain",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
name: "admin-unit-respiratory_wearer-edit",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "respiratory_wearer" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "respiratory-mission",
|
||||||
|
name: "admin-unit-respiratory_mission-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "respiratory_mission" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-respiratory_mission",
|
||||||
|
component: () => import("@/views/admin/unit/respiratoryMission/RespiratoryMission.vue"),
|
||||||
|
beforeEnter: [resetRespiratoryMissionStores],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":respiratoryMissionId",
|
||||||
|
name: "admin-unit-respiratory_mission-routing",
|
||||||
|
component: () => import("@/views/admin/unit/respiratoryMission/RespiratoryMissionRouting.vue"),
|
||||||
|
beforeEnter: [setRespiratoryMissionId],
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "overview",
|
||||||
|
name: "admin-unit-respiratory_mission-overview",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "wearer",
|
||||||
|
name: "admin-unit-respiratory_mission-wearer",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "gear",
|
||||||
|
name: "admin-unit-respiratory_mission-gear",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
name: "admin-unit-respiratory_mission-edit",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "respiratory_mission" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "damage-report",
|
||||||
|
name: "admin-unit-damage_report-route",
|
||||||
|
component: () => import("@/views/admin/unit/damageReport/DamageReportRouting.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "damage_report" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-damage_report",
|
||||||
|
component: () => import("@/views/admin/unit/damageReport/DamageReport.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "done",
|
||||||
|
name: "admin-unit-damage_report-done",
|
||||||
|
component: () => import("@/views/admin/unit/damageReport/DamageReport.vue"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "equipment-type",
|
||||||
|
name: "admin-unit-equipment_type-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "equipment_type" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-equipment_type",
|
||||||
|
component: () => import("@/views/admin/unit/equipmentType/EquipmentType.vue"),
|
||||||
|
beforeEnter: [resetEquipmentTypeStores],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":equipmentTypeId",
|
||||||
|
name: "admin-unit-equipment_type-routing",
|
||||||
|
component: () => import("@/views/admin/unit/equipmentType/EquipmentTypeRouting.vue"),
|
||||||
|
beforeEnter: [setEquipmentTypeId],
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "overview",
|
||||||
|
name: "admin-unit-equipment_type-overview",
|
||||||
|
component: () => import("@/views/admin/unit/equipmentType/Overview.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "inspection-plan",
|
||||||
|
name: "admin-unit-equipment_type-inspection_plan",
|
||||||
|
component: () => import("@/views/admin/unit/equipmentType/InspectionPlans.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
name: "admin-unit-equipment_type-edit",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "equipment_type" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "vehicle-type",
|
||||||
|
name: "admin-unit-vehicle_type-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "vehicle_type" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-vehicle_type",
|
||||||
|
component: () => import("@/views/admin/unit/vehicleType/VehicleType.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":vehicleTypeId",
|
||||||
|
name: "admin-unit-vehicle_type-routing",
|
||||||
|
component: () => import("@/views/admin/unit/vehicleType/VehicleTypeRouting.vue"),
|
||||||
|
beforeEnter: [setEquipmentTypeId],
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "overview",
|
||||||
|
name: "admin-unit-vehicle_type-overview",
|
||||||
|
component: () => import("@/views/admin/unit/vehicleType/Overview.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "inspection-plan",
|
||||||
|
name: "admin-unit-vehicle_type-inspection_plan",
|
||||||
|
component: () => import("@/views/admin/unit/vehicleType/InspectionPlans.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
name: "admin-unit-vehicle_type-edit",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "equipment_type" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "wearable-type",
|
||||||
|
name: "admin-unit-wearable_type-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "wearable_type" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-wearable_type",
|
||||||
|
component: () => import("@/views/admin/unit/wearableType/WearableType.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":wearableTypeId/edit",
|
||||||
|
name: "admin-unit-wearable_type-edit",
|
||||||
|
component: () => import("@/views/admin/unit/wearableType/UpdateWearableType.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "wearable_type" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "inspection-plan",
|
||||||
|
name: "admin-unit-inspection_plan-route",
|
||||||
|
component: () => import("@/views/RouterView.vue"),
|
||||||
|
meta: { type: "read", section: "unit", module: "inspection_plan" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-inspection_plan",
|
||||||
|
component: () => import("@/views/admin/unit/inspectionPlan/InspectionPlan.vue"),
|
||||||
|
beforeEnter: [resetInspectionPlanStores],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "create",
|
||||||
|
name: "admin-unit-inspection_plan-create",
|
||||||
|
component: () => import("@/views/admin/unit/inspectionPlan/CreateInspectionPlan.vue"),
|
||||||
|
meta: { type: "create", section: "unit", module: "equipment" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ":inspectionPlanId",
|
||||||
|
name: "admin-unit-inspection_plan-routing",
|
||||||
|
component: () => import("@/views/admin/unit/inspectionPlan/InspectionPlanRouting.vue"),
|
||||||
|
beforeEnter: [setInspectionPlanId],
|
||||||
|
props: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "admin-unit-inspection_plan-overview",
|
||||||
|
component: () => import("@/views/admin/unit/inspectionPlan/Overview.vue"),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "edit",
|
||||||
|
name: "admin-unit-inspection_plan-edit",
|
||||||
|
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||||
|
meta: { type: "update", section: "unit", module: "inspection_plan" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "configuration",
|
path: "configuration",
|
||||||
name: "admin-configuration",
|
name: "admin-configuration",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { useBackupStore } from "../stores/admin/management/backup";
|
import { useBackupStore } from "@/stores/admin/management/backup";
|
||||||
|
|
||||||
export async function setBackupPage(to: any, from: any, next: any) {
|
export async function setBackupPage(to: any, from: any, next: any) {
|
||||||
const backup = useBackupStore();
|
const backup = useBackupStore();
|
20
src/router/unit/equipment.ts
Normal file
20
src/router/unit/equipment.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
|
||||||
|
export async function setEquipmentId(to: any, from: any, next: any) {
|
||||||
|
const EquipmentStore = useEquipmentStore();
|
||||||
|
EquipmentStore.activeEquipment = to.params?.equipmentId ?? null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetEquipmentStores(to: any, from: any, next: any) {
|
||||||
|
const EquipmentStore = useEquipmentStore();
|
||||||
|
EquipmentStore.activeEquipment = null;
|
||||||
|
EquipmentStore.activeEquipmentObj = null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
20
src/router/unit/equipmentType.ts
Normal file
20
src/router/unit/equipmentType.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useEquipmentTypeStore } from "@/stores/admin/unit/equipmentType/equipmentType";
|
||||||
|
|
||||||
|
export async function setEquipmentTypeId(to: any, from: any, next: any) {
|
||||||
|
const equipmentTypeStore = useEquipmentTypeStore();
|
||||||
|
equipmentTypeStore.activeEquipmentType = to.params?.equipmentTypeId ?? null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetEquipmentTypeStores(to: any, from: any, next: any) {
|
||||||
|
const equipmentTypeStore = useEquipmentTypeStore();
|
||||||
|
equipmentTypeStore.activeEquipmentType = null;
|
||||||
|
equipmentTypeStore.activeEquipmentTypeObj = null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
20
src/router/unit/inspectionPlan.ts
Normal file
20
src/router/unit/inspectionPlan.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useInspectionPlanStore } from "@/stores/admin/unit/inspectionPlan/inspectionPlan";
|
||||||
|
|
||||||
|
export async function setInspectionPlanId(to: any, from: any, next: any) {
|
||||||
|
const InspectionPlanStore = useInspectionPlanStore();
|
||||||
|
InspectionPlanStore.activeInspectionPlan = to.params?.inspectionPlanId ?? null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetInspectionPlanStores(to: any, from: any, next: any) {
|
||||||
|
const InspectionPlanStore = useInspectionPlanStore();
|
||||||
|
InspectionPlanStore.activeInspectionPlan = null;
|
||||||
|
InspectionPlanStore.activeInspectionPlanObj = null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
20
src/router/unit/respiratoryGear.ts
Normal file
20
src/router/unit/respiratoryGear.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useRespiratoryGearStore } from "@/stores/admin/unit/respiratoryGear/respiratoryGear";
|
||||||
|
|
||||||
|
export async function setRespiratoryGearId(to: any, from: any, next: any) {
|
||||||
|
const RespiratoryGearStore = useRespiratoryGearStore();
|
||||||
|
RespiratoryGearStore.activeRespiratoryGear = to.params?.respiratoryGearId ?? null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetRespiratoryGearStores(to: any, from: any, next: any) {
|
||||||
|
const RespiratoryGearStore = useRespiratoryGearStore();
|
||||||
|
RespiratoryGearStore.activeRespiratoryGear = null;
|
||||||
|
RespiratoryGearStore.activeRespiratoryGearObj = null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
20
src/router/unit/respiratoryMission.ts
Normal file
20
src/router/unit/respiratoryMission.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useRespiratoryMissionStore } from "@/stores/admin/unit/respiratoryMission/respiratoryMission";
|
||||||
|
|
||||||
|
export async function setRespiratoryMissionId(to: any, from: any, next: any) {
|
||||||
|
const RespiratoryMissionStore = useRespiratoryMissionStore();
|
||||||
|
RespiratoryMissionStore.activeRespiratoryMission = to.params?.respiratoryMissionId ?? null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetRespiratoryMissionStores(to: any, from: any, next: any) {
|
||||||
|
const RespiratoryMissionStore = useRespiratoryMissionStore();
|
||||||
|
RespiratoryMissionStore.activeRespiratoryMission = null;
|
||||||
|
RespiratoryMissionStore.activeRespiratoryMissionObj = null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
20
src/router/unit/respiratoryWearer.ts
Normal file
20
src/router/unit/respiratoryWearer.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useRespiratoryWearerStore } from "@/stores/admin/unit/respiratoryWearer/respiratoryWearer";
|
||||||
|
|
||||||
|
export async function setRespiratoryWearerId(to: any, from: any, next: any) {
|
||||||
|
const RespiratoryWearerStore = useRespiratoryWearerStore();
|
||||||
|
RespiratoryWearerStore.activeRespiratoryWearer = to.params?.respiratoryWearerId ?? null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetRespiratoryWearerStores(to: any, from: any, next: any) {
|
||||||
|
const RespiratoryWearerStore = useRespiratoryWearerStore();
|
||||||
|
RespiratoryWearerStore.activeRespiratoryWearer = null;
|
||||||
|
RespiratoryWearerStore.activeRespiratoryWearerObj = null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
20
src/router/unit/vehicle.ts
Normal file
20
src/router/unit/vehicle.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useVehicleStore } from "@/stores/admin/unit/vehicle/vehicle";
|
||||||
|
|
||||||
|
export async function setVehicleId(to: any, from: any, next: any) {
|
||||||
|
const VehicleStore = useVehicleStore();
|
||||||
|
VehicleStore.activeVehicle = to.params?.vehicleId ?? null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetVehicleStores(to: any, from: any, next: any) {
|
||||||
|
const VehicleStore = useVehicleStore();
|
||||||
|
VehicleStore.activeVehicle = null;
|
||||||
|
VehicleStore.activeVehicleObj = null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
20
src/router/unit/vehicleType.ts
Normal file
20
src/router/unit/vehicleType.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useVehicleTypeStore } from "@/stores/admin/unit/vehicleType/vehicleType";
|
||||||
|
|
||||||
|
export async function setVehicleTypeId(to: any, from: any, next: any) {
|
||||||
|
const vehicleTypeStore = useVehicleTypeStore();
|
||||||
|
vehicleTypeStore.activeVehicleType = to.params?.vehicleTypeId ?? null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetVehicleTypeStores(to: any, from: any, next: any) {
|
||||||
|
const vehicleTypeStore = useVehicleTypeStore();
|
||||||
|
vehicleTypeStore.activeVehicleType = null;
|
||||||
|
vehicleTypeStore.activeVehicleTypeObj = null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
20
src/router/unit/wearable.ts
Normal file
20
src/router/unit/wearable.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { useWearableStore } from "@/stores/admin/unit/wearable/wearable";
|
||||||
|
|
||||||
|
export async function setWearableId(to: any, from: any, next: any) {
|
||||||
|
const WearableStore = useWearableStore();
|
||||||
|
WearableStore.activeWearable = to.params?.wearableId ?? null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resetWearableStores(to: any, from: any, next: any) {
|
||||||
|
const WearableStore = useWearableStore();
|
||||||
|
WearableStore.activeWearable = null;
|
||||||
|
WearableStore.activeWearableObj = null;
|
||||||
|
|
||||||
|
//useXYStore().$reset();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
|
@ -57,6 +57,15 @@ export const useNavigationStore = defineStore("navigation", {
|
||||||
} as topLevelNavigationModel,
|
} as topLevelNavigationModel,
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
...(abilityStore.canSection("read", "unit")
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
key: "unit",
|
||||||
|
title: "Wehr",
|
||||||
|
levelDefault: "equipment",
|
||||||
|
} as topLevelNavigationModel,
|
||||||
|
]
|
||||||
|
: []),
|
||||||
...(abilityStore.canSection("read", "configuration")
|
...(abilityStore.canSection("read", "configuration")
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
|
@ -95,8 +104,41 @@ export const useNavigationStore = defineStore("navigation", {
|
||||||
...(abilityStore.can("read", "club", "listprint") ? [{ key: "listprint", title: "Liste Drucken" }] : []),
|
...(abilityStore.can("read", "club", "listprint") ? [{ key: "listprint", title: "Liste Drucken" }] : []),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
unit: {
|
||||||
|
mainTitle: "Wehr",
|
||||||
|
main: [
|
||||||
|
...(abilityStore.can("read", "unit", "equipment") ? [{ key: "equipment", title: "Gerätschaften" }] : []),
|
||||||
|
...(abilityStore.can("read", "unit", "vehicle") ? [{ key: "vehicle", title: "Fahrzeuge" }] : []),
|
||||||
|
...(abilityStore.can("read", "unit", "wearable") ? [{ key: "wearable", title: "Kleidung" }] : []),
|
||||||
|
...(abilityStore.can("read", "unit", "respiratory_gear")
|
||||||
|
? [{ key: "respiratory_gear", title: "Atemschutz-Geräte" }]
|
||||||
|
: []),
|
||||||
|
...(abilityStore.can("read", "unit", "respiratory_wearer")
|
||||||
|
? [{ key: "respiratory_wearer", title: "Atemschutz-Träger" }]
|
||||||
|
: []),
|
||||||
|
...(abilityStore.can("read", "unit", "respiratory_mission")
|
||||||
|
? [{ key: "respiratory_mission", title: "Atemschutz-Einsätze" }]
|
||||||
|
: []),
|
||||||
|
...(abilityStore.can("read", "unit", "damage_report")
|
||||||
|
? [{ key: "damage_report", title: "Schadensmeldungen" }]
|
||||||
|
: []),
|
||||||
|
{ key: "divider1", title: "Basisdaten" },
|
||||||
|
...(abilityStore.can("read", "unit", "equipment_type")
|
||||||
|
? [{ key: "equipment_type", title: "Geräte-Typen" }]
|
||||||
|
: []),
|
||||||
|
...(abilityStore.can("read", "unit", "vehicle_type")
|
||||||
|
? [{ key: "vehicle_type", title: "Fahrzeug-Arten" }]
|
||||||
|
: []),
|
||||||
|
...(abilityStore.can("read", "unit", "wearable_type")
|
||||||
|
? [{ key: "wearable_type", title: "Kleidungs-Arten" }]
|
||||||
|
: []),
|
||||||
|
...(abilityStore.can("read", "unit", "inspection_plan")
|
||||||
|
? [{ key: "inspection_plan", title: "Prüfpläne" }]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
},
|
||||||
configuration: {
|
configuration: {
|
||||||
mainTitle: "Einstellungen",
|
mainTitle: "Konfiguration",
|
||||||
main: [
|
main: [
|
||||||
{ key: "divider1", title: "Mitgliederdaten" },
|
{ key: "divider1", title: "Mitgliederdaten" },
|
||||||
...(abilityStore.can("read", "configuration", "salutation")
|
...(abilityStore.can("read", "configuration", "salutation")
|
||||||
|
|
91
src/stores/admin/unit/damageReport/damageReport.ts
Normal file
91
src/stores/admin/unit/damageReport/damageReport.ts
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
DamageReportViewModel,
|
||||||
|
CreateDamageReportViewModel,
|
||||||
|
UpdateDamageReportViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/damageReport/damageReport.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import type { VehicleViewModel } from "../../../../viewmodels/admin/unit/vehicle/vehicle.models";
|
||||||
|
import { damageReportDemoData } from "../../../../demodata/damageReport";
|
||||||
|
|
||||||
|
export const useDamageReportStore = defineStore("damageReport", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
damageReports: [] as Array<DamageReportViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchDamageReports(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.damageReports = damageReportDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.damageReports.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.damageReports = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/damageReport?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.damageReports
|
||||||
|
.filter((elem: DamageReportViewModel) => this.damageReports.findIndex((m) => m.id == elem.id) == -1)
|
||||||
|
.map((elem: DamageReportViewModel, index: number): DamageReportViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((elem: DamageReportViewModel & { tab_pos: number }) => {
|
||||||
|
this.damageReports.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllDamageReports(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/damageReport?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.damageReports };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getDamageReportsByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http
|
||||||
|
.post(`/admin/damageReport/ids`, {
|
||||||
|
ids,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return { ...res, data: res.data.damageReports };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchDamageReports(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/damageReport?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.damageReports };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchDamageReportById(id: string) {
|
||||||
|
return http.get(`/admin/damageReport/${id}`);
|
||||||
|
},
|
||||||
|
async createDamageReport(damageReport: CreateDamageReportViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/damageReport`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchDamageReports();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateDamageReport(damageReport: UpdateDamageReportViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/damageReport/${damageReport.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchDamageReports();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteDamageReport(damageReport: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/damageReport/${damageReport}`);
|
||||||
|
this.fetchDamageReports();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
108
src/stores/admin/unit/equipment/equipment.ts
Normal file
108
src/stores/admin/unit/equipment/equipment.ts
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
EquipmentViewModel,
|
||||||
|
CreateEquipmentViewModel,
|
||||||
|
UpdateEquipmentViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/equipment/equipment.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { equipmentDemoData } from "../../../../demodata/equipment";
|
||||||
|
|
||||||
|
export const useEquipmentStore = defineStore("equipment", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
equipments: [] as Array<EquipmentViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
activeEquipment: null as string | null,
|
||||||
|
activeEquipmentObj: null as EquipmentViewModel | null,
|
||||||
|
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchEquipments(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.equipments = equipmentDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.equipments.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.equipments = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/equipment?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.equipments
|
||||||
|
.filter((elem: EquipmentViewModel) => this.equipments.findIndex((m) => m.id == elem.id) == -1)
|
||||||
|
.map((elem: EquipmentViewModel, index: number): EquipmentViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((elem: EquipmentViewModel & { tab_pos: number }) => {
|
||||||
|
this.equipments.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllEquipments(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/equipment?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.equipments };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getEquipmentsByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http
|
||||||
|
.post(`/admin/equipment/ids`, {
|
||||||
|
ids,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return { ...res, data: res.data.equipments };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchEquipments(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/equipment?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.equipments };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchEquipmentByActiveId() {
|
||||||
|
this.activeEquipmentObj = equipmentDemoData.find((e) => e.id == this.activeEquipment) as EquipmentViewModel;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
this.loadingActive = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/equipment/${this.activeEquipment}`)
|
||||||
|
.then((res) => {
|
||||||
|
this.activeEquipmentObj = res.data;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loadingActive = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchEquipmentById(id: string) {
|
||||||
|
return http.get(`/admin/equipment/${id}`);
|
||||||
|
},
|
||||||
|
async createEquipment(equipment: CreateEquipmentViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/equipment`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchEquipments();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveEquipment(equipment: UpdateEquipmentViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/equipment/${equipment.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchEquipments();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteEquipment(equipment: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/equipment/${equipment}`);
|
||||||
|
this.fetchEquipments();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
101
src/stores/admin/unit/equipmentType/equipmentType.ts
Normal file
101
src/stores/admin/unit/equipmentType/equipmentType.ts
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
EquipmentTypeViewModel,
|
||||||
|
CreateEquipmentTypeViewModel,
|
||||||
|
UpdateEquipmentTypeViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/equipmentType/equipmentType.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { equipmentTypeDemoData } from "../../../../demodata/equipmentType";
|
||||||
|
|
||||||
|
export const useEquipmentTypeStore = defineStore("equipmentType", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
equipmentTypes: [] as Array<EquipmentTypeViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
activeEquipmentType: null as string | null,
|
||||||
|
activeEquipmentTypeObj: null as EquipmentTypeViewModel | null,
|
||||||
|
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchEquipmentTypes(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.equipmentTypes = equipmentTypeDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.equipmentTypes.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.equipmentTypes = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/equipmentType?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.equipments
|
||||||
|
.filter((elem: EquipmentTypeViewModel) => this.equipmentTypes.findIndex((m) => m.id == elem.id) == -1)
|
||||||
|
.map((elem: EquipmentTypeViewModel, index: number): EquipmentTypeViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((elem: EquipmentTypeViewModel & { tab_pos: number }) => {
|
||||||
|
this.equipmentTypes.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllEquipmentTypes(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/equipmentType?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.equipments };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchEquipmentTypes(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/equipmentType?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.equipments };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchEquipmentTypeByActiveId() {
|
||||||
|
this.activeEquipmentTypeObj = equipmentTypeDemoData.find(
|
||||||
|
(e) => e.id == this.activeEquipmentType
|
||||||
|
) as EquipmentTypeViewModel;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
return;
|
||||||
|
this.loadingActive = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/equipmentType/${this.activeEquipmentType}`)
|
||||||
|
.then((res) => {
|
||||||
|
this.activeEquipmentTypeObj = res.data;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loadingActive = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchEquipmentTypeById(id: string) {
|
||||||
|
return http.get(`/admin/equipmentType/${id}`);
|
||||||
|
},
|
||||||
|
async createEquipmentType(equipmentType: CreateEquipmentTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/equipmentType`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchEquipmentTypes();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveEquipmentType(equipmentType: UpdateEquipmentTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/equipmentType/${equipmentType.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchEquipmentTypes();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteEquipmentType(equipmentType: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/equipmentType/${equipmentType}`);
|
||||||
|
this.fetchEquipmentTypes();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
110
src/stores/admin/unit/inspectionPlan/inspectionPlan.ts
Normal file
110
src/stores/admin/unit/inspectionPlan/inspectionPlan.ts
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
InspectionPlanViewModel,
|
||||||
|
CreateInspectionPlanViewModel,
|
||||||
|
UpdateInspectionPlanViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { inspectionPlanDemoData } from "@/demodata/inspectionPlan";
|
||||||
|
|
||||||
|
export const useInspectionPlanStore = defineStore("inspectionPlan", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
inspectionPlans: [] as Array<InspectionPlanViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
activeInspectionPlan: null as string | null,
|
||||||
|
activeInspectionPlanObj: null as InspectionPlanViewModel | null,
|
||||||
|
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchInspectionPlans(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.inspectionPlans = inspectionPlanDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.inspectionPlans.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.inspectionPlans = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/inspectionPlan?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.inspectionPlans
|
||||||
|
.filter((elem: InspectionPlanViewModel) => this.inspectionPlans.findIndex((m) => m.id == elem.id) == -1)
|
||||||
|
.map((elem: InspectionPlanViewModel, index: number): InspectionPlanViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((elem: InspectionPlanViewModel & { tab_pos: number }) => {
|
||||||
|
this.inspectionPlans.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllInspectionPlans(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/inspectionPlan?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.inspectionPlans };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getInspectionPlansByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http
|
||||||
|
.post(`/admin/inspectionPlan/ids`, {
|
||||||
|
ids,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return { ...res, data: res.data.inspectionPlans };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchInspectionPlans(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/inspectionPlan?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.inspectionPlans };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchInspectionPlanByActiveId() {
|
||||||
|
this.activeInspectionPlanObj = inspectionPlanDemoData.find(
|
||||||
|
(e) => e.id == this.activeInspectionPlan
|
||||||
|
) as InspectionPlanViewModel;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
this.loadingActive = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/inspectionPlan/${this.activeInspectionPlan}`)
|
||||||
|
.then((res) => {
|
||||||
|
this.activeInspectionPlanObj = res.data;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loadingActive = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchInspectionPlanById(id: string) {
|
||||||
|
return http.get(`/admin/inspectionPlan/${id}`);
|
||||||
|
},
|
||||||
|
async createInspectionPlan(inspectionPlan: CreateInspectionPlanViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/inspectionPlan`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchInspectionPlans();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveInspectionPlan(inspectionPlan: UpdateInspectionPlanViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/inspectionPlan/${inspectionPlan.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchInspectionPlans();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteInspectionPlan(inspectionPlan: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/inspectionPlan/${inspectionPlan}`);
|
||||||
|
this.fetchInspectionPlans();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
103
src/stores/admin/unit/respiratoryGear/respiratoryGear.ts
Normal file
103
src/stores/admin/unit/respiratoryGear/respiratoryGear.ts
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
RespiratoryGearViewModel,
|
||||||
|
CreateRespiratoryGearViewModel,
|
||||||
|
UpdateRespiratoryGearViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { respiratoryGearDemoData } from "../../../../demodata/respiratoryGear";
|
||||||
|
|
||||||
|
export const useRespiratoryGearStore = defineStore("respiratoryGear", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
respiratoryGears: [] as Array<RespiratoryGearViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
activeRespiratoryGear: null as string | null,
|
||||||
|
activeRespiratoryGearObj: null as RespiratoryGearViewModel | null,
|
||||||
|
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchRespiratoryGears(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.respiratoryGears = respiratoryGearDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.respiratoryGears.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.respiratoryGears = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/respiratoryGear?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.respiratoryGears
|
||||||
|
.filter((elem: RespiratoryGearViewModel) => this.respiratoryGears.findIndex((m) => m.id == elem.id) == -1)
|
||||||
|
.map((elem: RespiratoryGearViewModel, index: number): RespiratoryGearViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((elem: RespiratoryGearViewModel & { tab_pos: number }) => {
|
||||||
|
this.respiratoryGears.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllRespiratoryGears(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/respiratoryGear?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.respiratoryGears };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchRespiratoryGears(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/respiratoryGear?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.respiratoryGears };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchRespiratoryGearByActiveId() {
|
||||||
|
this.activeRespiratoryGearObj = respiratoryGearDemoData.find(
|
||||||
|
(e) => e.id == this.activeRespiratoryGear
|
||||||
|
) as RespiratoryGearViewModel;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
this.loadingActive = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/respiratoryGear/${this.activeRespiratoryGear}`)
|
||||||
|
.then((res) => {
|
||||||
|
this.activeRespiratoryGearObj = res.data;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loadingActive = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchRespiratoryGearById(id: string) {
|
||||||
|
return http.get(`/admin/respiratoryGear/${id}`);
|
||||||
|
},
|
||||||
|
async createRespiratoryGear(respiratoryGear: CreateRespiratoryGearViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/respiratoryGear`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchRespiratoryGears();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveRespiratoryGear(
|
||||||
|
respiratoryGear: UpdateRespiratoryGearViewModel
|
||||||
|
): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/respiratoryGear/${respiratoryGear.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchRespiratoryGears();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteRespiratoryGear(respiratoryGear: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/respiratoryGear/${respiratoryGear}`);
|
||||||
|
this.fetchRespiratoryGears();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
109
src/stores/admin/unit/respiratoryMission/respiratoryMission.ts
Normal file
109
src/stores/admin/unit/respiratoryMission/respiratoryMission.ts
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
RespiratoryMissionViewModel,
|
||||||
|
CreateRespiratoryMissionViewModel,
|
||||||
|
UpdateRespiratoryMissionViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { respiratoryMissionDemoData } from "../../../../demodata/respiratoryMission";
|
||||||
|
|
||||||
|
export const useRespiratoryMissionStore = defineStore("respiratoryMission", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
respiratoryMissions: [] as Array<RespiratoryMissionViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
activeRespiratoryMission: null as string | null,
|
||||||
|
activeRespiratoryMissionObj: null as RespiratoryMissionViewModel | null,
|
||||||
|
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchRespiratoryMissions(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.respiratoryMissions = respiratoryMissionDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.respiratoryMissions.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.respiratoryMissions = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/respiratoryMission?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.respiratoryMissions
|
||||||
|
.filter(
|
||||||
|
(elem: RespiratoryMissionViewModel) => this.respiratoryMissions.findIndex((m) => m.id == elem.id) == -1
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
(elem: RespiratoryMissionViewModel, index: number): RespiratoryMissionViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.forEach((elem: RespiratoryMissionViewModel & { tab_pos: number }) => {
|
||||||
|
this.respiratoryMissions.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllRespiratoryMissions(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/respiratoryMission?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.respiratoryMissions };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchRespiratoryMissions(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/respiratoryMission?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.respiratoryMissions };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchRespiratoryMissionByActiveId() {
|
||||||
|
this.activeRespiratoryMissionObj = this.respiratoryMissions.find(
|
||||||
|
(e) => e.id == this.activeRespiratoryMission
|
||||||
|
) as RespiratoryMissionViewModel;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
this.loadingActive = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/respiratoryMission/${this.activeRespiratoryMission}`)
|
||||||
|
.then((res) => {
|
||||||
|
this.activeRespiratoryMissionObj = res.data;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loadingActive = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchRespiratoryMissionById(id: string) {
|
||||||
|
return http.get(`/admin/respiratoryMission/${id}`);
|
||||||
|
},
|
||||||
|
async createRespiratoryMission(
|
||||||
|
respiratoryMission: CreateRespiratoryMissionViewModel
|
||||||
|
): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/respiratoryMission`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchRespiratoryMissions();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveRespiratoryMission(
|
||||||
|
respiratoryMission: UpdateRespiratoryMissionViewModel
|
||||||
|
): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/respiratoryMission/${respiratoryMission.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchRespiratoryMissions();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteRespiratoryMission(respiratoryMission: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/respiratoryMission/${respiratoryMission}`);
|
||||||
|
this.fetchRespiratoryMissions();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
109
src/stores/admin/unit/respiratoryWearer/respiratoryWearer.ts
Normal file
109
src/stores/admin/unit/respiratoryWearer/respiratoryWearer.ts
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
RespiratoryWearerViewModel,
|
||||||
|
CreateRespiratoryWearerViewModel,
|
||||||
|
UpdateRespiratoryWearerViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { respiratoryWearerDemoData } from "../../../../demodata/respiratoryWearer";
|
||||||
|
|
||||||
|
export const useRespiratoryWearerStore = defineStore("respiratoryWearer", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
respiratoryWearers: [] as Array<RespiratoryWearerViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
activeRespiratoryWearer: null as string | null,
|
||||||
|
activeRespiratoryWearerObj: null as RespiratoryWearerViewModel | null,
|
||||||
|
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchRespiratoryWearers(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.respiratoryWearers = respiratoryWearerDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.respiratoryWearers.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.respiratoryWearers = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/respiratoryWearer?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.respiratoryWearers
|
||||||
|
.filter(
|
||||||
|
(elem: RespiratoryWearerViewModel) => this.respiratoryWearers.findIndex((m) => m.id == elem.id) == -1
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
(elem: RespiratoryWearerViewModel, index: number): RespiratoryWearerViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.forEach((elem: RespiratoryWearerViewModel & { tab_pos: number }) => {
|
||||||
|
this.respiratoryWearers.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllRespiratoryWearers(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/respiratoryWearer?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.respiratoryWearers };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchRespiratoryWearers(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/respiratoryWearer?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.respiratoryWearers };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchRespiratoryWearerByActiveId() {
|
||||||
|
this.activeRespiratoryWearerObj = respiratoryWearerDemoData.find(
|
||||||
|
(e) => e.id == this.activeRespiratoryWearer
|
||||||
|
) as RespiratoryWearerViewModel;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
this.loadingActive = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/respiratoryWearer/${this.activeRespiratoryWearer}`)
|
||||||
|
.then((res) => {
|
||||||
|
this.activeRespiratoryWearerObj = res.data;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loadingActive = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchRespiratoryWearerById(id: string) {
|
||||||
|
return http.get(`/admin/respiratoryWearer/${id}`);
|
||||||
|
},
|
||||||
|
async createRespiratoryWearer(
|
||||||
|
respiratoryWearer: CreateRespiratoryWearerViewModel
|
||||||
|
): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/respiratoryWearer`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchRespiratoryWearers();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveRespiratoryWearer(
|
||||||
|
respiratoryWearer: UpdateRespiratoryWearerViewModel
|
||||||
|
): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/respiratoryWearer/${respiratoryWearer.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchRespiratoryWearers();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteRespiratoryWearer(respiratoryWearer: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/respiratoryWearer/${respiratoryWearer}`);
|
||||||
|
this.fetchRespiratoryWearers();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
108
src/stores/admin/unit/vehicle/vehicle.ts
Normal file
108
src/stores/admin/unit/vehicle/vehicle.ts
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
VehicleViewModel,
|
||||||
|
CreateVehicleViewModel,
|
||||||
|
UpdateVehicleViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/vehicle/vehicle.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { vehicleDemoData } from "../../../../demodata/vehicle";
|
||||||
|
|
||||||
|
export const useVehicleStore = defineStore("vehicle", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
vehicles: [] as Array<VehicleViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
activeVehicle: null as string | null,
|
||||||
|
activeVehicleObj: null as VehicleViewModel | null,
|
||||||
|
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchVehicles(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.vehicles = vehicleDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.vehicles.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.vehicles = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/vehicle?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.vehicles
|
||||||
|
.filter((elem: VehicleViewModel) => this.vehicles.findIndex((m) => m.id == elem.id) == -1)
|
||||||
|
.map((elem: VehicleViewModel, index: number): VehicleViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((elem: VehicleViewModel & { tab_pos: number }) => {
|
||||||
|
this.vehicles.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllVehicles(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/vehicle?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.vehicles };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getVehiclesByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http
|
||||||
|
.post(`/admin/vehicle/ids`, {
|
||||||
|
ids,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return { ...res, data: res.data.vehicles };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchVehicles(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/vehicle?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.vehicles };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchVehicleByActiveId() {
|
||||||
|
this.activeVehicleObj = vehicleDemoData.find((e) => e.id == this.activeVehicle) as VehicleViewModel;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
this.loadingActive = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/vehicle/${this.activeVehicle}`)
|
||||||
|
.then((res) => {
|
||||||
|
this.activeVehicleObj = res.data;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loadingActive = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchVehicleById(id: string) {
|
||||||
|
return http.get(`/admin/vehicle/${id}`);
|
||||||
|
},
|
||||||
|
async createVehicle(vehicle: CreateVehicleViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/vehicle`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchVehicles();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveVehicle(vehicle: UpdateVehicleViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/vehicle/${vehicle.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchVehicles();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteVehicle(vehicle: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/vehicle/${vehicle}`);
|
||||||
|
this.fetchVehicles();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
101
src/stores/admin/unit/vehicleType/vehicleType.ts
Normal file
101
src/stores/admin/unit/vehicleType/vehicleType.ts
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
VehicleTypeViewModel,
|
||||||
|
CreateVehicleTypeViewModel,
|
||||||
|
UpdateVehicleTypeViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/vehicleType/vehicleType.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { vehicleTypeDemoData } from "../../../../demodata/vehicleType";
|
||||||
|
|
||||||
|
export const useVehicleTypeStore = defineStore("vehicleType", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
vehicleTypes: [] as Array<VehicleTypeViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
activeVehicleType: null as string | null,
|
||||||
|
activeVehicleTypeObj: null as VehicleTypeViewModel | null,
|
||||||
|
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchVehicleTypes(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.vehicleTypes = vehicleTypeDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.vehicleTypes.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.vehicleTypes = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/vehicleType?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.vehicles
|
||||||
|
.filter((elem: VehicleTypeViewModel) => this.vehicleTypes.findIndex((m) => m.id == elem.id) == -1)
|
||||||
|
.map((elem: VehicleTypeViewModel, index: number): VehicleTypeViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((elem: VehicleTypeViewModel & { tab_pos: number }) => {
|
||||||
|
this.vehicleTypes.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllVehicleTypes(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/vehicleType?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.vehicles };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchVehicleTypes(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/vehicleType?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.vehicles };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchVehicleTypeByActiveId() {
|
||||||
|
this.activeVehicleTypeObj = vehicleTypeDemoData.find(
|
||||||
|
(e) => e.id == this.activeVehicleType
|
||||||
|
) as VehicleTypeViewModel;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
return;
|
||||||
|
this.loadingActive = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/vehicleType/${this.activeVehicleType}`)
|
||||||
|
.then((res) => {
|
||||||
|
this.activeVehicleTypeObj = res.data;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loadingActive = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchVehicleTypeById(id: string) {
|
||||||
|
return http.get(`/admin/vehicleType/${id}`);
|
||||||
|
},
|
||||||
|
async createVehicleType(vehicleType: CreateVehicleTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/vehicleType`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchVehicleTypes();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveVehicleType(vehicleType: UpdateVehicleTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/vehicleType/${vehicleType.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchVehicleTypes();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteVehicleType(vehicleType: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/vehicleType/${vehicleType}`);
|
||||||
|
this.fetchVehicleTypes();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
108
src/stores/admin/unit/wearable/wearable.ts
Normal file
108
src/stores/admin/unit/wearable/wearable.ts
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
WearableViewModel,
|
||||||
|
CreateWearableViewModel,
|
||||||
|
UpdateWearableViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/wearable/wearable.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { wearableDemoData } from "../../../../demodata/wearable";
|
||||||
|
|
||||||
|
export const useWearableStore = defineStore("wearable", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
wearables: [] as Array<WearableViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
activeWearable: null as string | null,
|
||||||
|
activeWearableObj: null as WearableViewModel | null,
|
||||||
|
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchWearables(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.wearables = wearableDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.wearables.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.wearables = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/wearable?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.wearables
|
||||||
|
.filter((elem: WearableViewModel) => this.wearables.findIndex((m) => m.id == elem.id) == -1)
|
||||||
|
.map((elem: WearableViewModel, index: number): WearableViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((elem: WearableViewModel & { tab_pos: number }) => {
|
||||||
|
this.wearables.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllWearables(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/wearable?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.wearables };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getWearablesByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http
|
||||||
|
.post(`/admin/wearable/ids`, {
|
||||||
|
ids,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return { ...res, data: res.data.wearables };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchWearables(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/wearable?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.wearables };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchWearableByActiveId() {
|
||||||
|
this.activeWearableObj = wearableDemoData.find((e) => e.id == this.activeWearable) as WearableViewModel;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
this.loadingActive = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/wearable/${this.activeWearable}`)
|
||||||
|
.then((res) => {
|
||||||
|
this.activeWearableObj = res.data;
|
||||||
|
this.loadingActive = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loadingActive = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchWearableById(id: string) {
|
||||||
|
return http.get(`/admin/wearable/${id}`);
|
||||||
|
},
|
||||||
|
async createWearable(wearable: CreateWearableViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/wearable`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchWearables();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveWearable(wearable: UpdateWearableViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/wearable/${wearable.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchWearables();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteWearable(wearable: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/wearable/${wearable}`);
|
||||||
|
this.fetchWearables();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
81
src/stores/admin/unit/wearableType/wearableType.ts
Normal file
81
src/stores/admin/unit/wearableType/wearableType.ts
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type {
|
||||||
|
WearableTypeViewModel,
|
||||||
|
CreateWearableTypeViewModel,
|
||||||
|
UpdateWearableTypeViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/wearableType/wearableType.models";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import { wearableTypeDemoData } from "../../../../demodata/wearableType";
|
||||||
|
|
||||||
|
export const useWearableTypeStore = defineStore("wearableType", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
wearableTypes: [] as Array<WearableTypeViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchWearableTypes(offset = 0, count = 25, search = "", clear = false) {
|
||||||
|
this.wearableTypes = wearableTypeDemoData.map((e, i) => ({ ...e, tab_pos: i }));
|
||||||
|
this.totalCount = this.wearableTypes.length;
|
||||||
|
this.loading = "fetched";
|
||||||
|
return;
|
||||||
|
if (clear) this.wearableTypes = [];
|
||||||
|
this.loading = "loading";
|
||||||
|
http
|
||||||
|
.get(`/admin/wearableType?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||||
|
.then((result) => {
|
||||||
|
this.totalCount = result.data.total;
|
||||||
|
result.data.wearables
|
||||||
|
.filter((elem: WearableTypeViewModel) => this.wearableTypes.findIndex((m) => m.id == elem.id) == -1)
|
||||||
|
.map((elem: WearableTypeViewModel, index: number): WearableTypeViewModel & { tab_pos: number } => {
|
||||||
|
return {
|
||||||
|
...elem,
|
||||||
|
tab_pos: index + offset,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.forEach((elem: WearableTypeViewModel & { tab_pos: number }) => {
|
||||||
|
this.wearableTypes.push(elem);
|
||||||
|
});
|
||||||
|
this.loading = "fetched";
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async getAllWearableTypes(): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/wearableType?noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.wearables };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async searchWearableTypes(search: string): Promise<AxiosResponse<any, any>> {
|
||||||
|
return await http.get(`/admin/wearableType?search=${search}&noLimit=true`).then((res) => {
|
||||||
|
return { ...res, data: res.data.wearables };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchWearableTypeById(id: string) {
|
||||||
|
return http.get(`/admin/wearableType/${id}`);
|
||||||
|
},
|
||||||
|
async createWearableType(wearableType: CreateWearableTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/wearableType`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchWearableTypes();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateWearableType(wearableType: UpdateWearableTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/wearableType/${wearableType.id}`, {
|
||||||
|
// TODO: data
|
||||||
|
});
|
||||||
|
this.fetchWearableTypes();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteWearableType(wearableType: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/wearableType/${wearableType}`);
|
||||||
|
this.fetchWearableTypes();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -4,19 +4,22 @@ export const useModalStore = defineStore("modal", {
|
||||||
state: () => {
|
state: () => {
|
||||||
return {
|
return {
|
||||||
show: false,
|
show: false,
|
||||||
component_ref: null as any,
|
component_ref: undefined as any,
|
||||||
data: null as any,
|
data: undefined as any,
|
||||||
|
callback: undefined as undefined | Function,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
openModal(component_ref: any, data?: any) {
|
openModal(component_ref: any, data?: any, callback?: Function) {
|
||||||
this.component_ref = component_ref;
|
this.component_ref = component_ref;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
this.callback = callback;
|
||||||
this.show = true;
|
this.show = true;
|
||||||
},
|
},
|
||||||
closeModal() {
|
closeModal() {
|
||||||
this.component_ref = null;
|
this.component_ref = undefined;
|
||||||
this.data = null;
|
this.data = undefined;
|
||||||
|
this.callback = undefined;
|
||||||
this.show = false;
|
this.show = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,27 +1,42 @@
|
||||||
export type PermissionSection = "club" | "configuration" | "management";
|
export type PermissionSection = "club" | "unit" | "configuration" | "management";
|
||||||
|
|
||||||
export type PermissionModule =
|
export type PermissionModule =
|
||||||
|
// club
|
||||||
| "member"
|
| "member"
|
||||||
| "calendar"
|
| "calendar"
|
||||||
| "newsletter"
|
| "newsletter"
|
||||||
| "newsletter_config"
|
|
||||||
| "protocol"
|
| "protocol"
|
||||||
|
| "query"
|
||||||
| "listprint"
|
| "listprint"
|
||||||
|
// unit
|
||||||
|
| "equipment"
|
||||||
|
| "equipment_type"
|
||||||
|
| "vehicle"
|
||||||
|
| "vehicle_type"
|
||||||
|
| "inspection_plan"
|
||||||
|
| "respiratory_gear"
|
||||||
|
| "respiratory_wearer"
|
||||||
|
| "respiratory_mission"
|
||||||
|
| "damage_report"
|
||||||
|
| "wearable"
|
||||||
|
| "wearable_type"
|
||||||
|
// configuration
|
||||||
| "qualification"
|
| "qualification"
|
||||||
| "award"
|
| "award"
|
||||||
| "executive_position"
|
| "executive_position"
|
||||||
| "communication_type"
|
| "communication_type"
|
||||||
| "membership_status"
|
| "membership_status"
|
||||||
|
| "newsletter_config"
|
||||||
| "salutation"
|
| "salutation"
|
||||||
| "calendar_type"
|
| "calendar_type"
|
||||||
| "user"
|
|
||||||
| "role"
|
|
||||||
| "webapi"
|
|
||||||
| "query"
|
|
||||||
| "query_store"
|
| "query_store"
|
||||||
| "template"
|
| "template"
|
||||||
| "template_usage"
|
| "template_usage"
|
||||||
| "backup";
|
| "backup"
|
||||||
|
// management
|
||||||
|
| "user"
|
||||||
|
| "role"
|
||||||
|
| "webapi";
|
||||||
|
|
||||||
export type PermissionType = "read" | "create" | "update" | "delete";
|
export type PermissionType = "read" | "create" | "update" | "delete";
|
||||||
|
|
||||||
|
@ -44,33 +59,61 @@ export type SectionsAndModulesObject = {
|
||||||
[section in PermissionSection]: Array<PermissionModule>;
|
[section in PermissionSection]: Array<PermissionModule>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const permissionSections: Array<PermissionSection> = ["club", "configuration", "management"];
|
export const permissionSections: Array<PermissionSection> = ["club", "unit", "configuration", "management"];
|
||||||
export const permissionModules: Array<PermissionModule> = [
|
export const permissionModules: Array<PermissionModule> = [
|
||||||
|
// club
|
||||||
"member",
|
"member",
|
||||||
"calendar",
|
"calendar",
|
||||||
"newsletter",
|
"newsletter",
|
||||||
"newsletter_config",
|
|
||||||
"protocol",
|
"protocol",
|
||||||
|
"query",
|
||||||
"listprint",
|
"listprint",
|
||||||
|
// unit
|
||||||
|
"equipment",
|
||||||
|
"equipment_type",
|
||||||
|
"vehicle",
|
||||||
|
"vehicle_type",
|
||||||
|
"inspection_plan",
|
||||||
|
"respiratory_gear",
|
||||||
|
"respiratory_wearer",
|
||||||
|
"respiratory_mission",
|
||||||
|
"damage_report",
|
||||||
|
"wearable",
|
||||||
|
"wearable_type",
|
||||||
|
// configuration
|
||||||
"qualification",
|
"qualification",
|
||||||
"award",
|
"award",
|
||||||
"executive_position",
|
"executive_position",
|
||||||
"communication_type",
|
"communication_type",
|
||||||
"membership_status",
|
"membership_status",
|
||||||
|
"newsletter_config",
|
||||||
"salutation",
|
"salutation",
|
||||||
"calendar_type",
|
"calendar_type",
|
||||||
"user",
|
|
||||||
"role",
|
|
||||||
"webapi",
|
|
||||||
"query",
|
|
||||||
"query_store",
|
"query_store",
|
||||||
"template",
|
"template",
|
||||||
"template_usage",
|
"template_usage",
|
||||||
"backup",
|
"backup",
|
||||||
|
// management
|
||||||
|
"user",
|
||||||
|
"role",
|
||||||
|
"webapi",
|
||||||
];
|
];
|
||||||
export const permissionTypes: Array<PermissionType> = ["read", "create", "update", "delete"];
|
export const permissionTypes: Array<PermissionType> = ["read", "create", "update", "delete"];
|
||||||
export const sectionsAndModules: SectionsAndModulesObject = {
|
export const sectionsAndModules: SectionsAndModulesObject = {
|
||||||
club: ["member", "calendar", "newsletter", "protocol", "query", "listprint"],
|
club: ["member", "calendar", "newsletter", "protocol", "query", "listprint"],
|
||||||
|
unit: [
|
||||||
|
"equipment",
|
||||||
|
"equipment_type",
|
||||||
|
"vehicle",
|
||||||
|
"vehicle_type",
|
||||||
|
"inspection_plan",
|
||||||
|
"respiratory_gear",
|
||||||
|
"respiratory_wearer",
|
||||||
|
"respiratory_mission",
|
||||||
|
"damage_report",
|
||||||
|
"wearable",
|
||||||
|
"wearable_type",
|
||||||
|
],
|
||||||
configuration: [
|
configuration: [
|
||||||
"qualification",
|
"qualification",
|
||||||
"award",
|
"award",
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import type { EquipmentViewModel } from "../equipment/equipment.models";
|
||||||
|
|
||||||
|
export interface DamageReportViewModel {
|
||||||
|
id: string;
|
||||||
|
reported: Date;
|
||||||
|
status: string;
|
||||||
|
done: boolean;
|
||||||
|
description: string;
|
||||||
|
affectedEquipmentId: string;
|
||||||
|
affectedEquipment: EquipmentViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateDamageReportViewModel {
|
||||||
|
description: string;
|
||||||
|
affectedEquipmentId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateDamageReportViewModel {
|
||||||
|
id: string;
|
||||||
|
status: string;
|
||||||
|
done: boolean;
|
||||||
|
}
|
24
src/viewmodels/admin/unit/equipment/equipment.models.ts
Normal file
24
src/viewmodels/admin/unit/equipment/equipment.models.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import type { EquipmentTypeViewModel } from "../equipmentType/equipmentType.models";
|
||||||
|
|
||||||
|
export interface EquipmentViewModel {
|
||||||
|
id: string;
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
location: string;
|
||||||
|
equipmentTypeId: string;
|
||||||
|
equipmentType: EquipmentTypeViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateEquipmentViewModel {
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
location: string;
|
||||||
|
equipmentTypeId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateEquipmentViewModel {
|
||||||
|
id: string;
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
location: string;
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
export interface EquipmentTypeViewModel {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
// attached inspection plans
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateEquipmentTypeViewModel {
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateEquipmentTypeViewModel {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import type { EquipmentTypeViewModel } from "../equipmentType/equipmentType.models";
|
||||||
|
|
||||||
|
export interface InspectionPlanViewModel {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
inspectionInterval?: `${number}-${"d" | "m" | "y"}` | `${number}/${number}`;
|
||||||
|
equipmentTypeId: string;
|
||||||
|
equipmentType: EquipmentTypeViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateInspectionPlanViewModel {
|
||||||
|
title: string;
|
||||||
|
inspectionInterval?: `${number}-${"d" | "m" | "y"}` | `${number}/${number}`;
|
||||||
|
equipmentTypeId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateInspectionPlanViewModel {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
inspectionInterval?: `${number}-${"d" | "m" | "y"}` | `${number}/${number}`;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import type { EquipmentViewModel } from "../equipment/equipment.models";
|
||||||
|
|
||||||
|
export interface RespiratoryGearViewModel {
|
||||||
|
id: string;
|
||||||
|
equipmentId: string;
|
||||||
|
equipment: EquipmentViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateRespiratoryGearViewModel {
|
||||||
|
equipmentId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateRespiratoryGearViewModel {
|
||||||
|
id: string;
|
||||||
|
equipmentId: string;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
export interface RespiratoryMissionViewModel {
|
||||||
|
id: string;
|
||||||
|
date: Date;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
// refs to used respiratory gear and wearers
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateRespiratoryMissionViewModel {
|
||||||
|
date: Date;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateRespiratoryMissionViewModel {
|
||||||
|
id: string;
|
||||||
|
date: Date;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import type { MemberViewModel } from "../../club/member/member.models";
|
||||||
|
|
||||||
|
export interface RespiratoryWearerViewModel {
|
||||||
|
id: string;
|
||||||
|
memberId: string;
|
||||||
|
member: MemberViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateRespiratoryWearerViewModel {
|
||||||
|
memberId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateRespiratoryWearerViewModel {
|
||||||
|
id: string;
|
||||||
|
memberId: string;
|
||||||
|
}
|
16
src/viewmodels/admin/unit/vehicle/vehicle.models.ts
Normal file
16
src/viewmodels/admin/unit/vehicle/vehicle.models.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
export interface VehicleViewModel {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateVehicleViewModel {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateVehicleViewModel {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
16
src/viewmodels/admin/unit/vehicleType/vehicleType.models.ts
Normal file
16
src/viewmodels/admin/unit/vehicleType/vehicleType.models.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
export interface VehicleTypeViewModel {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateVehicleTypeViewModel {
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateVehicleTypeViewModel {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
}
|
29
src/viewmodels/admin/unit/wearable/wearable.models.ts
Normal file
29
src/viewmodels/admin/unit/wearable/wearable.models.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import type { MemberViewModel } from "../../club/member/member.models";
|
||||||
|
import type { WearableTypeViewModel } from "../wearableType/wearableType.models";
|
||||||
|
|
||||||
|
export interface WearableViewModel {
|
||||||
|
id: string;
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
location?: string;
|
||||||
|
wearerId?: string;
|
||||||
|
wearer: MemberViewModel;
|
||||||
|
wearableTypeId: string;
|
||||||
|
wearableType: WearableTypeViewModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateWearableViewModel {
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
wearerId?: string;
|
||||||
|
location?: string;
|
||||||
|
wearableTypeId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateWearableViewModel {
|
||||||
|
id: string;
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
location?: string;
|
||||||
|
wearerId?: string;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
export interface WearableTypeViewModel {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateWearableTypeViewModel {
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateWearableTypeViewModel {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
}
|
46
src/views/admin/unit/damageReport/DamageReport.vue
Normal file
46
src/views/admin/unit/damageReport/DamageReport.vue
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col w-full h-full gap-2 justify-center px-7">
|
||||||
|
<Pagination
|
||||||
|
:items="damageReports"
|
||||||
|
:totalCount="totalCount"
|
||||||
|
:indicateLoading="loading == 'loading'"
|
||||||
|
@load-data="(offset, count, search) => fetchDamageReports(offset, count, search)"
|
||||||
|
@search="(search) => fetchDamageReports(0, maxEntriesPerPage, search, true)"
|
||||||
|
>
|
||||||
|
<template #pageRow="{ row }: { row: DamageReportViewModel }">
|
||||||
|
<DamageReportListItem :damageReport="row" />
|
||||||
|
</template>
|
||||||
|
</Pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { useDamageReportStore } from "@/stores/admin/unit/damageReport/damageReport";
|
||||||
|
import type { DamageReportViewModel } from "@/viewmodels/admin/unit/damageReport/damageReport.models";
|
||||||
|
import Pagination from "@/components/Pagination.vue";
|
||||||
|
import DamageReportListItem from "@/components/admin/unit/damageReport/DamageReportListItem.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
maxEntriesPerPage: 25,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useDamageReportStore, ["damageReports", "totalCount", "loading"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchDamageReports(0, this.maxEntriesPerPage, "", true);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useDamageReportStore, ["fetchDamageReports"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
69
src/views/admin/unit/damageReport/DamageReportRouting.vue
Normal file
69
src/views/admin/unit/damageReport/DamageReportRouting.vue
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<template>
|
||||||
|
<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">Schadensmeldungen</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<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 class="w-full flex flex-row max-lg:flex-wrap justify-center">
|
||||||
|
<RouterLink
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.route"
|
||||||
|
v-slot="{ isExactActive }"
|
||||||
|
:to="{ name: tab.route }"
|
||||||
|
class="w-1/2 md:w-1/3 lg:w-full p-0.5 first:pl-0 last:pr-0"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
:class="[
|
||||||
|
'w-full rounded-lg py-2.5 text-sm text-center font-medium leading-5 focus:ring-0 outline-hidden',
|
||||||
|
isExactActive ? 'bg-red-200 shadow-sm border-b-2 border-primary rounded-b-none' : ' hover:bg-red-200',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ tab.title }}
|
||||||
|
</p>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
<RouterView />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { RouterLink, RouterView } from "vue-router";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
equipmentId: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabs: [
|
||||||
|
{ route: "admin-unit-damage_report", title: "offen" },
|
||||||
|
{ route: "admin-unit-damage_report-done", title: "bearbeitet" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useEquipmentStore, ["activeEquipmentObj"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchEquipmentByActiveId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useEquipmentStore, ["fetchEquipmentByActiveId"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
211
src/views/admin/unit/equipment/CreateEquipment.vue
Normal file
211
src/views/admin/unit/equipment/CreateEquipment.vue
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
<template>
|
||||||
|
<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">Ausrüstung erfassen</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="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<Combobox v-model="selectedType">
|
||||||
|
<ComboboxLabel>Typ</ComboboxLabel>
|
||||||
|
<div class="relative mt-1">
|
||||||
|
<ComboboxInput
|
||||||
|
class="rounded-md shadow-xs relative block w-full px-3 py-2 border border-gray-300 focus:border-primary placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-hidden focus:ring-0 focus:z-10 sm:text-sm resize-none"
|
||||||
|
@input="query = $event.target.value"
|
||||||
|
/>
|
||||||
|
<ComboboxButton class="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
|
<ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||||
|
</ComboboxButton>
|
||||||
|
<TransitionRoot
|
||||||
|
leave="transition ease-in duration-100"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
@after-leave="query = ''"
|
||||||
|
>
|
||||||
|
<ComboboxOptions
|
||||||
|
class="z-20 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-md ring-1 ring-black/5 focus:outline-hidden sm:text-sm"
|
||||||
|
>
|
||||||
|
<ComboboxOption v-if="loading || deferingSearch" as="template" disabled>
|
||||||
|
<li class="flex flex-row gap-2 text-text relative cursor-default select-none py-2 pl-3 pr-4">
|
||||||
|
<Spinner />
|
||||||
|
<span class="font-normal block truncate">suche</span>
|
||||||
|
</li>
|
||||||
|
</ComboboxOption>
|
||||||
|
<ComboboxOption v-else-if="filtered.length === 0 && query == ''" as="template" disabled>
|
||||||
|
<li class="text-text relative cursor-default select-none py-2 pl-3 pr-4">
|
||||||
|
<span class="font-normal block truncate">tippe, um zu suchen...</span>
|
||||||
|
</li>
|
||||||
|
</ComboboxOption>
|
||||||
|
<ComboboxOption v-else-if="filtered.length === 0" as="template" disabled>
|
||||||
|
<li class="text-text relative cursor-default select-none py-2 pl-3 pr-4">
|
||||||
|
<span class="font-normal block truncate">Keine Auswahl gefunden.</span>
|
||||||
|
</li>
|
||||||
|
</ComboboxOption>
|
||||||
|
|
||||||
|
<ComboboxOption
|
||||||
|
v-if="!(loading || deferingSearch)"
|
||||||
|
v-for="type in filtered"
|
||||||
|
as="template"
|
||||||
|
:key="type.id"
|
||||||
|
:value="type.id"
|
||||||
|
v-slot="{ selected, active }"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
class="relative cursor-default select-none py-2 pl-10 pr-4"
|
||||||
|
:class="{
|
||||||
|
'bg-primary text-white': active,
|
||||||
|
'text-gray-900': !active,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span class="block truncate" :class="{ 'font-medium': selected, 'font-normal': !selected }">
|
||||||
|
{{ type.type }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="selected"
|
||||||
|
class="absolute inset-y-0 left-0 flex items-center pl-3"
|
||||||
|
:class="{ 'text-white': active, 'text-primary': !active }"
|
||||||
|
>
|
||||||
|
<CheckIcon class="h-5 w-5" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ComboboxOption>
|
||||||
|
</ComboboxOptions>
|
||||||
|
</TransitionRoot>
|
||||||
|
</div>
|
||||||
|
</Combobox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="name">Bezeichnung</label>
|
||||||
|
<input type="text" id="name" required />
|
||||||
|
</div>
|
||||||
|
<ScanInput name="code" label="Code" :required="false" />
|
||||||
|
<div>
|
||||||
|
<label for="location">Verortung (optional)</label>
|
||||||
|
<input type="text" id="location" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row justify-end gap-2">
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-equipment' }"
|
||||||
|
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>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
import type { CreateEquipmentViewModel } from "@/viewmodels/admin/unit/equipment/equipment.models";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import SuccessCheckmark from "@/components/SuccessCheckmark.vue";
|
||||||
|
import FailureXMark from "@/components/FailureXMark.vue";
|
||||||
|
import {
|
||||||
|
Combobox,
|
||||||
|
ComboboxLabel,
|
||||||
|
ComboboxInput,
|
||||||
|
ComboboxButton,
|
||||||
|
ComboboxOptions,
|
||||||
|
ComboboxOption,
|
||||||
|
TransitionRoot,
|
||||||
|
} from "@headlessui/vue";
|
||||||
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
|
import type { EquipmentTypeViewModel } from "@/viewmodels/admin/unit/equipmentType/equipmentType.models";
|
||||||
|
import { useEquipmentTypeStore } from "@/stores/admin/unit/equipmentType/equipmentType";
|
||||||
|
import ScanInput from "@/components/ScanInput.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
watch: {
|
||||||
|
query() {
|
||||||
|
this.deferingSearch = true;
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = setTimeout(() => {
|
||||||
|
this.deferingSearch = false;
|
||||||
|
this.search();
|
||||||
|
}, 600);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
timeout: null as any,
|
||||||
|
selectedType: null as null | string,
|
||||||
|
loading: false as boolean,
|
||||||
|
deferingSearch: false as boolean,
|
||||||
|
timer: undefined as any,
|
||||||
|
query: "" as string,
|
||||||
|
filtered: [] as Array<EquipmentTypeViewModel>,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useEquipmentTypeStore, ["equipmentTypes"]),
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useEquipmentStore, ["createEquipment"]),
|
||||||
|
...mapActions(useEquipmentTypeStore, ["searchEquipmentTypes"]),
|
||||||
|
search() {
|
||||||
|
this.filtered = [];
|
||||||
|
if (this.query == "") return;
|
||||||
|
this.loading = true;
|
||||||
|
this.searchEquipmentTypes(this.query)
|
||||||
|
.then((res) => {
|
||||||
|
this.filtered = res.data;
|
||||||
|
})
|
||||||
|
.catch((err) => {})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
triggerCreate(e: any) {
|
||||||
|
if (this.selectedType == null) return;
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let createEquipment: CreateEquipmentViewModel = {
|
||||||
|
code: formData.code.value || null,
|
||||||
|
name: formData.name.value,
|
||||||
|
location: formData.location.value,
|
||||||
|
equipmentTypeId: this.selectedType,
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.createEquipment(createEquipment)
|
||||||
|
.then((res) => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.$router.push({
|
||||||
|
name: "admin-unit-equipment-overview",
|
||||||
|
params: {
|
||||||
|
equipmentId: res.data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
70
src/views/admin/unit/equipment/Equipment.vue
Normal file
70
src/views/admin/unit/equipment/Equipment.vue
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<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">Gerätschaften</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #diffMain>
|
||||||
|
<div class="flex flex-col w-full h-full gap-2 justify-center px-7">
|
||||||
|
<Pagination
|
||||||
|
:items="equipments"
|
||||||
|
:totalCount="totalCount"
|
||||||
|
:indicateLoading="loading == 'loading'"
|
||||||
|
useSearch
|
||||||
|
useScanner
|
||||||
|
@load-data="(offset, count, search) => fetchEquipments(offset, count, search)"
|
||||||
|
@search="(search) => fetchEquipments(0, maxEntriesPerPage, search, true)"
|
||||||
|
>
|
||||||
|
<template #pageRow="{ row }: { row: EquipmentViewModel }">
|
||||||
|
<EquipmentListItem :equipment="row" />
|
||||||
|
</template>
|
||||||
|
</Pagination>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<RouterLink
|
||||||
|
v-if="can('create', 'unit', 'equipment')"
|
||||||
|
:to="{ name: 'admin-unit-equipment-create' }"
|
||||||
|
primary
|
||||||
|
button
|
||||||
|
class="w-fit!"
|
||||||
|
>
|
||||||
|
Gerätschaft erfassen
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
import Pagination from "@/components/Pagination.vue";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { EquipmentViewModel } from "@/viewmodels/admin/unit/equipment/equipment.models";
|
||||||
|
import EquipmentListItem from "@/components/admin/unit/equipment/EquipmentListItem.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 0,
|
||||||
|
maxEntriesPerPage: 25,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useEquipmentStore, ["equipments", "totalCount", "loading"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchEquipments(0, this.maxEntriesPerPage, "", true);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useEquipmentStore, ["fetchEquipments"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
81
src/views/admin/unit/equipment/EquipmentRouting.vue
Normal file
81
src/views/admin/unit/equipment/EquipmentRouting.vue
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<template>
|
||||||
|
<MainTemplate>
|
||||||
|
<template #headerInsert>
|
||||||
|
<RouterLink to="../" class="text-primary">zurück zur Liste</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template #topBar>
|
||||||
|
<div class="flex flex-row gap-2 items-center justify-between pt-5 pb-3 px-7">
|
||||||
|
<h1 class="font-bold text-xl h-8 min-h-fit grow">
|
||||||
|
{{ activeEquipmentObj?.name }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<RouterLink v-if="can('update', 'unit', 'equipment')" :to="{ name: 'admin-unit-equipment-edit' }">
|
||||||
|
<PencilIcon class="w-5 h-5" />
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<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 class="w-full flex flex-row max-lg:flex-wrap justify-center">
|
||||||
|
<RouterLink
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.route"
|
||||||
|
v-slot="{ isActive }"
|
||||||
|
:to="{ name: tab.route }"
|
||||||
|
class="w-1/2 md:w-1/3 lg:w-full p-0.5 first:pl-0 last:pr-0"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
:class="[
|
||||||
|
'w-full rounded-lg py-2.5 text-sm text-center font-medium leading-5 focus:ring-0 outline-hidden',
|
||||||
|
isActive ? 'bg-red-200 shadow-sm border-b-2 border-primary rounded-b-none' : ' hover:bg-red-200',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ tab.title }}
|
||||||
|
</p>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
<RouterView />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { RouterLink, RouterView } from "vue-router";
|
||||||
|
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
equipmentId: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabs: [
|
||||||
|
{ route: "admin-unit-equipment-overview", title: "Übersicht" },
|
||||||
|
{ route: "admin-unit-equipment-maintenance", title: "Wartungen" },
|
||||||
|
{ route: "admin-unit-equipment-inspection", title: "Prüfungen" },
|
||||||
|
{ route: "admin-unit-equipment-damage_report", title: "Schadensmeldungen" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useEquipmentStore, ["activeEquipmentObj"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchEquipmentByActiveId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useEquipmentStore, ["fetchEquipmentByActiveId"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
51
src/views/admin/unit/equipment/Overview.vue
Normal file
51
src/views/admin/unit/equipment/Overview.vue
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
|
||||||
|
<div v-if="activeEquipmentObj != null" class="flex flex-col gap-2 w-full">
|
||||||
|
<div>
|
||||||
|
<label for="type">Typ</label>
|
||||||
|
<input type="text" id="type" :value="activeEquipmentObj.equipmentType.type" readonly />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="name">Bezeichnung</label>
|
||||||
|
<input type="text" id="name" :value="activeEquipmentObj.name" readonly />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="code">Code</label>
|
||||||
|
<input type="text" id="code" :value="activeEquipmentObj.code" readonly />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="location">Verortung</label>
|
||||||
|
<input type="text" id="location" :value="activeEquipmentObj.location" readonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Spinner v-if="loadingActive == 'loading'" class="mx-auto" />
|
||||||
|
<p v-else-if="loadingActive == 'failed'" @click="fetchEquipmentByActiveId" class="cursor-pointer">
|
||||||
|
↺ laden fehlgeschlagen
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
equipmentId: String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useEquipmentStore, ["activeEquipmentObj", "loadingActive"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchEquipmentByActiveId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useEquipmentStore, ["fetchEquipmentByActiveId"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
119
src/views/admin/unit/equipment/UpdateEquipment.vue
Normal file
119
src/views/admin/unit/equipment/UpdateEquipment.vue
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
|
||||||
|
<Spinner v-if="loading == 'loading'" class="mx-auto" />
|
||||||
|
<p v-else-if="loading == 'failed'">laden fehlgeschlagen</p>
|
||||||
|
<form
|
||||||
|
v-else-if="equipment != null"
|
||||||
|
class="flex flex-col gap-4 py-2 w-full max-w-xl mx-auto"
|
||||||
|
@submit.prevent="triggerUpdate"
|
||||||
|
>
|
||||||
|
<p class="mx-auto">Ausrüstung bearbeiten</p>
|
||||||
|
<div>
|
||||||
|
<label for="name">Bezeichnung</label>
|
||||||
|
<input type="text" id="name" required v-model="equipment.name" />
|
||||||
|
</div>
|
||||||
|
<ScanInput name="code" label="Code" :required="false" v-model="equipment.code" />
|
||||||
|
<div>
|
||||||
|
<label for="location">Verortung (optional)</label>
|
||||||
|
<input type="text" id="location" v-model="equipment.location" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row justify-end gap-2">
|
||||||
|
<button primary-outline type="reset" class="w-fit!" :disabled="canSaveOrReset" @click="resetForm">
|
||||||
|
abbrechen
|
||||||
|
</button>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||||
|
import type {
|
||||||
|
CreateEquipmentViewModel,
|
||||||
|
EquipmentViewModel,
|
||||||
|
UpdateEquipmentViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/equipment/equipment.models";
|
||||||
|
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 isEqual from "lodash.isequal";
|
||||||
|
import cloneDeep from "lodash.clonedeep";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
equipmentId: String,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
loadingActive() {
|
||||||
|
if (this.loading == "loading") {
|
||||||
|
this.loading = this.loadingActive;
|
||||||
|
}
|
||||||
|
if (this.loadingActive == "fetched") this.equipment = cloneDeep(this.activeEquipmentObj);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
equipment: null as null | EquipmentViewModel,
|
||||||
|
timeout: null as any,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
canSaveOrReset(): boolean {
|
||||||
|
return isEqual(this.activeEquipmentObj, this.equipment);
|
||||||
|
},
|
||||||
|
...mapState(useEquipmentStore, ["activeEquipmentObj", "loadingActive"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchItem();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useEquipmentStore, ["updateActiveEquipment", "fetchEquipmentByActiveId"]),
|
||||||
|
resetForm() {
|
||||||
|
this.equipment = cloneDeep(this.activeEquipmentObj);
|
||||||
|
},
|
||||||
|
fetchItem() {
|
||||||
|
this.fetchEquipmentByActiveId();
|
||||||
|
},
|
||||||
|
triggerUpdate(e: any) {
|
||||||
|
if (this.equipment == null) return;
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let updateEquipment: UpdateEquipmentViewModel = {
|
||||||
|
id: this.equipment.id,
|
||||||
|
code: formData.code.value || null,
|
||||||
|
name: formData.name.value,
|
||||||
|
location: formData.location.value,
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.updateActiveEquipment(updateEquipment)
|
||||||
|
.then((res) => {
|
||||||
|
this.fetchItem();
|
||||||
|
this.status = { status: "success" };
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.status = null;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
72
src/views/admin/unit/equipmentType/EquipmentType.vue
Normal file
72
src/views/admin/unit/equipmentType/EquipmentType.vue
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<template>
|
||||||
|
<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">Geräte-Typen</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #diffMain>
|
||||||
|
<div class="flex flex-col w-full h-full gap-2 justify-center px-7">
|
||||||
|
<Pagination
|
||||||
|
:items="equipmentTypes"
|
||||||
|
:totalCount="totalCount"
|
||||||
|
:indicateLoading="loading == 'loading'"
|
||||||
|
useSearch
|
||||||
|
@load-data="(offset, count, search) => fetchEquipmentTypes(offset, count, search)"
|
||||||
|
@search="(search) => fetchEquipmentTypes(0, maxEntriesPerPage, search, true)"
|
||||||
|
>
|
||||||
|
<template #pageRow="{ row }: { row: EquipmentTypeViewModel }">
|
||||||
|
<EquipmentTypeListItem :equipmentType="row" />
|
||||||
|
</template>
|
||||||
|
</Pagination>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<button v-if="can('create', 'unit', 'equipment_type')" primary class="w-fit!" @click="openCreateModal">
|
||||||
|
Geräte-Typ erstellen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineAsyncComponent, defineComponent, markRaw } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { useEquipmentTypeStore } from "@/stores/admin/unit/equipmentType/equipmentType";
|
||||||
|
import { useModalStore } from "@/stores/modal";
|
||||||
|
import Pagination from "@/components/Pagination.vue";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { EquipmentTypeViewModel } from "@/viewmodels/admin/unit/equipmentType/equipmentType.models";
|
||||||
|
import EquipmentTypeListItem from "@/components/admin/unit/equipmentType/EquipmentTypeListItem.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 0,
|
||||||
|
maxEntriesPerPage: 25,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useEquipmentTypeStore, ["equipmentTypes", "totalCount", "loading"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchEquipmentTypes(0, this.maxEntriesPerPage, "", true);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useEquipmentTypeStore, ["fetchEquipmentTypes"]),
|
||||||
|
...mapActions(useModalStore, ["openModal"]),
|
||||||
|
openCreateModal() {
|
||||||
|
this.openModal(
|
||||||
|
markRaw(
|
||||||
|
defineAsyncComponent(() => import("@/components/admin/unit/equipmentType/CreateEquipmentTypeModal.vue"))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
94
src/views/admin/unit/equipmentType/EquipmentTypeRouting.vue
Normal file
94
src/views/admin/unit/equipmentType/EquipmentTypeRouting.vue
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
<template>
|
||||||
|
<MainTemplate>
|
||||||
|
<template #headerInsert>
|
||||||
|
<RouterLink :to="{ name: 'admin-unit-equipment_type' }" class="text-primary">zurück zur Liste</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template #topBar>
|
||||||
|
<div class="flex flex-row gap-2 items-center justify-between pt-5 pb-3 px-7">
|
||||||
|
<h1 class="font-bold text-xl h-8 min-h-fit grow">
|
||||||
|
{{ activeEquipmentTypeObj?.type }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<RouterLink v-if="can('update', 'unit', 'equipment_type')" :to="{ name: 'admin-unit-equipment_type-edit' }">
|
||||||
|
<PencilIcon class="w-5 h-5" />
|
||||||
|
</RouterLink>
|
||||||
|
<TrashIcon
|
||||||
|
v-if="can('delete', 'unit', 'equipment_type')"
|
||||||
|
class="w-5 h-5 cursor-pointer"
|
||||||
|
@click="openDeleteModal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<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 class="w-full flex flex-row max-lg:flex-wrap justify-center">
|
||||||
|
<RouterLink
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.route"
|
||||||
|
v-slot="{ isActive }"
|
||||||
|
:to="{ name: tab.route }"
|
||||||
|
class="w-1/2 md:w-1/3 lg:w-full p-0.5 first:pl-0 last:pr-0"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
:class="[
|
||||||
|
'w-full rounded-lg py-2.5 text-sm text-center font-medium leading-5 focus:ring-0 outline-hidden',
|
||||||
|
isActive ? 'bg-red-200 shadow-sm border-b-2 border-primary rounded-b-none' : ' hover:bg-red-200',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ tab.title }}
|
||||||
|
</p>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
<RouterView />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineAsyncComponent, defineComponent, markRaw } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { RouterLink, RouterView } from "vue-router";
|
||||||
|
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
||||||
|
import { useModalStore } from "@/stores/modal";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { useEquipmentTypeStore } from "@/stores/admin/unit/equipmentType/equipmentType";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
equipmentTypeId: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabs: [
|
||||||
|
{ route: "admin-unit-equipment_type-overview", title: "Übersicht" },
|
||||||
|
{ route: "admin-unit-equipment_type-inspection_plan", title: "Prüfpläne" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useEquipmentTypeStore, ["activeEquipmentTypeObj"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchEquipmentTypeByActiveId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useEquipmentTypeStore, ["fetchEquipmentTypeByActiveId"]),
|
||||||
|
...mapActions(useModalStore, ["openModal"]),
|
||||||
|
openDeleteModal() {
|
||||||
|
this.openModal(
|
||||||
|
markRaw(
|
||||||
|
defineAsyncComponent(() => import("@/components/admin/unit/equipmentType/CreateEquipmentTypeModal.vue"))
|
||||||
|
),
|
||||||
|
this.equipmentTypeId ?? ""
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
42
src/views/admin/unit/equipmentType/InspectionPlans.vue
Normal file
42
src/views/admin/unit/equipmentType/InspectionPlans.vue
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
|
||||||
|
<Pagination
|
||||||
|
:items="[]"
|
||||||
|
:totalCount="0"
|
||||||
|
:indicateLoading="false"
|
||||||
|
@load-data="(offset, count, search) => {}"
|
||||||
|
@search="(search) => {}"
|
||||||
|
>
|
||||||
|
<template #pageRow="{ row }: { row: { id: string } }">
|
||||||
|
<p>{{ row }}</p>
|
||||||
|
</template>
|
||||||
|
</Pagination>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<button v-if="can('create', 'unit', 'equipment_type')" primary class="w-fit!" @click="">
|
||||||
|
Prüfplan erstellen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import Pagination from "@/components/Pagination.vue";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 0,
|
||||||
|
maxEntriesPerPage: 25,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
43
src/views/admin/unit/equipmentType/Overview.vue
Normal file
43
src/views/admin/unit/equipmentType/Overview.vue
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
|
||||||
|
<div v-if="activeEquipmentTypeObj != null" class="flex flex-col gap-2 w-full">
|
||||||
|
<div>
|
||||||
|
<label for="type">Typ</label>
|
||||||
|
<input type="text" id="type" :value="activeEquipmentTypeObj.type" readonly />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="description">Beschreibung</label>
|
||||||
|
<textarea id="description" :value="activeEquipmentTypeObj.description" class="h-18" readonly></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Spinner v-if="loadingActive == 'loading'" class="mx-auto" />
|
||||||
|
<p v-else-if="loadingActive == 'failed'" @click="fetchEquipmentTypeByActiveId" class="cursor-pointer">
|
||||||
|
↺ laden fehlgeschlagen
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import { useEquipmentTypeStore } from "@/stores/admin/unit/equipmentType/equipmentType";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
equipmentTypeId: String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useEquipmentTypeStore, ["activeEquipmentTypeObj", "loadingActive"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchEquipmentTypeByActiveId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useEquipmentTypeStore, ["fetchEquipmentTypeByActiveId"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
209
src/views/admin/unit/inspectionPlan/CreateInspectionPlan.vue
Normal file
209
src/views/admin/unit/inspectionPlan/CreateInspectionPlan.vue
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
<template>
|
||||||
|
<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">Ausrüstung erfassen</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="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<Combobox v-model="selectedType">
|
||||||
|
<ComboboxLabel>Typ</ComboboxLabel>
|
||||||
|
<div class="relative mt-1">
|
||||||
|
<ComboboxInput
|
||||||
|
class="rounded-md shadow-xs relative block w-full px-3 py-2 border border-gray-300 focus:border-primary placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-hidden focus:ring-0 focus:z-10 sm:text-sm resize-none"
|
||||||
|
@input="query = $event.target.value"
|
||||||
|
/>
|
||||||
|
<ComboboxButton class="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
|
<ChevronUpDownIcon class="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||||
|
</ComboboxButton>
|
||||||
|
<TransitionRoot
|
||||||
|
leave="transition ease-in duration-100"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
@after-leave="query = ''"
|
||||||
|
>
|
||||||
|
<ComboboxOptions
|
||||||
|
class="z-20 absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-md ring-1 ring-black/5 focus:outline-hidden sm:text-sm"
|
||||||
|
>
|
||||||
|
<ComboboxOption v-if="loading || deferingSearch" as="template" disabled>
|
||||||
|
<li class="flex flex-row gap-2 text-text relative cursor-default select-none py-2 pl-3 pr-4">
|
||||||
|
<Spinner />
|
||||||
|
<span class="font-normal block truncate">suche</span>
|
||||||
|
</li>
|
||||||
|
</ComboboxOption>
|
||||||
|
<ComboboxOption v-else-if="filtered.length === 0 && query == ''" as="template" disabled>
|
||||||
|
<li class="text-text relative cursor-default select-none py-2 pl-3 pr-4">
|
||||||
|
<span class="font-normal block truncate">tippe, um zu suchen...</span>
|
||||||
|
</li>
|
||||||
|
</ComboboxOption>
|
||||||
|
<ComboboxOption v-else-if="filtered.length === 0" as="template" disabled>
|
||||||
|
<li class="text-text relative cursor-default select-none py-2 pl-3 pr-4">
|
||||||
|
<span class="font-normal block truncate">Keine Auswahl gefunden.</span>
|
||||||
|
</li>
|
||||||
|
</ComboboxOption>
|
||||||
|
|
||||||
|
<ComboboxOption
|
||||||
|
v-if="!(loading || deferingSearch)"
|
||||||
|
v-for="type in filtered"
|
||||||
|
as="template"
|
||||||
|
:key="type.id"
|
||||||
|
:value="type.id"
|
||||||
|
v-slot="{ selected, active }"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
class="relative cursor-default select-none py-2 pl-10 pr-4"
|
||||||
|
:class="{
|
||||||
|
'bg-primary text-white': active,
|
||||||
|
'text-gray-900': !active,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span class="block truncate" :class="{ 'font-medium': selected, 'font-normal': !selected }">
|
||||||
|
{{ type.type }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="selected"
|
||||||
|
class="absolute inset-y-0 left-0 flex items-center pl-3"
|
||||||
|
:class="{ 'text-white': active, 'text-primary': !active }"
|
||||||
|
>
|
||||||
|
<CheckIcon class="h-5 w-5" aria-hidden="true" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ComboboxOption>
|
||||||
|
</ComboboxOptions>
|
||||||
|
</TransitionRoot>
|
||||||
|
</div>
|
||||||
|
</Combobox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="name">Bezeichnung</label>
|
||||||
|
<input type="text" id="name" required />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="interval">Intervall (optional)</label>
|
||||||
|
<input type="text" id="interval" placeholder="<number>-(d|m|y) oder DD/MM" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row justify-end gap-2">
|
||||||
|
<RouterLink
|
||||||
|
:to="{ name: 'admin-unit-inspectionPlan' }"
|
||||||
|
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>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
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 {
|
||||||
|
Combobox,
|
||||||
|
ComboboxLabel,
|
||||||
|
ComboboxInput,
|
||||||
|
ComboboxButton,
|
||||||
|
ComboboxOptions,
|
||||||
|
ComboboxOption,
|
||||||
|
TransitionRoot,
|
||||||
|
} from "@headlessui/vue";
|
||||||
|
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||||
|
import { useInspectionPlanStore } from "@/stores/admin/unit/inspectionPlan/inspectionPlan";
|
||||||
|
import type { CreateInspectionPlanViewModel } from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
|
||||||
|
import ScanInput from "@/components/ScanInput.vue";
|
||||||
|
import type { EquipmentTypeViewModel } from "../../../../viewmodels/admin/unit/equipmentType/equipmentType.models";
|
||||||
|
import { useEquipmentTypeStore } from "../../../../stores/admin/unit/equipmentType/equipmentType";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
watch: {
|
||||||
|
query() {
|
||||||
|
this.deferingSearch = true;
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = setTimeout(() => {
|
||||||
|
this.deferingSearch = false;
|
||||||
|
this.search();
|
||||||
|
}, 600);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
timeout: null as any,
|
||||||
|
selectedType: null as null | string,
|
||||||
|
loading: false as boolean,
|
||||||
|
deferingSearch: false as boolean,
|
||||||
|
timer: undefined as any,
|
||||||
|
query: "" as string,
|
||||||
|
filtered: [] as Array<EquipmentTypeViewModel>,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useInspectionPlanStore, ["inspectionPlans"]),
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useInspectionPlanStore, ["createInspectionPlan"]),
|
||||||
|
...mapActions(useEquipmentTypeStore, ["searchEquipmentTypes"]),
|
||||||
|
search() {
|
||||||
|
this.filtered = [];
|
||||||
|
if (this.query == "") return;
|
||||||
|
this.loading = true;
|
||||||
|
this.searchEquipmentTypes(this.query)
|
||||||
|
.then((res) => {
|
||||||
|
this.filtered = res.data;
|
||||||
|
})
|
||||||
|
.catch((err) => {})
|
||||||
|
.finally(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
triggerCreate(e: any) {
|
||||||
|
if (this.selectedType == null) return;
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let createInspectionPlan: CreateInspectionPlanViewModel = {
|
||||||
|
title: formData.name.value,
|
||||||
|
equipmentTypeId: "",
|
||||||
|
inspectionInterval: formData.name.value || null,
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.createInspectionPlan(createInspectionPlan)
|
||||||
|
.then((res) => {
|
||||||
|
this.status = { status: "success" };
|
||||||
|
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.$router.push({
|
||||||
|
name: "admin-unit-inspectionPlan-overview",
|
||||||
|
params: {
|
||||||
|
inspectionPlanId: res.data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 1500);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
70
src/views/admin/unit/inspectionPlan/InspectionPlan.vue
Normal file
70
src/views/admin/unit/inspectionPlan/InspectionPlan.vue
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<template>
|
||||||
|
<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üfpläne</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #diffMain>
|
||||||
|
<div class="flex flex-col w-full h-full gap-2 justify-center px-7">
|
||||||
|
<Pagination
|
||||||
|
:items="inspectionPlans"
|
||||||
|
:totalCount="totalCount"
|
||||||
|
:indicateLoading="loading == 'loading'"
|
||||||
|
useSearch
|
||||||
|
useScanner
|
||||||
|
@load-data="(offset, count, search) => fetchInspectionPlans(offset, count, search)"
|
||||||
|
@search="(search) => fetchInspectionPlans(0, maxEntriesPerPage, search, true)"
|
||||||
|
>
|
||||||
|
<template #pageRow="{ row }: { row: InspectionPlanViewModel }">
|
||||||
|
<InspectionPlanListItem :inspectionPlan="row" />
|
||||||
|
</template>
|
||||||
|
</Pagination>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<RouterLink
|
||||||
|
v-if="can('create', 'unit', 'inspection_plan')"
|
||||||
|
:to="{ name: 'admin-unit-inspection_plan-create' }"
|
||||||
|
primary
|
||||||
|
button
|
||||||
|
class="w-fit!"
|
||||||
|
>
|
||||||
|
Prüfplan erstellen
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { useInspectionPlanStore } from "@/stores/admin/unit/inspectionPlan/inspectionPlan";
|
||||||
|
import Pagination from "@/components/Pagination.vue";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { InspectionPlanViewModel } from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
|
||||||
|
import InspectionPlanListItem from "@/components/admin/unit/inspectionPlan/InspectionPlanListItem.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 0,
|
||||||
|
maxEntriesPerPage: 25,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useInspectionPlanStore, ["inspectionPlans", "totalCount", "loading"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchInspectionPlans(0, this.maxEntriesPerPage, "", true);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useInspectionPlanStore, ["fetchInspectionPlans"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<MainTemplate>
|
||||||
|
<template #headerInsert>
|
||||||
|
<RouterLink to="../" class="text-primary">zurück zur Liste</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template #topBar>
|
||||||
|
<div class="flex flex-row gap-2 items-center justify-between pt-5 pb-3 px-7">
|
||||||
|
<h1 class="font-bold text-xl h-8 min-h-fit grow">
|
||||||
|
{{ activeInspectionPlanObj?.title }}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<RouterLink v-if="can('update', 'unit', 'inspection_plan')" :to="{ name: 'admin-unit-inspection_plan-edit' }">
|
||||||
|
<PencilIcon class="w-5 h-5" />
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #diffMain>
|
||||||
|
<div class="flex flex-col gap-2 grow px-7 overflow-hidden">
|
||||||
|
<RouterView />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { RouterLink, RouterView } from "vue-router";
|
||||||
|
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { useInspectionPlanStore } from "@/stores/admin/unit/inspectionPlan/inspectionPlan";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
inspectionPlanId: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabs: [{ route: "admin-unit-inspection_plan-overview", title: "Übersicht" }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useInspectionPlanStore, ["activeInspectionPlanObj"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchInspectionPlanByActiveId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useInspectionPlanStore, ["fetchInspectionPlanByActiveId"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
43
src/views/admin/unit/inspectionPlan/Overview.vue
Normal file
43
src/views/admin/unit/inspectionPlan/Overview.vue
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
|
||||||
|
<div v-if="activeInspectionPlanObj != null" class="flex flex-col gap-2 w-full">
|
||||||
|
<div>
|
||||||
|
<label for="type">Typ</label>
|
||||||
|
<input type="text" id="type" :value="activeInspectionPlanObj.equipmentType.type" readonly />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="interval">Intervall</label>
|
||||||
|
<input type="text" id="interval" :value="activeInspectionPlanObj.inspectionInterval" reaonly />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Spinner v-if="loadingActive == 'loading'" class="mx-auto" />
|
||||||
|
<p v-else-if="loadingActive == 'failed'" @click="fetchInspectionPlanByActiveId" class="cursor-pointer">
|
||||||
|
↺ laden fehlgeschlagen
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import Spinner from "@/components/Spinner.vue";
|
||||||
|
import { useInspectionPlanStore } from "@/stores/admin/unit/inspectionPlan/inspectionPlan";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
inspectionPlanId: String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useInspectionPlanStore, ["activeInspectionPlanObj", "loadingActive"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchInspectionPlanByActiveId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useInspectionPlanStore, ["fetchInspectionPlanByActiveId"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
117
src/views/admin/unit/inspectionPlan/UpdateInspectionPlan.vue
Normal file
117
src/views/admin/unit/inspectionPlan/UpdateInspectionPlan.vue
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col gap-2 h-full w-full overflow-y-auto">
|
||||||
|
<Spinner v-if="loading == 'loading'" class="mx-auto" />
|
||||||
|
<p v-else-if="loading == 'failed'">laden fehlgeschlagen</p>
|
||||||
|
<form
|
||||||
|
v-else-if="inspectionPlan != null"
|
||||||
|
class="flex flex-col gap-4 py-2 w-full max-w-xl mx-auto"
|
||||||
|
@submit.prevent="triggerUpdate"
|
||||||
|
>
|
||||||
|
<p class="mx-auto">Ausrüstung bearbeiten</p>
|
||||||
|
<div>
|
||||||
|
<label for="name">Bezeichnung</label>
|
||||||
|
<input type="text" id="name" required v-model="inspectionPlan.title" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="interval">Intervall (optional)</label>
|
||||||
|
<input type="text" id="interval" placeholder="<number>-(d|m|y) oder DD/MM" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row justify-end gap-2">
|
||||||
|
<button primary-outline type="reset" class="w-fit!" :disabled="canSaveOrReset" @click="resetForm">
|
||||||
|
abbrechen
|
||||||
|
</button>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import { useInspectionPlanStore } from "@/stores/admin/unit/inspectionPlan/inspectionPlan";
|
||||||
|
import type {
|
||||||
|
CreateInspectionPlanViewModel,
|
||||||
|
InspectionPlanViewModel,
|
||||||
|
UpdateInspectionPlanViewModel,
|
||||||
|
} from "@/viewmodels/admin/unit/inspectionPlan/inspectionPlan.models";
|
||||||
|
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 isEqual from "lodash.isequal";
|
||||||
|
import cloneDeep from "lodash.clonedeep";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
inspectionPlanId: String,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
loadingActive() {
|
||||||
|
if (this.loading == "loading") {
|
||||||
|
this.loading = this.loadingActive;
|
||||||
|
}
|
||||||
|
if (this.loadingActive == "fetched") this.inspectionPlan = cloneDeep(this.activeInspectionPlanObj);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
inspectionPlan: null as null | InspectionPlanViewModel,
|
||||||
|
timeout: null as any,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
canSaveOrReset(): boolean {
|
||||||
|
return isEqual(this.activeInspectionPlanObj, this.inspectionPlan);
|
||||||
|
},
|
||||||
|
...mapState(useInspectionPlanStore, ["activeInspectionPlanObj", "loadingActive"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchItem();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useInspectionPlanStore, ["updateActiveInspectionPlan", "fetchInspectionPlanByActiveId"]),
|
||||||
|
resetForm() {
|
||||||
|
this.inspectionPlan = cloneDeep(this.activeInspectionPlanObj);
|
||||||
|
},
|
||||||
|
fetchItem() {
|
||||||
|
this.fetchInspectionPlanByActiveId();
|
||||||
|
},
|
||||||
|
triggerUpdate(e: any) {
|
||||||
|
if (this.inspectionPlan == null) return;
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let updateInspectionPlan: UpdateInspectionPlanViewModel = {
|
||||||
|
id: this.inspectionPlan.id,
|
||||||
|
title: formData.name.value,
|
||||||
|
inspectionInterval: formData.location.value || null,
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.updateActiveInspectionPlan(updateInspectionPlan)
|
||||||
|
.then((res) => {
|
||||||
|
this.fetchItem();
|
||||||
|
this.status = { status: "success" };
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.status = null;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
72
src/views/admin/unit/respiratoryGear/RespiratoryGear.vue
Normal file
72
src/views/admin/unit/respiratoryGear/RespiratoryGear.vue
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<template>
|
||||||
|
<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">Atemschutz-Geräte</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #diffMain>
|
||||||
|
<div class="flex flex-col w-full h-full gap-2 justify-center px-7">
|
||||||
|
<Pagination
|
||||||
|
:items="respiratoryGears"
|
||||||
|
:totalCount="totalCount"
|
||||||
|
:indicateLoading="loading == 'loading'"
|
||||||
|
useSearch
|
||||||
|
@load-data="(offset, count, search) => fetchRespiratoryGears(offset, count, search)"
|
||||||
|
@search="(search) => fetchRespiratoryGears(0, maxEntriesPerPage, search, true)"
|
||||||
|
>
|
||||||
|
<template #pageRow="{ row }: { row: RespiratoryGearViewModel }">
|
||||||
|
<RespiratoryGearListItem :respiratoryGear="row" />
|
||||||
|
</template>
|
||||||
|
</Pagination>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<button v-if="can('create', 'unit', 'respiratory_gear')" primary class="w-fit!" @click="openCreateModal">
|
||||||
|
Gerät erfassen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineAsyncComponent, defineComponent, markRaw } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { useRespiratoryGearStore } from "@/stores/admin/unit/respiratoryGear/respiratoryGear";
|
||||||
|
import { useModalStore } from "@/stores/modal";
|
||||||
|
import Pagination from "@/components/Pagination.vue";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { RespiratoryGearViewModel } from "@/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models";
|
||||||
|
import RespiratoryGearListItem from "@/components/admin/unit/respiratoryGear/RespiratoryGearListItem.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 0,
|
||||||
|
maxEntriesPerPage: 25,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useRespiratoryGearStore, ["respiratoryGears", "totalCount", "loading"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchRespiratoryGears(0, this.maxEntriesPerPage, "", true);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useRespiratoryGearStore, ["fetchRespiratoryGears"]),
|
||||||
|
...mapActions(useModalStore, ["openModal"]),
|
||||||
|
openCreateModal() {
|
||||||
|
this.openModal(
|
||||||
|
markRaw(
|
||||||
|
defineAsyncComponent(() => import("@/components/admin/unit/respiratoryGear/CreateRespiratoryGearModal.vue"))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,85 @@
|
||||||
|
<template>
|
||||||
|
<MainTemplate>
|
||||||
|
<template #headerInsert>
|
||||||
|
<RouterLink to="../" class="text-primary">zurück zur Liste</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template #topBar>
|
||||||
|
<div class="flex flex-row gap-2 items-center justify-between pt-5 pb-3 px-7">
|
||||||
|
<h1 class="font-bold text-xl h-8 min-h-fit grow">AGT-Gerät: {{ activeRespiratoryGearObj?.equipment.name }}</h1>
|
||||||
|
|
||||||
|
<RouterLink
|
||||||
|
v-if="can('read', 'unit', 'equipment')"
|
||||||
|
:to="{
|
||||||
|
name: 'admin-unit-equipment-overview',
|
||||||
|
params: { equipmentId: activeRespiratoryGearObj?.equipment.id ?? '_' },
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<ArrowTopRightOnSquareIcon class="w-5 h-5" />
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<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 class="w-full flex flex-row max-lg:flex-wrap justify-center">
|
||||||
|
<RouterLink
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.route"
|
||||||
|
v-slot="{ isActive }"
|
||||||
|
:to="{ name: tab.route }"
|
||||||
|
class="w-1/2 md:w-1/3 lg:w-full p-0.5 first:pl-0 last:pr-0"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
:class="[
|
||||||
|
'w-full rounded-lg py-2.5 text-sm text-center font-medium leading-5 focus:ring-0 outline-hidden',
|
||||||
|
isActive ? 'bg-red-200 shadow-sm border-b-2 border-primary rounded-b-none' : ' hover:bg-red-200',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ tab.title }}
|
||||||
|
</p>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
<RouterView />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { RouterLink, RouterView } from "vue-router";
|
||||||
|
import { ArrowTopRightOnSquareIcon } from "@heroicons/vue/24/outline";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { useRespiratoryGearStore } from "@/stores/admin/unit/respiratoryGear/respiratoryGear";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
respiratoryGearId: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabs: [
|
||||||
|
{ route: "admin-unit-respiratory_gear-overview", title: "Übersicht" },
|
||||||
|
{ route: "admin-unit-respiratory_gear-maintenance", title: "Wartungen" },
|
||||||
|
{ route: "admin-unit-respiratory_gear-inspection", title: "Prüfungen" },
|
||||||
|
{ route: "admin-unit-respiratory_gear-mission", title: "Einsätze" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useRespiratoryGearStore, ["activeRespiratoryGearObj"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchRespiratoryGearByActiveId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useRespiratoryGearStore, ["fetchRespiratoryGearByActiveId"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,74 @@
|
||||||
|
<template>
|
||||||
|
<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">Atemschutz-Einsätze</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #diffMain>
|
||||||
|
<div class="flex flex-col w-full h-full gap-2 justify-center px-7">
|
||||||
|
<Pagination
|
||||||
|
:items="respiratoryMissions"
|
||||||
|
:totalCount="totalCount"
|
||||||
|
:indicateLoading="loading == 'loading'"
|
||||||
|
useSearch
|
||||||
|
@load-data="(offset, count, search) => fetchRespiratoryMissions(offset, count, search)"
|
||||||
|
@search="(search) => fetchRespiratoryMissions(0, maxEntriesPerPage, search, true)"
|
||||||
|
>
|
||||||
|
<template #pageRow="{ row }: { row: RespiratoryMissionViewModel }">
|
||||||
|
<RespiratoryMissionListItem :respiratoryMission="row" />
|
||||||
|
</template>
|
||||||
|
</Pagination>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<button v-if="can('create', 'unit', 'respiratory_mission')" primary class="w-fit!" @click="openCreateModal">
|
||||||
|
Einsatz erfassen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineAsyncComponent, defineComponent, markRaw } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { useRespiratoryMissionStore } from "@/stores/admin/unit/respiratoryMission/respiratoryMission";
|
||||||
|
import { useModalStore } from "@/stores/modal";
|
||||||
|
import Pagination from "@/components/Pagination.vue";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { RespiratoryMissionViewModel } from "@/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models";
|
||||||
|
import RespiratoryMissionListItem from "@/components/admin/unit/respiratoryMission/RespiratoryMissionListItem.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 0,
|
||||||
|
maxEntriesPerPage: 25,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useRespiratoryMissionStore, ["respiratoryMissions", "totalCount", "loading"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchRespiratoryMissions(0, this.maxEntriesPerPage, "", true);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useRespiratoryMissionStore, ["fetchRespiratoryMissions"]),
|
||||||
|
...mapActions(useModalStore, ["openModal"]),
|
||||||
|
openCreateModal() {
|
||||||
|
this.openModal(
|
||||||
|
markRaw(
|
||||||
|
defineAsyncComponent(
|
||||||
|
() => import("@/components/admin/unit/respiratoryMission/CreateRespiratoryMissionModal.vue")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,73 @@
|
||||||
|
<template>
|
||||||
|
<MainTemplate>
|
||||||
|
<template #headerInsert>
|
||||||
|
<RouterLink to="../" class="text-primary">zurück zur Liste</RouterLink>
|
||||||
|
</template>
|
||||||
|
<template #topBar>
|
||||||
|
<div class="flex flex-row gap-2 items-center justify-between pt-5 pb-3 px-7">
|
||||||
|
<h1 class="font-bold text-xl h-8 min-h-fit grow">AGT-Einsatz: {{ activeRespiratoryMissionObj?.title }}</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<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 class="w-full flex flex-row max-lg:flex-wrap justify-center">
|
||||||
|
<RouterLink
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.route"
|
||||||
|
v-slot="{ isActive }"
|
||||||
|
:to="{ name: tab.route }"
|
||||||
|
class="w-1/2 md:w-1/3 lg:w-full p-0.5 first:pl-0 last:pr-0"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
:class="[
|
||||||
|
'w-full rounded-lg py-2.5 text-sm text-center font-medium leading-5 focus:ring-0 outline-hidden',
|
||||||
|
isActive ? 'bg-red-200 shadow-sm border-b-2 border-primary rounded-b-none' : ' hover:bg-red-200',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ tab.title }}
|
||||||
|
</p>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
<RouterView />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { RouterLink, RouterView } from "vue-router";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import { useRespiratoryMissionStore } from "@/stores/admin/unit/respiratoryMission/respiratoryMission";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
respiratoryMissionId: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabs: [
|
||||||
|
{ route: "admin-unit-respiratory_mission-overview", title: "Übersicht" },
|
||||||
|
{ route: "admin-unit-respiratory_mission-wearer", title: "Träger" },
|
||||||
|
{ route: "admin-unit-respiratory_mission-gear", title: "Geräte" },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useRespiratoryMissionStore, ["activeRespiratoryMissionObj"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchRespiratoryMissionByActiveId();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useRespiratoryMissionStore, ["fetchRespiratoryMissionByActiveId"]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
74
src/views/admin/unit/respiratoryWearer/RespiratoryWearer.vue
Normal file
74
src/views/admin/unit/respiratoryWearer/RespiratoryWearer.vue
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<template>
|
||||||
|
<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">Atemschutz-Träger</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #diffMain>
|
||||||
|
<div class="flex flex-col w-full h-full gap-2 justify-center px-7">
|
||||||
|
<Pagination
|
||||||
|
:items="respiratoryWearers"
|
||||||
|
:totalCount="totalCount"
|
||||||
|
:indicateLoading="loading == 'loading'"
|
||||||
|
useSearch
|
||||||
|
@load-data="(offset, count, search) => fetchRespiratoryWearers(offset, count, search)"
|
||||||
|
@search="(search) => fetchRespiratoryWearers(0, maxEntriesPerPage, search, true)"
|
||||||
|
>
|
||||||
|
<template #pageRow="{ row }: { row: RespiratoryWearerViewModel }">
|
||||||
|
<RespiratoryWearerListItem :respiratoryWearer="row" />
|
||||||
|
</template>
|
||||||
|
</Pagination>
|
||||||
|
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<button v-if="can('create', 'unit', 'respiratory_wearer')" primary class="w-fit!" @click="openCreateModal">
|
||||||
|
Träger erfassen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainTemplate>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineAsyncComponent, defineComponent, markRaw } from "vue";
|
||||||
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
|
import { useRespiratoryWearerStore } from "@/stores/admin/unit/respiratoryWearer/respiratoryWearer";
|
||||||
|
import { useModalStore } from "@/stores/modal";
|
||||||
|
import Pagination from "@/components/Pagination.vue";
|
||||||
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
|
import type { RespiratoryWearerViewModel } from "@/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models";
|
||||||
|
import RespiratoryWearerListItem from "@/components/admin/unit/respiratoryWearer/RespiratoryWearerListItem.vue";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentPage: 0,
|
||||||
|
maxEntriesPerPage: 25,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(useRespiratoryWearerStore, ["respiratoryWearers", "totalCount", "loading"]),
|
||||||
|
...mapState(useAbilityStore, ["can"]),
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchRespiratoryWearers(0, this.maxEntriesPerPage, "", true);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useRespiratoryWearerStore, ["fetchRespiratoryWearers"]),
|
||||||
|
...mapActions(useModalStore, ["openModal"]),
|
||||||
|
openCreateModal() {
|
||||||
|
this.openModal(
|
||||||
|
markRaw(
|
||||||
|
defineAsyncComponent(
|
||||||
|
() => import("@/components/admin/unit/respiratoryWearer/CreateRespiratoryWearerModal.vue")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue