Compare commits
51 commits
main
...
unit/#103-
Author | SHA1 | Date | |
---|---|---|---|
e7078960ba | |||
fe0f31ce6b | |||
6575948841 | |||
50fa0128ea | |||
b7f22357ec | |||
ddeac1aa26 | |||
6c8d57a7e5 | |||
b280654f92 | |||
bd1fdaa234 | |||
ab3083c18d | |||
98fd7b64d2 | |||
30baca2567 | |||
9b2ab1923e | |||
d70edd0086 | |||
def32b786c | |||
b83b22d806 | |||
43f46c0fad | |||
b6e80b358a | |||
dd93f7a7b8 | |||
9ee1cca46d | |||
70e9b47483 | |||
b359044cb5 | |||
ee700d9e02 | |||
a49babe48d | |||
0ea9601ea3 | |||
8766bbce08 | |||
05555425ce | |||
d5c33d899f | |||
bdc139f37f | |||
4ebacc5f52 | |||
6ad2da1c16 | |||
835e6ef8db | |||
c4a67fd11a | |||
46432fbf7d | |||
e25d91802c | |||
00fad29b25 | |||
b6c68d2205 | |||
553eeb7bfb | |||
716823f536 | |||
5641dbb57f | |||
8be88a5245 | |||
f951a1cd4c | |||
4faf93c3ce | |||
36ca3d90a7 | |||
5faa4b7906 | |||
45fe7b34c3 | |||
3e87bbc267 | |||
b6d6dd0796 | |||
2a77a950f5 | |||
2b3231e26c | |||
4338f58dea |
154 changed files with 10804 additions and 116 deletions
package-lock.jsonpackage.json
src
components
CodeDetector.vueModal.vuePagination.vueScanInput.vue
admin/unit
damageReport
equipment
equipmentType
inspection
inspectionPlan
maintenance
respiratoryGear
respiratoryMission
respiratoryWearer
vehicle
vehicleType
wearable
wearableType
search
enums
helpers
main.cssrouter
stores
admin
navigation.ts
modal.tsunit
damageReport
equipment
equipmentType
inspection
inspectionPlan
maintenance
respiratoryGear
respiratoryMission
respiratoryWearer
vehicle
vehicleType
wearable
wearableType
types
viewmodels/admin/unit
views/admin
club
unit/damageReport
64
package-lock.json
generated
64
package-lock.json
generated
|
@ -42,6 +42,7 @@
|
|||
"unplugin-vue-markdown": "^28.3.1",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.16",
|
||||
"vue-qrcode-reader": "^5.7.1",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -3784,6 +3785,18 @@
|
|||
"@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": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||
|
@ -4806,6 +4819,16 @@
|
|||
"dev": true,
|
||||
"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": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz",
|
||||
|
@ -9405,6 +9428,12 @@
|
|||
"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": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
|
||||
|
@ -10967,6 +10996,19 @@
|
|||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-qrcode-reader": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-qrcode-reader/-/vue-qrcode-reader-5.7.2.tgz",
|
||||
"integrity": "sha512-MRwo8IWM+1UzvfRhOQQBqEap06nl0E8QFIb+/HxS1KiH8BqL2qhlzMVvJgMUti4m5x+XH2YlGS0v1Qshpg+Hbw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"barcode-detector": "2.2.2",
|
||||
"webrtc-adapter": "8.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.5.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz",
|
||||
|
@ -11018,6 +11060,19 @@
|
|||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||
"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": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
|
||||
|
@ -11710,6 +11765,15 @@
|
|||
"funding": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
"unplugin-vue-markdown": "^28.3.1",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.16",
|
||||
"vue-qrcode-reader": "^5.7.1",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"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
|
||||
:is="component_ref"
|
||||
:data="data"
|
||||
:callback="callback"
|
||||
@click.stop
|
||||
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">
|
||||
export default {
|
||||
computed: {
|
||||
...mapState(useModalStore, ["show", "component_ref", "data"]),
|
||||
...mapState(useModalStore, ["show", "component_ref", "data", "callback"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useModalStore, ["closeModal"]),
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<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">
|
||||
<Spinner v-if="deferingSearch" />
|
||||
<QrCodeIcon v-if="useScanner" class="h-7 cursor-pointer" @click="scanCode" />
|
||||
<input
|
||||
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"
|
||||
|
@ -66,10 +67,12 @@
|
|||
</template>
|
||||
|
||||
<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 Spinner from "./Spinner.vue";
|
||||
import type { FieldType } from "@/types/dynamicQueries";
|
||||
import { QrCodeIcon } from "@heroicons/vue/24/outline";
|
||||
import { useModalStore } from "../stores/modal";
|
||||
|
||||
const props = defineProps({
|
||||
items: { type: Array<T>, default: [] },
|
||||
|
@ -79,6 +82,7 @@ const props = defineProps({
|
|||
useSearch: { type: Boolean, default: false },
|
||||
enablePreSearch: { type: Boolean, default: false },
|
||||
indicateLoading: { type: Boolean, default: false },
|
||||
useScanner: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
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);
|
||||
};
|
||||
</script>
|
||||
|
||||
<!--
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
computed: {
|
||||
entryCount() {
|
||||
return this.totalCount ?? this.items.length;
|
||||
},
|
||||
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;
|
||||
function scanCode() {
|
||||
useModalStore().openModal(
|
||||
markRaw(defineAsyncComponent(() => import("@/components/CodeDetector.vue"))),
|
||||
"pagination",
|
||||
(result: string) => {
|
||||
searchString.value = result;
|
||||
}
|
||||
// abort if placeholder already added to array
|
||||
if (stateOfPush == true) return acc;
|
||||
// show placeholder if pagenumber is not actively rendered
|
||||
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> -->
|
||||
);
|
||||
}
|
||||
</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.related.name }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<p v-if="damageReport.related">Code: {{ damageReport.related.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.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/equipment/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/equipment/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,36 @@
|
|||
<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>
|
||||
<div class="flex flex-col p-2">
|
||||
<div class="flex flex-row gap-2">
|
||||
<p class="min-w-16">Beschreibung:</p>
|
||||
<p class="grow overflow-hidden">{{ equipmentType.description }}</p>
|
||||
</div>
|
||||
</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/equipment/equipmentType.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
equipmentType: { type: Object as PropType<EquipmentTypeViewModel>, default: {} },
|
||||
},
|
||||
computed: {
|
||||
...mapState(useAbilityStore, ["can"]),
|
||||
},
|
||||
});
|
||||
</script>
|
63
src/components/admin/unit/inspection/OkNotOk.vue
Normal file
63
src/components/admin/unit/inspection/OkNotOk.vue
Normal file
|
@ -0,0 +1,63 @@
|
|||
<template>
|
||||
<div class="flex flex-col h-fit w-full border border-primary rounded-md">
|
||||
<div class="bg-primary p-2 text-white flex flex-row justify-between items-center">
|
||||
<p>{{ inspectionPoint.title }}</p>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<p v-if="inspectionPoint.description" class="pb-2">Beschreibung: {{ inspectionPoint.description }}</p>
|
||||
<hr v-if="inspectionPoint.description" />
|
||||
<RadioGroup v-model="value" :name="inspectionPoint.id" class="flex flex-row gap-2">
|
||||
<RadioGroupOption
|
||||
v-for="option in options"
|
||||
:key="option.key"
|
||||
button
|
||||
:primary="value == option.key"
|
||||
:primary-outline="value != option.key"
|
||||
:value="option.key"
|
||||
>
|
||||
{{ option.title }}
|
||||
</RadioGroupOption>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import { RadioGroup, RadioGroupOption } from "@headlessui/vue";
|
||||
import type { InspectionPointViewModel } from "@/viewmodels/admin/unit/inspection/inspectionPlan.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
inspectionPoint: {
|
||||
type: Object as PropType<InspectionPointViewModel>,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: String as PropType<"true" | "false">,
|
||||
default: "false",
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
data() {
|
||||
return {
|
||||
options: [
|
||||
{ key: "true", title: "OK" },
|
||||
{ key: "false", title: "nicht OK" },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
value: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string) {
|
||||
this.$emit("update:model-value", val);
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
52
src/components/admin/unit/inspection/ResultInput.vue
Normal file
52
src/components/admin/unit/inspection/ResultInput.vue
Normal file
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<div class="flex flex-col h-fit w-full border border-primary rounded-md">
|
||||
<div class="bg-primary p-2 text-white flex flex-row justify-between items-center">
|
||||
<p>{{ inspectionPoint.title }}</p>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<p v-if="inspectionPoint.description" class="pb-2">Beschreibung: {{ inspectionPoint.description }}</p>
|
||||
<hr v-if="inspectionPoint.description" />
|
||||
<label :for="inspectionPoint.id">{{ inspectionPoint.type == "number" ? "Zahl" : "Freitext" }}</label>
|
||||
<input
|
||||
v-if="inspectionPoint.type == 'number'"
|
||||
:id="inspectionPoint.id"
|
||||
:name="inspectionPoint.id"
|
||||
type="number"
|
||||
required
|
||||
v-model="value"
|
||||
/>
|
||||
<textarea v-else :id="inspectionPoint.id" :name="inspectionPoint.id" required class="h-18" v-model="value" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import type { InspectionPointViewModel } from "@/viewmodels/admin/unit/inspection/inspectionPlan.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
inspectionPoint: {
|
||||
type: Object as PropType<InspectionPointViewModel>,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
computed: {
|
||||
value: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string | number) {
|
||||
this.$emit("update:model-value", val.toString());
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,31 @@
|
|||
<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 }} - {{ inspectionPlan.related.type }}</p>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<p>Interval: {{ inspectionPlan.inspectionInterval }}</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/inspection/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,26 @@
|
|||
<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>{{ inspectionPlan.title }}</p>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<p>Interval: {{ inspectionPlan.inspectionInterval }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import type { InspectionPlanViewModel } from "@/viewmodels/admin/unit/inspection/inspectionPlan.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
inspectionPlan: {
|
||||
type: Object as PropType<InspectionPlanViewModel>,
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
</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.related.name }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<p v-if="damageReport.related">Code: {{ damageReport.related.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.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
damageReport: { type: Object as PropType<DamageReportViewModel>, default: {} },
|
||||
},
|
||||
computed: {
|
||||
...mapState(useAbilityStore, ["can"]),
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,156 @@
|
|||
<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: "",
|
||||
commissioned: new Date(),
|
||||
};
|
||||
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/respiratory/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,156 @@
|
|||
<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: "",
|
||||
commissioned: new Date(),
|
||||
};
|
||||
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/respiratory/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,156 @@
|
|||
<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: "",
|
||||
commissioned: new Date(),
|
||||
};
|
||||
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.member.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/respiratory/respiratoryWearer.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
respiratoryWearer: { type: Object as PropType<RespiratoryWearerViewModel>, default: {} },
|
||||
},
|
||||
computed: {
|
||||
...mapState(useAbilityStore, ["can"]),
|
||||
},
|
||||
});
|
||||
</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/vehicle/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,36 @@
|
|||
<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>
|
||||
<div class="flex flex-col p-2">
|
||||
<div class="flex flex-row gap-2">
|
||||
<p class="min-w-16">Beschreibung:</p>
|
||||
<p class="grow overflow-hidden">{{ vehicleType.description }}</p>
|
||||
</div>
|
||||
</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/vehicle/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/wearable/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/wearable/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,47 @@
|
|||
<template>
|
||||
<RouterLink
|
||||
:to="{ name: 'admin-unit-wearable_type-overview', params: { wearableTypeId: wearableType.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>
|
||||
{{ wearableType.type }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-col p-2">
|
||||
<div class="flex flex-row gap-2">
|
||||
<p class="min-w-16">Beschreibung:</p>
|
||||
<p class="grow overflow-hidden">{{ wearableType.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent, defineComponent, markRaw, type PropType } from "vue";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
||||
import { useAbilityStore } from "@/stores/ability";
|
||||
import type { WearableTypeViewModel } from "@/viewmodels/admin/unit/wearable/wearableType.models";
|
||||
import { useModalStore } from "@/stores/modal";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
wearableType: { type: Object as PropType<WearableTypeViewModel>, default: {} },
|
||||
},
|
||||
computed: {
|
||||
...mapState(useAbilityStore, ["can"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useModalStore, ["openModal"]),
|
||||
openDeleteModal() {
|
||||
this.openModal(
|
||||
markRaw(defineAsyncComponent(() => import("@/components/admin/unit/wearableType/DeleteWearableTypeModal.vue"))),
|
||||
this.wearableType.id
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
212
src/components/search/EquipmentSearchSelect.vue
Normal file
212
src/components/search/EquipmentSearchSelect.vue
Normal file
|
@ -0,0 +1,212 @@
|
|||
<template>
|
||||
<div class="w-full">
|
||||
<Combobox v-model="selected" :disabled="disabled">
|
||||
<ComboboxLabel>{{ title }}</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"
|
||||
:class="useScanner ? 'pl-9!' : ''"
|
||||
:displayValue="() => chosen?.name ?? ''"
|
||||
@input="query = $event.target.value"
|
||||
/>
|
||||
<QrCodeIcon
|
||||
v-if="useScanner"
|
||||
class="absolute h-6 stroke-1 left-2 top-1/2 -translate-y-1/2 cursor-pointer z-10"
|
||||
@click="scanCode"
|
||||
/>
|
||||
<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="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 z-20"
|
||||
>
|
||||
<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="available.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">tippe, um zu suchen...</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<ComboboxOption v-else-if="available.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">Keine Auswahl gefunden.</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
|
||||
<ComboboxOption
|
||||
v-if="!(loading || deferingSearch)"
|
||||
v-for="equipment in available"
|
||||
as="template"
|
||||
:key="equipment.id"
|
||||
:value="equipment.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 }">
|
||||
{{ equipment.name }}<span v-if="equipment.code"> - Code: {{ equipment.code }}</span>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent, defineComponent, markRaw, type Prop } from "vue";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxLabel,
|
||||
ComboboxInput,
|
||||
ComboboxButton,
|
||||
ComboboxOptions,
|
||||
ComboboxOption,
|
||||
TransitionRoot,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import Spinner from "../Spinner.vue";
|
||||
import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
|
||||
import type { EquipmentViewModel } from "@/viewmodels/admin/unit/equipment/equipment.models";
|
||||
import { QrCodeIcon } from "@heroicons/vue/24/outline";
|
||||
import { useModalStore } from "@/stores/modal";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
title: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
useScanner: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.initialLoaded) return;
|
||||
this.initialLoaded = true;
|
||||
this.loadEquipmentInitial();
|
||||
},
|
||||
query() {
|
||||
this.deferingSearch = true;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.deferingSearch = false;
|
||||
this.search();
|
||||
}, 600);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialLoaded: false as boolean,
|
||||
loading: false as boolean,
|
||||
deferingSearch: false as boolean,
|
||||
timer: undefined as any,
|
||||
query: "" as string,
|
||||
all: [] as Array<EquipmentViewModel>,
|
||||
filtered: [] as Array<EquipmentViewModel>,
|
||||
chosen: undefined as undefined | EquipmentViewModel,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
available() {
|
||||
return this.query == "" ? this.all : this.filtered;
|
||||
},
|
||||
selected: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string) {
|
||||
this.chosen = this.getEquipmentFromSearch(val);
|
||||
this.$emit("update:model-value", val);
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadEquipmentInitial();
|
||||
this.preloadAll();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useEquipmentStore, ["searchEquipments", "fetchEquipmentById", "getAllEquipments"]),
|
||||
...mapActions(useModalStore, ["openModal"]),
|
||||
preloadAll() {
|
||||
this.loading = true;
|
||||
this.getAllEquipments()
|
||||
.then((res) => {
|
||||
this.all = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
search() {
|
||||
this.filtered = [];
|
||||
if (this.query == "") return;
|
||||
this.loading = true;
|
||||
this.searchEquipments(this.query)
|
||||
.then((res) => {
|
||||
this.filtered = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
getEquipmentFromSearch(id: string) {
|
||||
return this.filtered.find((f) => f.id == id);
|
||||
},
|
||||
loadEquipmentInitial() {
|
||||
if (this.modelValue == "") return;
|
||||
this.fetchEquipmentById(this.modelValue)
|
||||
.then((res) => {
|
||||
this.chosen = res.data;
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
scanCode() {
|
||||
this.openModal(
|
||||
markRaw(defineAsyncComponent(() => import("@/components/CodeDetector.vue"))),
|
||||
"codeScanInput",
|
||||
(result: string) => {
|
||||
this.getEquipmentFromSearch(result);
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
190
src/components/search/EquipmentTypeSearchSelect.vue
Normal file
190
src/components/search/EquipmentTypeSearchSelect.vue
Normal file
|
@ -0,0 +1,190 @@
|
|||
<template>
|
||||
<div class="w-full">
|
||||
<Combobox v-model="selected" :disabled="disabled">
|
||||
<ComboboxLabel>{{ title }}</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"
|
||||
:displayValue="() => chosen?.type ?? ''"
|
||||
@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="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 z-20"
|
||||
>
|
||||
<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="available.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">tippe, um zu suchen...</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<ComboboxOption v-else-if="available.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">Keine Auswahl gefunden.</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
|
||||
<ComboboxOption
|
||||
v-if="!(loading || deferingSearch)"
|
||||
v-for="equipmentType in available"
|
||||
as="template"
|
||||
:key="equipmentType.id"
|
||||
:value="equipmentType.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 }">
|
||||
{{ equipmentType.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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxLabel,
|
||||
ComboboxInput,
|
||||
ComboboxButton,
|
||||
ComboboxOptions,
|
||||
ComboboxOption,
|
||||
TransitionRoot,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import Spinner from "../Spinner.vue";
|
||||
import { useEquipmentTypeStore } from "@/stores/admin/unit/equipmentType/equipmentType";
|
||||
import type { EquipmentTypeViewModel } from "@/viewmodels/admin/unit/equipment/equipmentType.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
title: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.initialLoaded) return;
|
||||
this.initialLoaded = true;
|
||||
this.loadEquipmentTypeInitial();
|
||||
},
|
||||
query() {
|
||||
this.deferingSearch = true;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.deferingSearch = false;
|
||||
this.search();
|
||||
}, 600);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialLoaded: false as boolean,
|
||||
loading: false as boolean,
|
||||
deferingSearch: false as boolean,
|
||||
timer: undefined as any,
|
||||
query: "" as string,
|
||||
all: [] as Array<EquipmentTypeViewModel>,
|
||||
filtered: [] as Array<EquipmentTypeViewModel>,
|
||||
chosen: undefined as undefined | EquipmentTypeViewModel,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
available() {
|
||||
return this.query == "" ? this.all : this.filtered;
|
||||
},
|
||||
selected: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string) {
|
||||
this.chosen = this.getEquipmentTypeFromSearch(val);
|
||||
this.$emit("update:model-value", val);
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadEquipmentTypeInitial();
|
||||
this.preloadAll();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useEquipmentTypeStore, ["searchEquipmentTypes", "fetchEquipmentTypeById", "getAllEquipmentTypes"]),
|
||||
preloadAll() {
|
||||
this.loading = true;
|
||||
this.getAllEquipmentTypes()
|
||||
.then((res) => {
|
||||
this.all = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
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;
|
||||
});
|
||||
},
|
||||
getEquipmentTypeFromSearch(id: string) {
|
||||
return this.filtered.find((f) => f.id == id);
|
||||
},
|
||||
loadEquipmentTypeInitial() {
|
||||
if (this.modelValue == "") return;
|
||||
this.fetchEquipmentTypeById(this.modelValue)
|
||||
.then((res) => {
|
||||
this.chosen = res.data;
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
198
src/components/search/InspectionPlanSearchSelect.vue
Normal file
198
src/components/search/InspectionPlanSearchSelect.vue
Normal file
|
@ -0,0 +1,198 @@
|
|||
<template>
|
||||
<div class="w-full">
|
||||
<Combobox v-model="selected" :disabled="disabled">
|
||||
<ComboboxLabel>{{ title }}</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"
|
||||
:displayValue="() => chosen?.title ?? ''"
|
||||
@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="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 z-20"
|
||||
>
|
||||
<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="available.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">tippe, um zu suchen...</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<ComboboxOption v-else-if="available.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">Keine Auswahl gefunden.</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
|
||||
<ComboboxOption
|
||||
v-if="!(loading || deferingSearch)"
|
||||
v-for="inspectionPlan in available"
|
||||
as="template"
|
||||
:key="inspectionPlan.id"
|
||||
:value="inspectionPlan.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 }">
|
||||
{{ inspectionPlan.title }}
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxLabel,
|
||||
ComboboxInput,
|
||||
ComboboxButton,
|
||||
ComboboxOptions,
|
||||
ComboboxOption,
|
||||
TransitionRoot,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import Spinner from "../Spinner.vue";
|
||||
import { useInspectionPlanStore } from "@/stores/admin/unit/inspectionPlan/inspectionPlan";
|
||||
import type { InspectionPlanViewModel } from "@/viewmodels/admin/unit/inspection/inspectionPlan.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
title: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String as PropType<"vehicle" | "equipment" | "wearable">,
|
||||
default: "equipment",
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.initialLoaded) return;
|
||||
this.initialLoaded = true;
|
||||
this.loadInspectionPlanInitial();
|
||||
},
|
||||
query() {
|
||||
this.deferingSearch = true;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.deferingSearch = false;
|
||||
this.search();
|
||||
}, 600);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialLoaded: false as boolean,
|
||||
loading: false as boolean,
|
||||
deferingSearch: false as boolean,
|
||||
timer: undefined as any,
|
||||
query: "" as string,
|
||||
all: [] as Array<InspectionPlanViewModel>,
|
||||
filtered: [] as Array<InspectionPlanViewModel>,
|
||||
chosen: undefined as undefined | InspectionPlanViewModel,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
available() {
|
||||
return this.query == "" ? this.all : this.filtered;
|
||||
},
|
||||
selected: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string) {
|
||||
this.chosen = this.getInspectionPlanFromSearch(val);
|
||||
this.$emit("update:model-value", val);
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadInspectionPlanInitial();
|
||||
this.preloadAll();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useInspectionPlanStore, [
|
||||
"searchInspectionPlans",
|
||||
"fetchInspectionPlanById",
|
||||
"getAllInspectionPlans",
|
||||
]),
|
||||
preloadAll() {
|
||||
this.loading = true;
|
||||
this.getAllInspectionPlans()
|
||||
.then((res) => {
|
||||
this.all = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
search() {
|
||||
this.filtered = [];
|
||||
if (this.query == "") return;
|
||||
this.loading = true;
|
||||
this.searchInspectionPlans(this.query)
|
||||
.then((res) => {
|
||||
this.filtered = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
getInspectionPlanFromSearch(id: string) {
|
||||
return this.filtered.find((f) => f.id == id);
|
||||
},
|
||||
loadInspectionPlanInitial() {
|
||||
if (this.modelValue == "") return;
|
||||
this.fetchInspectionPlanById(this.modelValue)
|
||||
.then((res) => {
|
||||
this.chosen = res.data;
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -18,7 +18,7 @@
|
|||
@after-leave="query = ''"
|
||||
>
|
||||
<ComboboxOptions
|
||||
class="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"
|
||||
class="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 z-20"
|
||||
>
|
||||
<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">
|
174
src/components/search/MemberSearchSelectSingle.vue
Normal file
174
src/components/search/MemberSearchSelectSingle.vue
Normal file
|
@ -0,0 +1,174 @@
|
|||
<template>
|
||||
<div class="w-full">
|
||||
<Combobox v-model="selected" :disabled="disabled">
|
||||
<ComboboxLabel>{{ title }}</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"
|
||||
:display-value="() => (chosen?.firstname ?? '') + ' ' + (chosen?.lastname ?? '')"
|
||||
@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="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 z-20"
|
||||
>
|
||||
<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="member in filtered"
|
||||
as="template"
|
||||
:key="member.id"
|
||||
:value="member.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 }">
|
||||
{{ member.firstname }} {{ member.lastname }} {{ member.nameaffix }}
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxLabel,
|
||||
ComboboxInput,
|
||||
ComboboxButton,
|
||||
ComboboxOptions,
|
||||
ComboboxOption,
|
||||
TransitionRoot,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import { useMemberStore } from "@/stores/admin/club/member/member";
|
||||
import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.models";
|
||||
import Spinner from "../Spinner.vue";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
title: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
watch: {
|
||||
modelValue() {
|
||||
//if (this.initialLoaded) return;
|
||||
this.initialLoaded = true;
|
||||
this.loadMemberInitial();
|
||||
},
|
||||
query() {
|
||||
this.deferingSearch = true;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.deferingSearch = false;
|
||||
this.search();
|
||||
}, 600);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialLoaded: false as boolean,
|
||||
loading: false as boolean,
|
||||
deferingSearch: false as boolean,
|
||||
timer: undefined as any,
|
||||
query: "" as string,
|
||||
filtered: [] as Array<MemberViewModel>,
|
||||
chosen: undefined as undefined | MemberViewModel,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selected: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string) {
|
||||
this.chosen = this.getMemberFromSearch(val);
|
||||
this.$emit("update:model-value", val);
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadMemberInitial();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useMemberStore, ["searchMembers", "fetchMemberById"]),
|
||||
search() {
|
||||
this.filtered = [];
|
||||
if (this.query == "") return;
|
||||
this.loading = true;
|
||||
this.searchMembers(this.query)
|
||||
.then((res) => {
|
||||
this.filtered = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
getMemberFromSearch(id: string) {
|
||||
return this.filtered.find((f) => f.id == id);
|
||||
},
|
||||
loadMemberInitial() {
|
||||
if (this.modelValue == "" || this.modelValue == null) return;
|
||||
this.fetchMemberById(this.modelValue)
|
||||
.then((res) => {
|
||||
this.chosen = res.data;
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
212
src/components/search/VehicleSearchSelect.vue
Normal file
212
src/components/search/VehicleSearchSelect.vue
Normal file
|
@ -0,0 +1,212 @@
|
|||
<template>
|
||||
<div class="w-full">
|
||||
<Combobox v-model="selected" :disabled="disabled">
|
||||
<ComboboxLabel>{{ title }}</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"
|
||||
:class="useScanner ? 'pl-9!' : ''"
|
||||
:displayValue="() => chosen?.name ?? ''"
|
||||
@input="query = $event.target.value"
|
||||
/>
|
||||
<QrCodeIcon
|
||||
v-if="useScanner"
|
||||
class="absolute h-6 stroke-1 left-2 top-1/2 -translate-y-1/2 cursor-pointer z-10"
|
||||
@click="scanCode"
|
||||
/>
|
||||
<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="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 z-20"
|
||||
>
|
||||
<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="available.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">tippe, um zu suchen...</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<ComboboxOption v-else-if="available.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">Keine Auswahl gefunden.</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
|
||||
<ComboboxOption
|
||||
v-if="!(loading || deferingSearch)"
|
||||
v-for="vehicle in available"
|
||||
as="template"
|
||||
:key="vehicle.id"
|
||||
:value="vehicle.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 }">
|
||||
{{ vehicle.name }}<span v-if="vehicle.code"> - Code: {{ vehicle.code }}</span>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent, defineComponent, markRaw, type Prop } from "vue";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxLabel,
|
||||
ComboboxInput,
|
||||
ComboboxButton,
|
||||
ComboboxOptions,
|
||||
ComboboxOption,
|
||||
TransitionRoot,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import Spinner from "../Spinner.vue";
|
||||
import { useVehicleStore } from "@/stores/admin/unit/vehicle/vehicle";
|
||||
import type { VehicleViewModel } from "@/viewmodels/admin/unit/vehicle/vehicle.models";
|
||||
import { QrCodeIcon } from "@heroicons/vue/24/outline";
|
||||
import { useModalStore } from "@/stores/modal";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
title: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
useScanner: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.initialLoaded) return;
|
||||
this.initialLoaded = true;
|
||||
this.loadVehicleInitial();
|
||||
},
|
||||
query() {
|
||||
this.deferingSearch = true;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.deferingSearch = false;
|
||||
this.search();
|
||||
}, 600);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialLoaded: false as boolean,
|
||||
loading: false as boolean,
|
||||
deferingSearch: false as boolean,
|
||||
timer: undefined as any,
|
||||
query: "" as string,
|
||||
all: [] as Array<VehicleViewModel>,
|
||||
filtered: [] as Array<VehicleViewModel>,
|
||||
chosen: undefined as undefined | VehicleViewModel,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
available() {
|
||||
return this.query == "" ? this.all : this.filtered;
|
||||
},
|
||||
selected: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string) {
|
||||
this.chosen = this.getVehicleFromSearch(val);
|
||||
this.$emit("update:model-value", val);
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadVehicleInitial();
|
||||
this.preloadAll();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useVehicleStore, ["searchVehicles", "fetchVehicleById", "getAllVehicles"]),
|
||||
...mapActions(useModalStore, ["openModal"]),
|
||||
preloadAll() {
|
||||
this.loading = true;
|
||||
this.getAllVehicles()
|
||||
.then((res) => {
|
||||
this.all = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
search() {
|
||||
this.filtered = [];
|
||||
if (this.query == "") return;
|
||||
this.loading = true;
|
||||
this.searchVehicles(this.query)
|
||||
.then((res) => {
|
||||
this.filtered = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
getVehicleFromSearch(id: string) {
|
||||
return this.filtered.find((f) => f.id == id);
|
||||
},
|
||||
loadVehicleInitial() {
|
||||
if (this.modelValue == "") return;
|
||||
this.fetchVehicleById(this.modelValue)
|
||||
.then((res) => {
|
||||
this.chosen = res.data;
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
scanCode() {
|
||||
this.openModal(
|
||||
markRaw(defineAsyncComponent(() => import("@/components/CodeDetector.vue"))),
|
||||
"codeScanInput",
|
||||
(result: string) => {
|
||||
this.getVehicleFromSearch(result);
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
190
src/components/search/VehicleTypeSearchSelect.vue
Normal file
190
src/components/search/VehicleTypeSearchSelect.vue
Normal file
|
@ -0,0 +1,190 @@
|
|||
<template>
|
||||
<div class="w-full">
|
||||
<Combobox v-model="selected" :disabled="disabled">
|
||||
<ComboboxLabel>{{ title }}</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"
|
||||
:displayValue="() => chosen?.type ?? ''"
|
||||
@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="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 z-20"
|
||||
>
|
||||
<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="available.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">tippe, um zu suchen...</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<ComboboxOption v-else-if="available.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">Keine Auswahl gefunden.</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
|
||||
<ComboboxOption
|
||||
v-if="!(loading || deferingSearch)"
|
||||
v-for="vehicleType in available"
|
||||
as="template"
|
||||
:key="vehicleType.id"
|
||||
:value="vehicleType.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 }">
|
||||
{{ vehicleType.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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxLabel,
|
||||
ComboboxInput,
|
||||
ComboboxButton,
|
||||
ComboboxOptions,
|
||||
ComboboxOption,
|
||||
TransitionRoot,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import Spinner from "../Spinner.vue";
|
||||
import { useVehicleTypeStore } from "@/stores/admin/unit/vehicleType/vehicleType";
|
||||
import type { VehicleTypeViewModel } from "@/viewmodels/admin/unit/vehicle/vehicleType.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
title: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.initialLoaded) return;
|
||||
this.initialLoaded = true;
|
||||
this.loadVehicleTypeInitial();
|
||||
},
|
||||
query() {
|
||||
this.deferingSearch = true;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.deferingSearch = false;
|
||||
this.search();
|
||||
}, 600);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialLoaded: false as boolean,
|
||||
loading: false as boolean,
|
||||
deferingSearch: false as boolean,
|
||||
timer: undefined as any,
|
||||
query: "" as string,
|
||||
all: [] as Array<VehicleTypeViewModel>,
|
||||
filtered: [] as Array<VehicleTypeViewModel>,
|
||||
chosen: undefined as undefined | VehicleTypeViewModel,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
available() {
|
||||
return this.query == "" ? this.all : this.filtered;
|
||||
},
|
||||
selected: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string) {
|
||||
this.chosen = this.getVehicleTypeFromSearch(val);
|
||||
this.$emit("update:model-value", val);
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadVehicleTypeInitial();
|
||||
this.preloadAll();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useVehicleTypeStore, ["searchVehicleTypes", "fetchVehicleTypeById", "getAllVehicleTypes"]),
|
||||
preloadAll() {
|
||||
this.loading = true;
|
||||
this.getAllVehicleTypes()
|
||||
.then((res) => {
|
||||
this.all = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
search() {
|
||||
this.filtered = [];
|
||||
if (this.query == "") return;
|
||||
this.loading = true;
|
||||
this.searchVehicleTypes(this.query)
|
||||
.then((res) => {
|
||||
this.filtered = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
getVehicleTypeFromSearch(id: string) {
|
||||
return this.filtered.find((f) => f.id == id);
|
||||
},
|
||||
loadVehicleTypeInitial() {
|
||||
if (this.modelValue == "") return;
|
||||
this.fetchVehicleTypeById(this.modelValue)
|
||||
.then((res) => {
|
||||
this.chosen = res.data;
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
212
src/components/search/WearableSearchSelect.vue
Normal file
212
src/components/search/WearableSearchSelect.vue
Normal file
|
@ -0,0 +1,212 @@
|
|||
<template>
|
||||
<div class="w-full">
|
||||
<Combobox v-model="selected" :disabled="disabled">
|
||||
<ComboboxLabel>{{ title }}</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"
|
||||
:class="useScanner ? 'pl-9!' : ''"
|
||||
:displayValue="() => chosen?.name ?? ''"
|
||||
@input="query = $event.target.value"
|
||||
/>
|
||||
<QrCodeIcon
|
||||
v-if="useScanner"
|
||||
class="absolute h-6 stroke-1 left-2 top-1/2 -translate-y-1/2 cursor-pointer z-10"
|
||||
@click="scanCode"
|
||||
/>
|
||||
<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="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 z-20"
|
||||
>
|
||||
<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="available.length === 0 && all.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">tippe, um zu suchen...</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<ComboboxOption v-else-if="available.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">Keine Auswahl gefunden.</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
|
||||
<ComboboxOption
|
||||
v-if="!(loading || deferingSearch)"
|
||||
v-for="wearable in available"
|
||||
as="template"
|
||||
:key="wearable.id"
|
||||
:value="wearable.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 }">
|
||||
{{ wearable.name }}<span v-if="wearable.code"> - Code: {{ wearable.code }}</span>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent, defineComponent, markRaw, type Prop } from "vue";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxLabel,
|
||||
ComboboxInput,
|
||||
ComboboxButton,
|
||||
ComboboxOptions,
|
||||
ComboboxOption,
|
||||
TransitionRoot,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import Spinner from "../Spinner.vue";
|
||||
import { useWearableStore } from "@/stores/admin/unit/wearable/wearable";
|
||||
import type { WearableViewModel } from "@/viewmodels/admin/unit/wearable/wearable.models";
|
||||
import { QrCodeIcon } from "@heroicons/vue/24/outline";
|
||||
import { useModalStore } from "@/stores/modal";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
title: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
useScanner: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.initialLoaded) return;
|
||||
this.initialLoaded = true;
|
||||
this.loadWearableInitial();
|
||||
},
|
||||
query() {
|
||||
this.deferingSearch = true;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.deferingSearch = false;
|
||||
this.search();
|
||||
}, 600);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialLoaded: false as boolean,
|
||||
loading: false as boolean,
|
||||
deferingSearch: false as boolean,
|
||||
timer: undefined as any,
|
||||
query: "" as string,
|
||||
all: [] as Array<WearableViewModel>,
|
||||
filtered: [] as Array<WearableViewModel>,
|
||||
chosen: undefined as undefined | WearableViewModel,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
available() {
|
||||
return this.query == "" ? this.all : this.filtered;
|
||||
},
|
||||
selected: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string) {
|
||||
this.chosen = this.getWearableFromSearch(val);
|
||||
this.$emit("update:model-value", val);
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadWearableInitial();
|
||||
this.preloadAll();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useWearableStore, ["searchWearables", "fetchWearableById", "getAllWearables"]),
|
||||
...mapActions(useModalStore, ["openModal"]),
|
||||
preloadAll() {
|
||||
this.loading = true;
|
||||
this.getAllWearables()
|
||||
.then((res) => {
|
||||
this.all = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
search() {
|
||||
this.filtered = [];
|
||||
if (this.query == "") return;
|
||||
this.loading = true;
|
||||
this.searchWearables(this.query)
|
||||
.then((res) => {
|
||||
this.filtered = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
getWearableFromSearch(id: string) {
|
||||
return this.all.find((f) => f.id == id);
|
||||
},
|
||||
loadWearableInitial() {
|
||||
if (this.modelValue == "") return;
|
||||
this.fetchWearableById(this.modelValue)
|
||||
.then((res) => {
|
||||
this.chosen = res.data;
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
scanCode() {
|
||||
this.openModal(
|
||||
markRaw(defineAsyncComponent(() => import("@/components/CodeDetector.vue"))),
|
||||
"codeScanInput",
|
||||
(result: string) => {
|
||||
this.getWearableFromSearch(result);
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
190
src/components/search/WearableTypeSearchSelect.vue
Normal file
190
src/components/search/WearableTypeSearchSelect.vue
Normal file
|
@ -0,0 +1,190 @@
|
|||
<template>
|
||||
<div class="w-full">
|
||||
<Combobox v-model="selected" :disabled="disabled">
|
||||
<ComboboxLabel>{{ title }}</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"
|
||||
:displayValue="() => chosen?.type ?? ''"
|
||||
@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="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 z-20"
|
||||
>
|
||||
<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="available.length === 0 && all.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">tippe, um zu suchen...</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
<ComboboxOption v-else-if="available.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">Keine Auswahl gefunden.</span>
|
||||
</li>
|
||||
</ComboboxOption>
|
||||
|
||||
<ComboboxOption
|
||||
v-if="!(loading || deferingSearch)"
|
||||
v-for="wearableType in available"
|
||||
as="template"
|
||||
:key="wearableType.id"
|
||||
:value="wearableType.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 }">
|
||||
{{ wearableType.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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import { mapState, mapActions } from "pinia";
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxLabel,
|
||||
ComboboxInput,
|
||||
ComboboxButton,
|
||||
ComboboxOptions,
|
||||
ComboboxOption,
|
||||
TransitionRoot,
|
||||
} from "@headlessui/vue";
|
||||
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid";
|
||||
import Spinner from "../Spinner.vue";
|
||||
import { useWearableTypeStore } from "@/stores/admin/unit/wearableType/wearableType";
|
||||
import type { WearableTypeViewModel } from "@/viewmodels/admin/unit/wearable/wearableType.models";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
title: String,
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
watch: {
|
||||
modelValue() {
|
||||
if (this.initialLoaded) return;
|
||||
this.initialLoaded = true;
|
||||
this.loadWearableTypeInitial();
|
||||
},
|
||||
query() {
|
||||
this.deferingSearch = true;
|
||||
clearTimeout(this.timer);
|
||||
this.timer = setTimeout(() => {
|
||||
this.deferingSearch = false;
|
||||
this.search();
|
||||
}, 600);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialLoaded: false as boolean,
|
||||
loading: false as boolean,
|
||||
deferingSearch: false as boolean,
|
||||
timer: undefined as any,
|
||||
query: "" as string,
|
||||
all: [] as Array<WearableTypeViewModel>,
|
||||
filtered: [] as Array<WearableTypeViewModel>,
|
||||
chosen: undefined as undefined | WearableTypeViewModel,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
available() {
|
||||
return this.query == "" ? this.all : this.filtered;
|
||||
},
|
||||
selected: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(val: string) {
|
||||
this.chosen = this.getWearableTypeFromSearch(val);
|
||||
this.$emit("update:model-value", val);
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadWearableTypeInitial();
|
||||
this.preloadAll();
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useWearableTypeStore, ["searchWearableTypes", "fetchWearableTypeById", "getAllWearableTypes"]),
|
||||
preloadAll() {
|
||||
this.loading = true;
|
||||
this.getAllWearableTypes()
|
||||
.then((res) => {
|
||||
this.all = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
search() {
|
||||
this.filtered = [];
|
||||
if (this.query == "") return;
|
||||
this.loading = true;
|
||||
this.searchWearableTypes(this.query)
|
||||
.then((res) => {
|
||||
this.filtered = res.data;
|
||||
})
|
||||
.catch((err) => {})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
getWearableTypeFromSearch(id: string) {
|
||||
return this.all.find((f) => f.id == id);
|
||||
},
|
||||
loadWearableTypeInitial() {
|
||||
if (this.modelValue == "") return;
|
||||
this.fetchWearableTypeById(this.modelValue)
|
||||
.then((res) => {
|
||||
this.chosen = res.data;
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
5
src/enums/inspectionEnum.ts
Normal file
5
src/enums/inspectionEnum.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export enum InspectionPointEnum {
|
||||
oknok = "oknok",
|
||||
text = "text",
|
||||
number = "number",
|
||||
}
|
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;
|
||||
}
|
12
src/main.css
12
src/main.css
|
@ -68,23 +68,23 @@ body {
|
|||
|
||||
/*:not([headlessui]):not([id*="headlessui"]):not([class*="headlessui"])*/
|
||||
button:not([class*="ql"] *):not([class*="fc"]):not([id*="headlessui-combobox"]),
|
||||
a[button] {
|
||||
[button] {
|
||||
@apply cursor-pointer relative box-border h-10 w-full flex justify-center py-2 px-4 text-sm font-medium rounded-md focus:outline-hidden focus:ring-0;
|
||||
}
|
||||
|
||||
button[primary]:not([primary="false"]),
|
||||
a[button][primary]:not([primary="false"]) {
|
||||
[button][primary]:not([primary="false"]) {
|
||||
@apply border-2 border-transparent text-white bg-primary hover:bg-primary;
|
||||
}
|
||||
|
||||
button[primary-outline]:not([primary-outline="false"]),
|
||||
a[button][primary-outline]:not([primary-outline="false"]) {
|
||||
[button][primary-outline]:not([primary-outline="false"]) {
|
||||
@apply border-2 border-primary text-black hover:bg-primary;
|
||||
}
|
||||
|
||||
button:disabled,
|
||||
a[button]:disabled,
|
||||
a[button].disabled {
|
||||
[button]:disabled,
|
||||
[button].disabled {
|
||||
@apply opacity-75 pointer-events-none;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ select {
|
|||
input[readonly],
|
||||
textarea[readonly],
|
||||
select[readonly] {
|
||||
@apply select-none;
|
||||
@apply select-none focus:border-gray-300 cursor-default;
|
||||
/* pointer-events-none; */
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useMemberAwardStore } from "@/stores/admin/club/member/memberAward";
|
|||
import { useMemberExecutivePositionStore } from "@/stores/admin/club/member/memberExecutivePosition";
|
||||
import { useMemberQualificationStore } from "@/stores/admin/club/member/memberQualification";
|
||||
import { useMembershipStore } from "@/stores/admin/club/member/membership";
|
||||
import { useMemberEducationStore } from "../stores/admin/club/member/memberEducation";
|
||||
import { useMemberEducationStore } from "@/stores/admin/club/member/memberEducation";
|
||||
|
||||
export async function setMemberId(to: any, from: any, next: any) {
|
||||
const member = useMemberStore();
|
|
@ -5,10 +5,21 @@ import { isAuthenticated } from "./authGuard";
|
|||
import { isSetup } from "./setupGuard";
|
||||
import { abilityAndNavUpdate } from "./adminGuard";
|
||||
import type { PermissionType, PermissionSection, PermissionModule } from "@/types/permissionTypes";
|
||||
import { resetMemberStores, setMemberId } from "./memberGuard";
|
||||
import { resetProtocolStores, setProtocolId } from "./protocolGuard";
|
||||
import { resetNewsletterStores, setNewsletterId } from "./newsletterGuard";
|
||||
import { setBackupPage } from "./backupGuard";
|
||||
import { resetMemberStores, setMemberId } from "./club/memberGuard";
|
||||
import { resetProtocolStores, setProtocolId } from "./club/protocolGuard";
|
||||
import { resetNewsletterStores, setNewsletterId } from "./club/newsletterGuard";
|
||||
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";
|
||||
import { setVehicleTypeId } from "./unit/vehicleType";
|
||||
import { resetInspectionStores, setInspectionId } from "./unit/inspection";
|
||||
import { setWearableTypeId } from "./unit/wearableType";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
|
@ -309,6 +320,623 @@ 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/unit/equipment/Inspection.vue"),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "report",
|
||||
name: "admin-unit-equipment-damage_report",
|
||||
component: () => import("@/views/admin/unit/equipment/DamageReport.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/unit/vehicle/CreateVehicle.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/unit/vehicle/Overview.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/unit/vehicle/Inspection.vue"),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "report",
|
||||
name: "admin-unit-vehicle-damage_report",
|
||||
component: () => import("@/views/admin/unit/vehicle/DamageReport.vue"),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "edit",
|
||||
name: "admin-unit-vehicle-edit",
|
||||
component: () => import("@/views/admin/unit/vehicle/UpdateVehicle.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: "maintenance",
|
||||
name: "admin-unit-wearable-maintenance",
|
||||
component: () => import("@/views/admin/ViewSelect.vue"),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "inspection",
|
||||
name: "admin-unit-wearable-inspection",
|
||||
component: () => import("@/views/admin/unit/wearable/Inspection.vue"),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "report",
|
||||
name: "admin-unit-wearable-damage_report",
|
||||
component: () => import("@/views/admin/unit/wearable/DamageReport.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: "create",
|
||||
name: "admin-unit-respiratory_gear-create",
|
||||
component: () => import("@/views/admin/unit/respiratoryGear/CreateRespiratoryGear.vue"),
|
||||
meta: { type: "create", section: "unit", module: "respiratory_gear" },
|
||||
beforeEnter: [abilityAndNavUpdate],
|
||||
},
|
||||
{
|
||||
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: "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: "create",
|
||||
name: "admin-unit-respiratory_wearer-create",
|
||||
component: () => import("@/views/admin/unit/respiratoryWearer/CreateRespiratoryWearer.vue"),
|
||||
meta: { type: "create", section: "unit", module: "respiratory_wearer" },
|
||||
beforeEnter: [abilityAndNavUpdate],
|
||||
},
|
||||
{
|
||||
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: "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: "create",
|
||||
name: "admin-unit-respiratory_mission-create",
|
||||
component: () => import("@/views/admin/unit/respiratoryMission/CreateRespiratoryMission.vue"),
|
||||
meta: { type: "create", section: "unit", module: "respiratory_mission" },
|
||||
beforeEnter: [abilityAndNavUpdate],
|
||||
},
|
||||
{
|
||||
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: "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: "maintenance",
|
||||
name: "admin-unit-maintenance-route",
|
||||
component: () => import("@/views/admin/unit/maintenance/MaintenanceRouting.vue"),
|
||||
meta: { type: "read", section: "unit", module: "maintenance" },
|
||||
beforeEnter: [abilityAndNavUpdate],
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
name: "admin-unit-maintenance",
|
||||
component: () => import("@/views/admin/unit/maintenance/Maintenance.vue"),
|
||||
},
|
||||
{
|
||||
path: "done",
|
||||
name: "admin-unit-maintenance-done",
|
||||
component: () => import("@/views/admin/unit/maintenance/Maintenance.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/unit/equipmentType/UpdateEquipmentType.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: [setVehicleTypeId],
|
||||
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/unit/vehicleType/UpdateVehicleType.vue"),
|
||||
meta: { type: "update", section: "unit", module: "vehicle_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",
|
||||
name: "admin-unit-wearable_type-routing",
|
||||
component: () => import("@/views/admin/unit/wearableType/WearableTypeRouting.vue"),
|
||||
beforeEnter: [setWearableTypeId],
|
||||
props: true,
|
||||
children: [
|
||||
{
|
||||
path: "overview",
|
||||
name: "admin-unit-wearable_type-overview",
|
||||
component: () => import("@/views/admin/unit/wearableType/Overview.vue"),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "inspection-plan",
|
||||
name: "admin-unit-wearable_type-inspection_plan",
|
||||
component: () => import("@/views/admin/unit/wearableType/InspectionPlans.vue"),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "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: "inspection_plan" },
|
||||
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: "inspection",
|
||||
name: "admin-unit-inspection-route",
|
||||
component: () => import("@/views/RouterView.vue"),
|
||||
meta: { type: "create", section: "unit", module: "inspection" },
|
||||
beforeEnter: [abilityAndNavUpdate, resetInspectionStores],
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
name: "admin-unit-inspection",
|
||||
redirect: { name: "admin-unit-inspection-plan" },
|
||||
},
|
||||
{
|
||||
path: "plan/:type?/:relatedId?/:inspectionPlanId?",
|
||||
name: "admin-unit-inspection-plan",
|
||||
component: () => import("@/views/admin/unit/inspection/InspectionPlan.vue"),
|
||||
beforeEnter: [],
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: "execute/:inspectionId",
|
||||
name: "admin-unit-inspection-execute",
|
||||
component: () => import("@/views/admin/unit/inspection/InspectionExecute.vue"),
|
||||
beforeEnter: [setInspectionId],
|
||||
props: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "configuration",
|
||||
name: "admin-configuration",
|
||||
|
|
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();
|
||||
}
|
21
src/router/unit/equipmentType.ts
Normal file
21
src/router/unit/equipmentType.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { useEquipmentTypeStore } from "@/stores/admin/unit/equipmentType/equipmentType";
|
||||
import { useEquipmentTypeInspectionPlanStore } from "@/stores/admin/unit/equipmentType/inspectionPlan";
|
||||
|
||||
export async function setEquipmentTypeId(to: any, from: any, next: any) {
|
||||
const equipmentTypeStore = useEquipmentTypeStore();
|
||||
equipmentTypeStore.activeEquipmentType = to.params?.equipmentTypeId ?? null;
|
||||
|
||||
useEquipmentTypeInspectionPlanStore().$reset();
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
export async function resetEquipmentTypeStores(to: any, from: any, next: any) {
|
||||
const equipmentTypeStore = useEquipmentTypeStore();
|
||||
equipmentTypeStore.activeEquipmentType = null;
|
||||
equipmentTypeStore.activeEquipmentTypeObj = null;
|
||||
|
||||
useEquipmentTypeInspectionPlanStore().$reset();
|
||||
|
||||
next();
|
||||
}
|
20
src/router/unit/inspection.ts
Normal file
20
src/router/unit/inspection.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { useInspectionStore } from "@/stores/admin/unit/inspection/inspection";
|
||||
|
||||
export async function setInspectionId(to: any, from: any, next: any) {
|
||||
const InspectionStore = useInspectionStore();
|
||||
InspectionStore.activeInspection = to.params?.inspectionId ?? null;
|
||||
|
||||
//useXYStore().$reset();
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
export async function resetInspectionStores(to: any, from: any, next: any) {
|
||||
const InspectionStore = useInspectionStore();
|
||||
InspectionStore.activeInspection = null;
|
||||
InspectionStore.activeInspectionObj = null;
|
||||
|
||||
//useXYStore().$reset();
|
||||
|
||||
next();
|
||||
}
|
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();
|
||||
}
|
21
src/router/unit/vehicleType.ts
Normal file
21
src/router/unit/vehicleType.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { useVehicleTypeStore } from "@/stores/admin/unit/vehicleType/vehicleType";
|
||||
import { useVehicleTypeInspectionPlanStore } from "@/stores/admin/unit/vehicleType/inspectionPlan";
|
||||
|
||||
export async function setVehicleTypeId(to: any, from: any, next: any) {
|
||||
const vehicleTypeStore = useVehicleTypeStore();
|
||||
vehicleTypeStore.activeVehicleType = to.params?.vehicleTypeId ?? null;
|
||||
|
||||
useVehicleTypeInspectionPlanStore().$reset();
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
export async function resetVehicleTypeStores(to: any, from: any, next: any) {
|
||||
const vehicleTypeStore = useVehicleTypeStore();
|
||||
vehicleTypeStore.activeVehicleType = null;
|
||||
vehicleTypeStore.activeVehicleTypeObj = null;
|
||||
|
||||
useVehicleTypeInspectionPlanStore().$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();
|
||||
}
|
21
src/router/unit/wearableType.ts
Normal file
21
src/router/unit/wearableType.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { useWearableTypeStore } from "@/stores/admin/unit/wearableType/wearableType";
|
||||
import { useWearableTypeInspectionPlanStore } from "@/stores/admin/unit/wearableType/inspectionPlan";
|
||||
|
||||
export async function setWearableTypeId(to: any, from: any, next: any) {
|
||||
const wearableTypeStore = useWearableTypeStore();
|
||||
wearableTypeStore.activeWearableType = to.params?.wearableTypeId ?? null;
|
||||
|
||||
useWearableTypeInspectionPlanStore().$reset();
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
export async function resetWearableTypeStores(to: any, from: any, next: any) {
|
||||
const wearableTypeStore = useWearableTypeStore();
|
||||
wearableTypeStore.activeWearableType = null;
|
||||
wearableTypeStore.activeWearableTypeObj = null;
|
||||
|
||||
useWearableTypeInspectionPlanStore().$reset();
|
||||
|
||||
next();
|
||||
}
|
|
@ -57,6 +57,15 @@ export const useNavigationStore = defineStore("navigation", {
|
|||
} as topLevelNavigationModel,
|
||||
]
|
||||
: []),
|
||||
...(abilityStore.canAccessSection("unit")
|
||||
? [
|
||||
{
|
||||
key: "unit",
|
||||
title: "Wehr",
|
||||
levelDefault: "equipment",
|
||||
} as topLevelNavigationModel,
|
||||
]
|
||||
: []),
|
||||
...(abilityStore.canAccessSection("configuration")
|
||||
? [
|
||||
{
|
||||
|
@ -95,8 +104,45 @@ export const useNavigationStore = defineStore("navigation", {
|
|||
...(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("create", "unit", "inspection") ? [{ key: "inspection", title: "Prüfungen" }] : []),
|
||||
...(abilityStore.can("read", "unit", "damage_report")
|
||||
? [{ key: "damage_report", title: "Schadensmeldungen" }]
|
||||
: []),
|
||||
...(abilityStore.can("read", "unit", "maintenance")
|
||||
? [{ key: "maintenance", title: "Wartungen / Reparaturen" }]
|
||||
: []),
|
||||
{ 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: {
|
||||
mainTitle: "Einstellungen",
|
||||
mainTitle: "Konfiguration",
|
||||
main: [
|
||||
{ key: "divider1", title: "Mitgliederdaten" },
|
||||
...(abilityStore.can("read", "configuration", "salutation")
|
||||
|
|
74
src/stores/admin/unit/damageReport/damageReport.ts
Normal file
74
src/stores/admin/unit/damageReport/damageReport.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type {
|
||||
DamageReportViewModel,
|
||||
CreateDamageReportViewModel,
|
||||
UpdateDamageReportViewModel,
|
||||
} from "@/viewmodels/admin/unit/damageReport.models";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
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) {
|
||||
if (clear) this.damageReports = [];
|
||||
this.loading = "loading";
|
||||
//TODO enable fetch of done reports
|
||||
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 updateDamageReport(damageReport: UpdateDamageReportViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/damageReport/${damageReport.id}`, {
|
||||
// TODO: data
|
||||
});
|
||||
this.fetchDamageReports();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
43
src/stores/admin/unit/equipment/damageReport.ts
Normal file
43
src/stores/admin/unit/equipment/damageReport.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import { useEquipmentStore } from "./equipment";
|
||||
import type { DamageReportViewModel } from "@/viewmodels/admin/unit/damageReport.models";
|
||||
|
||||
export const useEquipmentDamageReportStore = defineStore("equipmentDamageReport", {
|
||||
state: () => {
|
||||
return {
|
||||
damageReports: [] as Array<DamageReportViewModel & { tab_pos: number }>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchDamageReportForEquipment(offset = 0, count = 25, search = "", clear = false) {
|
||||
const equipmentId = useEquipmentStore().activeEquipment;
|
||||
if (clear) this.damageReports = [];
|
||||
this.loading = "loading";
|
||||
http
|
||||
.get(
|
||||
`/admin/damagereport/equipment/${equipmentId}?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`
|
||||
)
|
||||
.then((result) => {
|
||||
this.totalCount = result.data.total;
|
||||
result.data.reports
|
||||
.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";
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
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";
|
||||
|
||||
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) {
|
||||
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.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`, {
|
||||
equipmentTypeId: equipment.equipmentTypeId,
|
||||
name: equipment.name,
|
||||
code: equipment.code,
|
||||
location: equipment.location,
|
||||
commissioned: equipment.commissioned,
|
||||
});
|
||||
this.fetchEquipments();
|
||||
return result;
|
||||
},
|
||||
async updateActiveEquipment(equipment: UpdateEquipmentViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/equipment/${equipment.id}`, {
|
||||
name: equipment.name,
|
||||
code: equipment.code,
|
||||
location: equipment.location,
|
||||
commissioned: equipment.commissioned,
|
||||
decommissioned: equipment.decommissioned,
|
||||
});
|
||||
this.fetchEquipments();
|
||||
return result;
|
||||
},
|
||||
async deleteEquipment(equipment: number): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.delete(`/admin/equipment/${equipment}`);
|
||||
this.fetchEquipments();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
41
src/stores/admin/unit/equipment/inspection.ts
Normal file
41
src/stores/admin/unit/equipment/inspection.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
|
||||
import { useEquipmentStore } from "./equipment";
|
||||
|
||||
export const useEquipmentInspectionStore = defineStore("equipmentInspection", {
|
||||
state: () => {
|
||||
return {
|
||||
inspections: [] as Array<InspectionViewModel & { tab_pos: number }>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchInspectionForEquipment(offset = 0, count = 25, clear = false) {
|
||||
const equipmentId = useEquipmentStore().activeEquipment;
|
||||
if (clear) this.inspections = [];
|
||||
this.loading = "loading";
|
||||
http
|
||||
.get(`/admin/inspection/equipment/${equipmentId}?offset=${offset}&count=${count}`)
|
||||
.then((result) => {
|
||||
this.totalCount = result.data.total;
|
||||
result.data.inspections
|
||||
.filter((elem: InspectionViewModel) => this.inspections.findIndex((m) => m.id == elem.id) == -1)
|
||||
.map((elem: InspectionViewModel, index: number): InspectionViewModel & { tab_pos: number } => {
|
||||
return {
|
||||
...elem,
|
||||
tab_pos: index + offset,
|
||||
};
|
||||
})
|
||||
.forEach((elem: InspectionViewModel & { tab_pos: number }) => {
|
||||
this.inspections.push(elem);
|
||||
});
|
||||
this.loading = "fetched";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.loading = "failed";
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
93
src/stores/admin/unit/equipmentType/equipmentType.ts
Normal file
93
src/stores/admin/unit/equipmentType/equipmentType.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type {
|
||||
EquipmentTypeViewModel,
|
||||
CreateEquipmentTypeViewModel,
|
||||
UpdateEquipmentTypeViewModel,
|
||||
} from "@/viewmodels/admin/unit/equipment/equipmentType.models";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
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) {
|
||||
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.equipmentTypes
|
||||
.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.equipmentTypes };
|
||||
});
|
||||
},
|
||||
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.equipmentTypes };
|
||||
});
|
||||
},
|
||||
fetchEquipmentTypeByActiveId() {
|
||||
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`, {
|
||||
type: equipmentType.type,
|
||||
description: equipmentType.description,
|
||||
});
|
||||
this.fetchEquipmentTypes();
|
||||
return result;
|
||||
},
|
||||
async updateActiveEquipmentType(equipmentType: UpdateEquipmentTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/equipmentType/${equipmentType.id}`, {
|
||||
type: equipmentType.type,
|
||||
description: equipmentType.description,
|
||||
});
|
||||
this.fetchEquipmentTypes();
|
||||
return result;
|
||||
},
|
||||
async deleteEquipmentType(equipmentType: number): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.delete(`/admin/equipmentType/${equipmentType}`);
|
||||
this.fetchEquipmentTypes();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
28
src/stores/admin/unit/equipmentType/inspectionPlan.ts
Normal file
28
src/stores/admin/unit/equipmentType/inspectionPlan.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import type { InspectionPlanViewModel } from "@/viewmodels/admin/unit/inspection/inspectionPlan.models";
|
||||
import { useEquipmentTypeStore } from "./equipmentType";
|
||||
|
||||
export const useEquipmentTypeInspectionPlanStore = defineStore("equipmentTypeInspectionPlan", {
|
||||
state: () => {
|
||||
return {
|
||||
inspectionPlans: [] as Array<InspectionPlanViewModel>,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchInspectionPlanForEquipmentType() {
|
||||
const equipmentTypeId = useEquipmentTypeStore().activeEquipmentType;
|
||||
this.loading = "loading";
|
||||
http
|
||||
.get(`/admin/inspectionPlan/equipmentType/${equipmentTypeId}`)
|
||||
.then((result) => {
|
||||
this.inspectionPlans = result.data;
|
||||
this.loading = "fetched";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.loading = "failed";
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
53
src/stores/admin/unit/inspection/inspection.ts
Normal file
53
src/stores/admin/unit/inspection/inspection.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
|
||||
|
||||
export const useInspectionStore = defineStore("inspection", {
|
||||
state: () => {
|
||||
return {
|
||||
activeInspection: null as string | null,
|
||||
activeInspectionObj: null as InspectionViewModel | null,
|
||||
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchInspectionByActiveId() {
|
||||
this.loadingActive = "loading";
|
||||
http
|
||||
.get(`/admin/inspection/${this.activeInspection}`)
|
||||
.then((res) => {
|
||||
this.activeInspectionObj = res.data;
|
||||
this.loadingActive = "fetched";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.loadingActive = "failed";
|
||||
});
|
||||
},
|
||||
fetchInspectionById(id: string) {
|
||||
return http.get(`/admin/inspection/${id}`);
|
||||
},
|
||||
async createInspection(inspection: any): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.post(`/admin/inspection`, {
|
||||
context: "",
|
||||
inspectionPlanId: "",
|
||||
relatedId: "",
|
||||
assigned: "equipment|vehicle",
|
||||
});
|
||||
this.fetchInspectionByActiveId();
|
||||
return result;
|
||||
},
|
||||
async updateActiveInspection(inspection: any): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/inspection/${inspection.id}`, {
|
||||
context: "",
|
||||
});
|
||||
this.fetchInspectionByActiveId();
|
||||
return result;
|
||||
},
|
||||
async deleteInspection(inspection: number): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.delete(`/admin/inspection/${inspection}`);
|
||||
this.fetchInspectionByActiveId();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
97
src/stores/admin/unit/inspectionPlan/inspectionPlan.ts
Normal file
97
src/stores/admin/unit/inspectionPlan/inspectionPlan.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type {
|
||||
InspectionPlanViewModel,
|
||||
CreateInspectionPlanViewModel,
|
||||
UpdateInspectionPlanViewModel,
|
||||
} from "@/viewmodels/admin/unit/inspection/inspectionPlan.models";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
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) {
|
||||
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 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.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`, {
|
||||
title: inspectionPlan.title,
|
||||
inspectionInterval: inspectionPlan.inspectionInterval,
|
||||
remindTime: inspectionPlan.remindTime,
|
||||
relatedId: inspectionPlan.relatedId,
|
||||
assigned: inspectionPlan.assigned,
|
||||
});
|
||||
this.fetchInspectionPlans();
|
||||
return result;
|
||||
},
|
||||
async updateActiveInspectionPlan(inspectionPlan: UpdateInspectionPlanViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/inspectionPlan/${inspectionPlan.id}`, {
|
||||
title: inspectionPlan.title,
|
||||
inspectionInterval: inspectionPlan.inspectionInterval,
|
||||
remindTime: inspectionPlan.remindTime,
|
||||
});
|
||||
this.fetchInspectionPlans();
|
||||
return result;
|
||||
},
|
||||
async deleteInspectionPlan(inspectionPlan: number): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.delete(`/admin/inspectionPlan/${inspectionPlan}`);
|
||||
this.fetchInspectionPlans();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
74
src/stores/admin/unit/maintenance/maintenance.ts
Normal file
74
src/stores/admin/unit/maintenance/maintenance.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type {
|
||||
MaintenanceViewModel,
|
||||
CreateMaintenanceViewModel,
|
||||
UpdateMaintenanceViewModel,
|
||||
} from "@/viewmodels/admin/unit/maintenance.models";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
export const useMaintenanceStore = defineStore("maintenance", {
|
||||
state: () => {
|
||||
return {
|
||||
maintenances: [] as Array<MaintenanceViewModel & { tab_pos: number }>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchMaintenances(offset = 0, count = 25, search = "", clear = false) {
|
||||
if (clear) this.maintenances = [];
|
||||
this.loading = "loading";
|
||||
//TODO enable fetch of done reports
|
||||
http
|
||||
.get(`/admin/maintenance?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
|
||||
.then((result) => {
|
||||
this.totalCount = result.data.total;
|
||||
result.data.maintenances
|
||||
.filter((elem: MaintenanceViewModel) => this.maintenances.findIndex((m) => m.id == elem.id) == -1)
|
||||
.map((elem: MaintenanceViewModel, index: number): MaintenanceViewModel & { tab_pos: number } => {
|
||||
return {
|
||||
...elem,
|
||||
tab_pos: index + offset,
|
||||
};
|
||||
})
|
||||
.forEach((elem: MaintenanceViewModel & { tab_pos: number }) => {
|
||||
this.maintenances.push(elem);
|
||||
});
|
||||
this.loading = "fetched";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.loading = "failed";
|
||||
});
|
||||
},
|
||||
async getAllMaintenances(): Promise<AxiosResponse<any, any>> {
|
||||
return await http.get(`/admin/maintenance?noLimit=true`).then((res) => {
|
||||
return { ...res, data: res.data.maintenances };
|
||||
});
|
||||
},
|
||||
async getMaintenancesByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
|
||||
return await http
|
||||
.post(`/admin/maintenance/ids`, {
|
||||
ids,
|
||||
})
|
||||
.then((res) => {
|
||||
return { ...res, data: res.data.maintenances };
|
||||
});
|
||||
},
|
||||
async searchMaintenances(search: string): Promise<AxiosResponse<any, any>> {
|
||||
return await http.get(`/admin/maintenance?search=${search}&noLimit=true`).then((res) => {
|
||||
return { ...res, data: res.data.maintenances };
|
||||
});
|
||||
},
|
||||
fetchMaintenanceById(id: string) {
|
||||
return http.get(`/admin/maintenance/${id}`);
|
||||
},
|
||||
async updateMaintenance(maintenance: UpdateMaintenanceViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/maintenance/${maintenance.id}`, {
|
||||
// TODO: data
|
||||
});
|
||||
this.fetchMaintenances();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
93
src/stores/admin/unit/respiratoryGear/respiratoryGear.ts
Normal file
93
src/stores/admin/unit/respiratoryGear/respiratoryGear.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type {
|
||||
RespiratoryGearViewModel,
|
||||
CreateRespiratoryGearViewModel,
|
||||
UpdateRespiratoryGearViewModel,
|
||||
} from "@/viewmodels/admin/unit/respiratory/respiratoryGear.models";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
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) {
|
||||
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.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;
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,99 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type {
|
||||
RespiratoryMissionViewModel,
|
||||
CreateRespiratoryMissionViewModel,
|
||||
UpdateRespiratoryMissionViewModel,
|
||||
} from "@/viewmodels/admin/unit/respiratory/respiratoryMission.models";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
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) {
|
||||
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.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;
|
||||
},
|
||||
},
|
||||
});
|
99
src/stores/admin/unit/respiratoryWearer/respiratoryWearer.ts
Normal file
99
src/stores/admin/unit/respiratoryWearer/respiratoryWearer.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type {
|
||||
RespiratoryWearerViewModel,
|
||||
CreateRespiratoryWearerViewModel,
|
||||
UpdateRespiratoryWearerViewModel,
|
||||
} from "@/viewmodels/admin/unit/respiratory/respiratoryWearer.models";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
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) {
|
||||
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.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;
|
||||
},
|
||||
},
|
||||
});
|
43
src/stores/admin/unit/vehicle/damageReport.ts
Normal file
43
src/stores/admin/unit/vehicle/damageReport.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import { useVehicleStore } from "./vehicle";
|
||||
import type { DamageReportViewModel } from "@/viewmodels/admin/unit/damageReport.models";
|
||||
|
||||
export const useVehicleDamageReportStore = defineStore("vehicleDamageReport", {
|
||||
state: () => {
|
||||
return {
|
||||
damageReports: [] as Array<DamageReportViewModel & { tab_pos: number }>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchDamageReportForVehicle(offset = 0, count = 25, search = "", clear = false) {
|
||||
const vehicleId = useVehicleStore().activeVehicle;
|
||||
if (clear) this.damageReports = [];
|
||||
this.loading = "loading";
|
||||
http
|
||||
.get(
|
||||
`/admin/damagereport/vehicle/${vehicleId}?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`
|
||||
)
|
||||
.then((result) => {
|
||||
this.totalCount = result.data.total;
|
||||
result.data.reports
|
||||
.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";
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
43
src/stores/admin/unit/vehicle/inspection.ts
Normal file
43
src/stores/admin/unit/vehicle/inspection.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
|
||||
import { useVehicleStore } from "./vehicle";
|
||||
|
||||
export const useVehicleInspectionStore = defineStore("vehicleInspection", {
|
||||
state: () => {
|
||||
return {
|
||||
inspections: [] as Array<InspectionViewModel & { tab_pos: number }>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchInspectionForVehicle(offset = 0, count = 25, search = "", clear = false) {
|
||||
const vehicleId = useVehicleStore().activeVehicle;
|
||||
if (clear) this.inspections = [];
|
||||
this.loading = "loading";
|
||||
http
|
||||
.get(
|
||||
`/admin/inspection/vehicle/${vehicleId}?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`
|
||||
)
|
||||
.then((result) => {
|
||||
this.totalCount = result.data.total;
|
||||
result.data.inspections
|
||||
.filter((elem: InspectionViewModel) => this.inspections.findIndex((m) => m.id == elem.id) == -1)
|
||||
.map((elem: InspectionViewModel, index: number): InspectionViewModel & { tab_pos: number } => {
|
||||
return {
|
||||
...elem,
|
||||
tab_pos: index + offset,
|
||||
};
|
||||
})
|
||||
.forEach((elem: InspectionViewModel & { tab_pos: number }) => {
|
||||
this.inspections.push(elem);
|
||||
});
|
||||
this.loading = "fetched";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.loading = "failed";
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
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";
|
||||
|
||||
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) {
|
||||
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.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`, {
|
||||
vehicleTypeId: vehicle.vehicleTypeId,
|
||||
name: vehicle.name,
|
||||
code: vehicle.code,
|
||||
location: vehicle.location,
|
||||
commissioned: vehicle.commissioned,
|
||||
});
|
||||
this.fetchVehicles();
|
||||
return result;
|
||||
},
|
||||
async updateActiveVehicle(vehicle: UpdateVehicleViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/vehicle/${vehicle.id}`, {
|
||||
name: vehicle.name,
|
||||
code: vehicle.code,
|
||||
location: vehicle.location,
|
||||
commissioned: vehicle.commissioned,
|
||||
decommissioned: vehicle.decommissioned,
|
||||
});
|
||||
this.fetchVehicles();
|
||||
return result;
|
||||
},
|
||||
async deleteVehicle(vehicle: number): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.delete(`/admin/vehicle/${vehicle}`);
|
||||
this.fetchVehicles();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
29
src/stores/admin/unit/vehicleType/inspectionPlan.ts
Normal file
29
src/stores/admin/unit/vehicleType/inspectionPlan.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import type { InspectionPlanViewModel } from "@/viewmodels/admin/unit/inspection/inspectionPlan.models";
|
||||
import { useVehicleTypeStore } from "./vehicleType";
|
||||
|
||||
export const useVehicleTypeInspectionPlanStore = defineStore("vehicleTypeInspectionPlan", {
|
||||
state: () => {
|
||||
return {
|
||||
inspectionPlans: [] as Array<InspectionPlanViewModel>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchInspectionPlanForVehicleType() {
|
||||
const vehicleTypeId = useVehicleTypeStore().activeVehicleType;
|
||||
this.loading = "loading";
|
||||
http
|
||||
.get(`/admin/inspectionPlan/vehicleType/${vehicleTypeId}`)
|
||||
.then((result) => {
|
||||
this.inspectionPlans = result.data;
|
||||
this.loading = "fetched";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.loading = "failed";
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
93
src/stores/admin/unit/vehicleType/vehicleType.ts
Normal file
93
src/stores/admin/unit/vehicleType/vehicleType.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type {
|
||||
VehicleTypeViewModel,
|
||||
CreateVehicleTypeViewModel,
|
||||
UpdateVehicleTypeViewModel,
|
||||
} from "@/viewmodels/admin/unit/vehicle/vehicleType.models";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
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) {
|
||||
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.vehicleTypes
|
||||
.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.vehicleTypes };
|
||||
});
|
||||
},
|
||||
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.vehicleTypes };
|
||||
});
|
||||
},
|
||||
fetchVehicleTypeByActiveId() {
|
||||
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`, {
|
||||
type: vehicleType.type,
|
||||
description: vehicleType.description,
|
||||
});
|
||||
this.fetchVehicleTypes();
|
||||
return result;
|
||||
},
|
||||
async updateActiveVehicleType(vehicleType: UpdateVehicleTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/vehicleType/${vehicleType.id}`, {
|
||||
type: vehicleType.type,
|
||||
description: vehicleType.description,
|
||||
});
|
||||
this.fetchVehicleTypes();
|
||||
return result;
|
||||
},
|
||||
async deleteVehicleType(vehicleType: number): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.delete(`/admin/vehicleType/${vehicleType}`);
|
||||
this.fetchVehicleTypes();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
43
src/stores/admin/unit/wearable/damageReport.ts
Normal file
43
src/stores/admin/unit/wearable/damageReport.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import { useWearableStore } from "./wearable";
|
||||
import type { DamageReportViewModel } from "@/viewmodels/admin/unit/damageReport.models";
|
||||
|
||||
export const useWearableDamageReportStore = defineStore("wearableDamageReport", {
|
||||
state: () => {
|
||||
return {
|
||||
damageReports: [] as Array<DamageReportViewModel & { tab_pos: number }>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchDamageReportForWearable(offset = 0, count = 25, search = "", clear = false) {
|
||||
const wearableId = useWearableStore().activeWearable;
|
||||
if (clear) this.damageReports = [];
|
||||
this.loading = "loading";
|
||||
http
|
||||
.get(
|
||||
`/admin/damagereport/wearable/${wearableId}?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`
|
||||
)
|
||||
.then((result) => {
|
||||
this.totalCount = result.data.total;
|
||||
result.data.reports
|
||||
.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";
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
43
src/stores/admin/unit/wearable/inspection.ts
Normal file
43
src/stores/admin/unit/wearable/inspection.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import type { InspectionViewModel } from "@/viewmodels/admin/unit/inspection/inspection.models";
|
||||
import { useWearableStore } from "./wearable";
|
||||
|
||||
export const useWearableInspectionStore = defineStore("wearableInspection", {
|
||||
state: () => {
|
||||
return {
|
||||
inspections: [] as Array<InspectionViewModel & { tab_pos: number }>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchInspectionForWearable(offset = 0, count = 25, search = "", clear = false) {
|
||||
const wearableId = useWearableStore().activeWearable;
|
||||
if (clear) this.inspections = [];
|
||||
this.loading = "loading";
|
||||
http
|
||||
.get(
|
||||
`/admin/inspection/wearable/${wearableId}?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`
|
||||
)
|
||||
.then((result) => {
|
||||
this.totalCount = result.data.total;
|
||||
result.data.inspections
|
||||
.filter((elem: InspectionViewModel) => this.inspections.findIndex((m) => m.id == elem.id) == -1)
|
||||
.map((elem: InspectionViewModel, index: number): InspectionViewModel & { tab_pos: number } => {
|
||||
return {
|
||||
...elem,
|
||||
tab_pos: index + offset,
|
||||
};
|
||||
})
|
||||
.forEach((elem: InspectionViewModel & { tab_pos: number }) => {
|
||||
this.inspections.push(elem);
|
||||
});
|
||||
this.loading = "fetched";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.loading = "failed";
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
110
src/stores/admin/unit/wearable/wearable.ts
Normal file
110
src/stores/admin/unit/wearable/wearable.ts
Normal file
|
@ -0,0 +1,110 @@
|
|||
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";
|
||||
|
||||
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) {
|
||||
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.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`, {
|
||||
wearableTypeId: wearable.wearableTypeId,
|
||||
name: wearable.name,
|
||||
code: wearable.code,
|
||||
location: wearable.location,
|
||||
commissioned: wearable.commissioned,
|
||||
wearerId: wearable.wearerId,
|
||||
});
|
||||
this.fetchWearables();
|
||||
return result;
|
||||
},
|
||||
async updateActiveWearable(wearable: UpdateWearableViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/wearable/${wearable.id}`, {
|
||||
name: wearable.name,
|
||||
code: wearable.code,
|
||||
location: wearable.location,
|
||||
commissioned: wearable.commissioned,
|
||||
decommissioned: wearable.decommissioned,
|
||||
wearerId: wearable.wearerId,
|
||||
});
|
||||
this.fetchWearables();
|
||||
return result;
|
||||
},
|
||||
async deleteWearable(wearable: number): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.delete(`/admin/wearable/${wearable}`);
|
||||
this.fetchWearables();
|
||||
return result;
|
||||
},
|
||||
},
|
||||
});
|
29
src/stores/admin/unit/wearableType/inspectionPlan.ts
Normal file
29
src/stores/admin/unit/wearableType/inspectionPlan.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { http } from "@/serverCom";
|
||||
import type { InspectionPlanViewModel } from "@/viewmodels/admin/unit/inspection/inspectionPlan.models";
|
||||
import { useWearableTypeStore } from "./wearableType";
|
||||
|
||||
export const useWearableTypeInspectionPlanStore = defineStore("wearableTypeInspectionPlan", {
|
||||
state: () => {
|
||||
return {
|
||||
inspectionPlans: [] as Array<InspectionPlanViewModel>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchInspectionPlanForWearableType() {
|
||||
const wearableTypeId = useWearableTypeStore().activeWearableType;
|
||||
this.loading = "loading";
|
||||
http
|
||||
.get(`/admin/inspectionPlan/wearableType/${wearableTypeId}`)
|
||||
.then((result) => {
|
||||
this.inspectionPlans = result.data;
|
||||
this.loading = "fetched";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.loading = "failed";
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
93
src/stores/admin/unit/wearableType/wearableType.ts
Normal file
93
src/stores/admin/unit/wearableType/wearableType.ts
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { defineStore } from "pinia";
|
||||
import type {
|
||||
WearableTypeViewModel,
|
||||
CreateWearableTypeViewModel,
|
||||
UpdateWearableTypeViewModel,
|
||||
} from "@/viewmodels/admin/unit/wearable/wearableType.models";
|
||||
import { http } from "@/serverCom";
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
export const useWearableTypeStore = defineStore("wearableType", {
|
||||
state: () => {
|
||||
return {
|
||||
wearableTypes: [] as Array<WearableTypeViewModel & { tab_pos: number }>,
|
||||
totalCount: 0 as number,
|
||||
loading: "loading" as "loading" | "fetched" | "failed",
|
||||
activeWearableType: null as string | null,
|
||||
activeWearableTypeObj: null as WearableTypeViewModel | null,
|
||||
loadingActive: "loading" as "loading" | "fetched" | "failed",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
fetchWearableTypes(offset = 0, count = 25, search = "", clear = false) {
|
||||
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.wearableTypes
|
||||
.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.wearableTypes };
|
||||
});
|
||||
},
|
||||
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.wearableTypes };
|
||||
});
|
||||
},
|
||||
fetchWearableTypeByActiveId() {
|
||||
this.loadingActive = "loading";
|
||||
http
|
||||
.get(`/admin/wearableType/${this.activeWearableType}`)
|
||||
.then((res) => {
|
||||
this.activeWearableTypeObj = res.data;
|
||||
this.loadingActive = "fetched";
|
||||
})
|
||||
.catch((err) => {
|
||||
this.loadingActive = "failed";
|
||||
});
|
||||
},
|
||||
fetchWearableTypeById(id: string) {
|
||||
return http.get(`/admin/wearableType/${id}`);
|
||||
},
|
||||
async createWearableType(wearableType: CreateWearableTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.post(`/admin/wearableType`, {
|
||||
type: wearableType.type,
|
||||
description: wearableType.description,
|
||||
});
|
||||
this.fetchWearableTypes();
|
||||
return result;
|
||||
},
|
||||
async updateWearableType(wearableType: UpdateWearableTypeViewModel): Promise<AxiosResponse<any, any>> {
|
||||
const result = await http.patch(`/admin/wearableType/${wearableType.id}`, {
|
||||
type: wearableType.type,
|
||||
description: wearableType.description,
|
||||
});
|
||||
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: () => {
|
||||
return {
|
||||
show: false,
|
||||
component_ref: null as any,
|
||||
data: null as any,
|
||||
component_ref: undefined as any,
|
||||
data: undefined as any,
|
||||
callback: undefined as undefined | Function,
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
openModal(component_ref: any, data?: any) {
|
||||
openModal(component_ref: any, data?: any, callback?: Function) {
|
||||
this.component_ref = component_ref;
|
||||
this.data = data;
|
||||
this.callback = callback;
|
||||
this.show = true;
|
||||
},
|
||||
closeModal() {
|
||||
this.component_ref = null;
|
||||
this.data = null;
|
||||
this.component_ref = undefined;
|
||||
this.data = undefined;
|
||||
this.callback = undefined;
|
||||
this.show = false;
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,28 +1,45 @@
|
|||
export type PermissionSection = "club" | "configuration" | "management";
|
||||
export type PermissionSection = "club" | "unit" | "configuration" | "management";
|
||||
|
||||
export type PermissionModule =
|
||||
// club
|
||||
| "member"
|
||||
| "calendar"
|
||||
| "newsletter"
|
||||
| "newsletter_config"
|
||||
| "protocol"
|
||||
| "query"
|
||||
| "listprint"
|
||||
// unit
|
||||
| "equipment"
|
||||
| "equipment_type"
|
||||
| "vehicle"
|
||||
| "vehicle_type"
|
||||
| "wearable"
|
||||
| "wearable_type"
|
||||
| "inspection"
|
||||
| "inspection_plan"
|
||||
| "respiratory_gear"
|
||||
| "respiratory_wearer"
|
||||
| "respiratory_mission"
|
||||
| "damage_report"
|
||||
| "maintenance"
|
||||
// configuration
|
||||
| "qualification"
|
||||
| "award"
|
||||
| "executive_position"
|
||||
| "communication_type"
|
||||
| "membership_status"
|
||||
| "newsletter_config"
|
||||
| "salutation"
|
||||
| "education"
|
||||
| "calendar_type"
|
||||
| "user"
|
||||
| "role"
|
||||
| "webapi"
|
||||
| "query"
|
||||
| "query_store"
|
||||
| "template"
|
||||
| "template_usage"
|
||||
| "backup"
|
||||
// management
|
||||
| "user"
|
||||
| "role"
|
||||
| "webapi"
|
||||
| "setting";
|
||||
|
||||
export type PermissionType = "read" | "create" | "update" | "delete";
|
||||
|
@ -57,35 +74,67 @@ export type SectionsAndModulesObject = {
|
|||
}>;
|
||||
};
|
||||
|
||||
export const permissionSections: Array<PermissionSection> = ["club", "configuration", "management"];
|
||||
export const permissionSections: Array<PermissionSection> = ["club", "unit", "configuration", "management"];
|
||||
export const permissionModules: Array<PermissionModule> = [
|
||||
// club
|
||||
"member",
|
||||
"calendar",
|
||||
"newsletter",
|
||||
"newsletter_config",
|
||||
"protocol",
|
||||
"query",
|
||||
"listprint",
|
||||
// unit
|
||||
"equipment",
|
||||
"equipment_type",
|
||||
"vehicle",
|
||||
"vehicle_type",
|
||||
"wearable",
|
||||
"wearable_type",
|
||||
"inspection",
|
||||
"inspection_plan",
|
||||
"respiratory_gear",
|
||||
"respiratory_wearer",
|
||||
"respiratory_mission",
|
||||
"damage_report",
|
||||
"maintenance",
|
||||
// configuration
|
||||
"qualification",
|
||||
"award",
|
||||
"executive_position",
|
||||
"communication_type",
|
||||
"membership_status",
|
||||
"newsletter_config",
|
||||
"salutation",
|
||||
"education",
|
||||
"calendar_type",
|
||||
"user",
|
||||
"role",
|
||||
"webapi",
|
||||
"query",
|
||||
"query_store",
|
||||
"template",
|
||||
"template_usage",
|
||||
"backup",
|
||||
// management
|
||||
"user",
|
||||
"role",
|
||||
"webapi",
|
||||
"setting",
|
||||
];
|
||||
export const permissionTypes: Array<PermissionType> = ["read", "create", "update", "delete"];
|
||||
export const sectionsAndModules: SectionsAndModulesObject = {
|
||||
club: ["member", "calendar", "newsletter", "protocol", "query", "listprint"],
|
||||
unit: [
|
||||
"equipment",
|
||||
"equipment_type",
|
||||
"vehicle",
|
||||
"vehicle_type",
|
||||
"wearable",
|
||||
"wearable_type",
|
||||
"inspection",
|
||||
"inspection_plan",
|
||||
"respiratory_gear",
|
||||
"respiratory_wearer",
|
||||
"respiratory_mission",
|
||||
"damage_report",
|
||||
"maintenance",
|
||||
],
|
||||
configuration: [
|
||||
"qualification",
|
||||
"award",
|
||||
|
|
45
src/viewmodels/admin/unit/damageReport.models.ts
Normal file
45
src/viewmodels/admin/unit/damageReport.models.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import type { EquipmentViewModel } from "./equipment/equipment.models";
|
||||
import type { MaintenanceViewModel } from "./maintenance.models";
|
||||
import type { VehicleViewModel } from "./vehicle/vehicle.models";
|
||||
import type { WearableViewModel } from "./wearable/wearable.models";
|
||||
|
||||
export type DamageReportAssigned = {
|
||||
relatedId: string;
|
||||
} & (
|
||||
| {
|
||||
assigned: "equipment";
|
||||
related: EquipmentViewModel;
|
||||
}
|
||||
| {
|
||||
assigned: "vehicle";
|
||||
related: VehicleViewModel;
|
||||
}
|
||||
| {
|
||||
assigned: "wearable";
|
||||
related: WearableViewModel;
|
||||
}
|
||||
);
|
||||
|
||||
export type DamageReportViewModel = {
|
||||
id: string;
|
||||
reportedAt: Date;
|
||||
status: string;
|
||||
done: boolean;
|
||||
description: string;
|
||||
imageCount: number;
|
||||
reportedBy: string;
|
||||
maintenance?: MaintenanceViewModel;
|
||||
} & DamageReportAssigned;
|
||||
|
||||
export interface CreateDamageReportViewModel {
|
||||
description: string;
|
||||
reportedBy: string;
|
||||
affectedId: string;
|
||||
affected: "equipment" | "vehicle" | "wearable";
|
||||
}
|
||||
|
||||
export interface UpdateDamageReportViewModel {
|
||||
id: string;
|
||||
status: string;
|
||||
done: boolean;
|
||||
}
|
29
src/viewmodels/admin/unit/equipment/equipment.models.ts
Normal file
29
src/viewmodels/admin/unit/equipment/equipment.models.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import type { EquipmentTypeViewModel } from "./equipmentType.models";
|
||||
|
||||
export interface EquipmentViewModel {
|
||||
id: string;
|
||||
code?: string;
|
||||
name: string;
|
||||
location: string;
|
||||
commissioned: Date;
|
||||
decommissioned?: Date;
|
||||
equipmentTypeId: string;
|
||||
equipmentType: EquipmentTypeViewModel;
|
||||
}
|
||||
|
||||
export interface CreateEquipmentViewModel {
|
||||
code?: string;
|
||||
name: string;
|
||||
location: string;
|
||||
commissioned: Date;
|
||||
equipmentTypeId: string;
|
||||
}
|
||||
|
||||
export interface UpdateEquipmentViewModel {
|
||||
id: string;
|
||||
code?: string;
|
||||
name: string;
|
||||
location: string;
|
||||
commissioned: Date;
|
||||
decommissioned?: Date;
|
||||
}
|
16
src/viewmodels/admin/unit/equipment/equipmentType.models.ts
Normal file
16
src/viewmodels/admin/unit/equipment/equipmentType.models.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export interface EquipmentTypeViewModel {
|
||||
id: string;
|
||||
type: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface CreateEquipmentTypeViewModel {
|
||||
type: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface UpdateEquipmentTypeViewModel {
|
||||
id: string;
|
||||
type: string;
|
||||
description: string;
|
||||
}
|
44
src/viewmodels/admin/unit/inspection/inspection.models.ts
Normal file
44
src/viewmodels/admin/unit/inspection/inspection.models.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import type { EquipmentViewModel } from "../equipment/equipment.models";
|
||||
import type {
|
||||
InspectionPlanViewModel,
|
||||
InspectionPointViewModel,
|
||||
InspectionVersionedPlanViewModel,
|
||||
} from "./inspectionPlan.models";
|
||||
import type { VehicleViewModel } from "../vehicle/vehicle.models";
|
||||
import type { WearableViewModel } from "../wearable/wearable.models";
|
||||
|
||||
export type InspectionViewModel = {
|
||||
id: string;
|
||||
inspectionPlanId: string;
|
||||
inspectionPlan: InspectionPlanViewModel;
|
||||
inspectionVersionedPlanId: string;
|
||||
inspectionVersionedPlan: InspectionVersionedPlanViewModel;
|
||||
context: string;
|
||||
created: Date;
|
||||
finished?: Date;
|
||||
isOpen: boolean;
|
||||
nextInspection?: Date;
|
||||
checks: Array<InspectionPointResultViewModel>;
|
||||
relatedId: string;
|
||||
} & (
|
||||
| {
|
||||
assigned: "equipment";
|
||||
related: EquipmentViewModel;
|
||||
}
|
||||
| {
|
||||
assigned: "vehicle";
|
||||
related: VehicleViewModel;
|
||||
}
|
||||
| {
|
||||
assigned: "wearable";
|
||||
related: WearableViewModel;
|
||||
}
|
||||
);
|
||||
|
||||
export interface InspectionPointResultViewModel {
|
||||
inspectionId: string;
|
||||
inspectionVersionedPlanId: string;
|
||||
inspectionPointId: string;
|
||||
inspectionPoint?: InspectionPointViewModel;
|
||||
value: string;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
import type { InspectionPointEnum } from "@/enums/inspectionEnum";
|
||||
import type { EquipmentViewModel } from "../equipment/equipment.models";
|
||||
import type { VehicleViewModel } from "../vehicle/vehicle.models";
|
||||
import type { EquipmentTypeViewModel } from "../equipment/equipmentType.models";
|
||||
import type { VehicleTypeViewModel } from "../vehicle/vehicleType.models";
|
||||
import type { WearableTypeViewModel } from "../wearable/wearableType.models";
|
||||
|
||||
export type PlanTimeDefinition = `${number}-${"d" | "m" | "y"}` | `${number}/${number | "*"}`;
|
||||
|
||||
export type InspectionPlanViewModel = {
|
||||
id: string;
|
||||
title: string;
|
||||
inspectionInterval: PlanTimeDefinition;
|
||||
remindTime: PlanTimeDefinition;
|
||||
version: number;
|
||||
created: Date;
|
||||
inspectionPoints: InspectionPointViewModel[];
|
||||
relatedId: string;
|
||||
} & (
|
||||
| {
|
||||
assigned: "equipment";
|
||||
related: EquipmentTypeViewModel;
|
||||
}
|
||||
| {
|
||||
assigned: "vehicle";
|
||||
related: VehicleTypeViewModel;
|
||||
}
|
||||
| {
|
||||
assigned: "wearable";
|
||||
related: WearableTypeViewModel;
|
||||
}
|
||||
);
|
||||
|
||||
export interface InspectionVersionedPlanViewModel {
|
||||
id: string;
|
||||
version: number;
|
||||
created: Date;
|
||||
inspectionPoints: InspectionPointViewModel[];
|
||||
}
|
||||
|
||||
export interface InspectionPointViewModel {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
type: InspectionPointEnum;
|
||||
min?: number;
|
||||
max?: number;
|
||||
sort: number;
|
||||
}
|
||||
|
||||
export interface CreateInspectionPlanViewModel {
|
||||
title: string;
|
||||
inspectionInterval: PlanTimeDefinition;
|
||||
remindTime: PlanTimeDefinition;
|
||||
relatedId: string;
|
||||
assigned: "vehicle" | "equipment" | "wearable";
|
||||
}
|
||||
|
||||
export interface UpdateInspectionPlanViewModel {
|
||||
id: string;
|
||||
title: string;
|
||||
inspectionInterval: PlanTimeDefinition;
|
||||
remindTime?: PlanTimeDefinition;
|
||||
}
|
43
src/viewmodels/admin/unit/maintenance.models.ts
Normal file
43
src/viewmodels/admin/unit/maintenance.models.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import type { DamageReportViewModel } from "./damageReport.models";
|
||||
import type { EquipmentViewModel } from "./equipment/equipment.models";
|
||||
import type { VehicleViewModel } from "./vehicle/vehicle.models";
|
||||
import type { WearableViewModel } from "./wearable/wearable.models";
|
||||
|
||||
export type MaintenanceAssigned = {
|
||||
relatedId: string;
|
||||
} & (
|
||||
| {
|
||||
assigned: "equipment";
|
||||
related: EquipmentViewModel;
|
||||
}
|
||||
| {
|
||||
assigned: "vehicle";
|
||||
related: VehicleViewModel;
|
||||
}
|
||||
| {
|
||||
assigned: "wearable";
|
||||
related: WearableViewModel;
|
||||
}
|
||||
);
|
||||
|
||||
export type MaintenanceViewModel = {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
status: string;
|
||||
done: boolean;
|
||||
description: string;
|
||||
reports: DamageReportViewModel[];
|
||||
} & MaintenanceAssigned;
|
||||
|
||||
export interface CreateMaintenanceViewModel {
|
||||
description: string;
|
||||
reportedBy: string;
|
||||
affectedId: string;
|
||||
affected: "equipment" | "vehicle" | "wearable";
|
||||
}
|
||||
|
||||
export interface UpdateMaintenanceViewModel {
|
||||
id: string;
|
||||
status: string;
|
||||
done: boolean;
|
||||
}
|
|
@ -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 "@/viewmodels/admin/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;
|
||||
}
|
29
src/viewmodels/admin/unit/vehicle/vehicle.models.ts
Normal file
29
src/viewmodels/admin/unit/vehicle/vehicle.models.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import type { VehicleTypeViewModel } from "./vehicleType.models";
|
||||
|
||||
export interface VehicleViewModel {
|
||||
id: string;
|
||||
code?: string;
|
||||
name: string;
|
||||
location: string;
|
||||
commissioned: Date;
|
||||
decommissioned?: Date;
|
||||
vehicleTypeId: string;
|
||||
vehicleType: VehicleTypeViewModel;
|
||||
}
|
||||
|
||||
export interface CreateVehicleViewModel {
|
||||
code?: string;
|
||||
name: string;
|
||||
location: string;
|
||||
commissioned: Date;
|
||||
vehicleTypeId: string;
|
||||
}
|
||||
|
||||
export interface UpdateVehicleViewModel {
|
||||
id: string;
|
||||
code?: string;
|
||||
name: string;
|
||||
location: string;
|
||||
commissioned: Date;
|
||||
decommissioned?: Date;
|
||||
}
|
16
src/viewmodels/admin/unit/vehicle/vehicleType.models.ts
Normal file
16
src/viewmodels/admin/unit/vehicle/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;
|
||||
}
|
34
src/viewmodels/admin/unit/wearable/wearable.models.ts
Normal file
34
src/viewmodels/admin/unit/wearable/wearable.models.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.models";
|
||||
import type { WearableTypeViewModel } from "./wearableType.models";
|
||||
|
||||
export interface WearableViewModel {
|
||||
id: string;
|
||||
code?: string;
|
||||
name: string;
|
||||
location: string;
|
||||
commissioned: Date;
|
||||
decommissioned?: Date;
|
||||
wearerId?: string;
|
||||
wearer?: MemberViewModel;
|
||||
wearableTypeId: string;
|
||||
wearableType: WearableTypeViewModel;
|
||||
}
|
||||
|
||||
export interface CreateWearableViewModel {
|
||||
code?: string;
|
||||
name: string;
|
||||
wearerId?: string;
|
||||
location?: string;
|
||||
commissioned: Date;
|
||||
wearableTypeId: string;
|
||||
}
|
||||
|
||||
export interface UpdateWearableViewModel {
|
||||
id: string;
|
||||
code?: string;
|
||||
name: string;
|
||||
location?: string;
|
||||
commissioned: Date;
|
||||
decommissioned?: Date;
|
||||
wearerId?: string;
|
||||
}
|
16
src/viewmodels/admin/unit/wearable/wearableType.models.ts
Normal file
16
src/viewmodels/admin/unit/wearable/wearableType.models.ts
Normal file
|
@ -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;
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-else class="flex flex-row gap-2 items-center">
|
||||
<MemberSearchSelect
|
||||
<MemberSearchSelectMultiple
|
||||
title="weitere Empfänger suchen"
|
||||
showTitleAsPlaceholder
|
||||
v-model="recipients"
|
||||
|
@ -74,7 +74,7 @@ import { useNewsletterRecipientsStore } from "@/stores/admin/club/newsletter/new
|
|||
import { useAbilityStore } from "@/stores/ability";
|
||||
import { useQueryStoreStore } from "@/stores/admin/configuration/queryStore";
|
||||
import { useQueryBuilderStore } from "@/stores/admin/club/queryBuilder";
|
||||
import MemberSearchSelect from "@/components/admin/MemberSearchSelect.vue";
|
||||
import MemberSearchSelectMultiple from "@/components/search/MemberSearchSelectMultiple.vue";
|
||||
import type { FieldType } from "@/types/dynamicQueries";
|
||||
import DoubleConfirmClick from "@/components/DoubleConfirmClick.vue";
|
||||
</script>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
↺ laden fehlgeschlagen
|
||||
</p>
|
||||
|
||||
<MemberSearchSelect
|
||||
<MemberSearchSelectMultiple
|
||||
title="Anwesende suchen"
|
||||
:model-value="presence.map((p) => p.memberId)"
|
||||
:disabled="!can('create', 'club', 'protocol')"
|
||||
|
@ -64,7 +64,7 @@ import { TrashIcon } from "@heroicons/vue/24/outline";
|
|||
import { TrashIcon as TrashIconSolid } from "@heroicons/vue/24/solid";
|
||||
import { useProtocolPresenceStore } from "@/stores/admin/club/protocol/protocolPresence";
|
||||
import { useAbilityStore } from "@/stores/ability";
|
||||
import MemberSearchSelect from "@/components/admin/MemberSearchSelect.vue";
|
||||
import MemberSearchSelectMultiple from "@/components/search/MemberSearchSelectMultiple.vue";
|
||||
import type { MemberViewModel } from "@/viewmodels/admin/club/member/member.models";
|
||||
import DoubleConfirmClick from "@/components/DoubleConfirmClick.vue";
|
||||
</script>
|
||||
|
|
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.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>
|
54
src/views/admin/unit/damageReport/DamageReportRouting.vue
Normal file
54
src/views/admin/unit/damageReport/DamageReportRouting.vue
Normal file
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<MainTemplate title="Schadensmeldungen">
|
||||
<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({
|
||||
data() {
|
||||
return {
|
||||
tabs: [
|
||||
{ route: "admin-unit-damage_report", title: "offen" },
|
||||
{ route: "admin-unit-damage_report-done", title: "bearbeitet" },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(useAbilityStore, ["can"]),
|
||||
},
|
||||
});
|
||||
</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