From 3e87bbc2679326b5ec74f818a24b756c4f5feb16 Mon Sep 17 00:00:00 2001
From: Julian Krauser <jkrauser209@gmail.com>
Date: Tue, 25 Mar 2025 10:42:40 +0100
Subject: [PATCH] base components on collections

---
 src/components/CodeDetector.vue               |   8 +-
 .../damageReport/DamageReportListItem.vue     |  34 ++++
 .../CreateRespiratoryGearModal.vue            | 157 ++++++++++++++++++
 .../RespiratoryGearListItem.vue               |  34 ++++
 .../CreateRespiratoryMissionModal.vue         | 157 ++++++++++++++++++
 .../RespiratoryMissionListItem.vue            |  34 ++++
 .../CreateRespiratoryWearerModal.vue          | 157 ++++++++++++++++++
 .../RespiratoryWearerListItem.vue             |  34 ++++
 src/helpers/codeScanner.ts                    |   6 +-
 src/router/index.ts                           |  11 +-
 .../admin/unit/damageReport/damageReport.ts   |  99 +++++++++++
 .../unit/respiratoryGear/respiratoryGear.ts   | 120 +++++++++++++
 .../respiratoryMission/respiratoryMission.ts  | 126 ++++++++++++++
 .../respiratoryWearer/respiratoryWearer.ts    | 126 ++++++++++++++
 .../unit/damageReport/damageReport.models.ts  |  39 +++++
 .../respiratoryGear/respiratoryGear.models.ts |  39 +++++
 .../respiratoryMission.models.ts              |  39 +++++
 .../respiratoryWearer.models.ts               |  39 +++++
 .../admin/unit/damageReport/DamageReport.vue  |  58 ++++++-
 .../unit/equipmentType/EquipmentType.vue      |   1 -
 .../unit/respiratoryGear/RespiratoryGear.vue  |  27 +--
 .../respiratoryMission/RespiratoryMission.vue |  29 ++--
 .../respiratoryWearer/RespiratoryWearer.vue   |  29 ++--
 src/views/admin/unit/vehicle/Vehicle.vue      |   1 -
 24 files changed, 1347 insertions(+), 57 deletions(-)
 create mode 100644 src/components/admin/unit/damageReport/DamageReportListItem.vue
 create mode 100644 src/components/admin/unit/respiratoryGear/CreateRespiratoryGearModal.vue
 create mode 100644 src/components/admin/unit/respiratoryGear/RespiratoryGearListItem.vue
 create mode 100644 src/components/admin/unit/respiratoryMission/CreateRespiratoryMissionModal.vue
 create mode 100644 src/components/admin/unit/respiratoryMission/RespiratoryMissionListItem.vue
 create mode 100644 src/components/admin/unit/respiratoryWearer/CreateRespiratoryWearerModal.vue
 create mode 100644 src/components/admin/unit/respiratoryWearer/RespiratoryWearerListItem.vue
 create mode 100644 src/stores/admin/unit/damageReport/damageReport.ts
 create mode 100644 src/stores/admin/unit/respiratoryGear/respiratoryGear.ts
 create mode 100644 src/stores/admin/unit/respiratoryMission/respiratoryMission.ts
 create mode 100644 src/stores/admin/unit/respiratoryWearer/respiratoryWearer.ts
 create mode 100644 src/viewmodels/admin/unit/damageReport/damageReport.models.ts
 create mode 100644 src/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models.ts
 create mode 100644 src/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models.ts
 create mode 100644 src/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models.ts

diff --git a/src/components/CodeDetector.vue b/src/components/CodeDetector.vue
index 1832f1a..373fc9d 100644
--- a/src/components/CodeDetector.vue
+++ b/src/components/CodeDetector.vue
@@ -2,7 +2,7 @@
   <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"
+      :constraints="selectedCamera?.constraints"
       :track="trackFunctionOptions[4].value"
       :formats="barcodeFormats"
       :paused="paused"
@@ -31,6 +31,7 @@ import {
   getAvailableCameras,
   handleScannerError,
   trackFunctionOptions,
+  type Camera,
 } from "../helpers/codeScanner";
 import { QrcodeStream, type DetectedBarcode } from "vue-qrcode-reader";
 import { XMarkIcon } from "@heroicons/vue/24/outline";
@@ -47,7 +48,7 @@ export default defineComponent({
   data() {
     return {
       selecteableCameras: defaultConstraintOptions,
-      selectedCamera: defaultConstraintOptions[0],
+      selectedCamera: undefined as undefined | Camera,
       paused: false,
       detected: "",
     };
@@ -56,6 +57,9 @@ export default defineComponent({
     ...mapActions(useModalStore, ["closeModal"]),
     async onCameraReady() {
       this.selecteableCameras = await getAvailableCameras();
+      if (!this.selectedCamera) {
+        this.selectedCamera = this.selecteableCameras[0];
+      }
     },
     onDetect(result: Array<DetectedBarcode>) {
       this.paused = true;
diff --git a/src/components/admin/unit/damageReport/DamageReportListItem.vue b/src/components/admin/unit/damageReport/DamageReportListItem.vue
new file mode 100644
index 0000000..d2515ae
--- /dev/null
+++ b/src/components/admin/unit/damageReport/DamageReportListItem.vue
@@ -0,0 +1,34 @@
+<template>
+  <RouterLink
+    :to="{ name: 'admin-unit-damageReport-overview', params: { damageReportId: damageReport.id } }"
+    class="flex flex-col h-fit w-full border border-primary rounded-md"
+  >
+    <div class="bg-primary p-2 text-white flex flex-row justify-between items-center">
+      <p>
+        {{ damageReport.lastname }}, {{ damageReport.firstname }}
+        {{ damageReport.nameaffix ? `- ${damageReport.nameaffix}` : "" }}
+      </p>
+    </div>
+    <div class="p-2">
+      <p v-if="damageReport.internalId">ID: {{ damageReport.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 { DamageReportViewModel } from "@/viewmodels/admin/unit/damageReport/damageReport.models";
+</script>
+
+<script lang="ts">
+export default defineComponent({
+  props: {
+    damageReport: { type: Object as PropType<DamageReportViewModel>, default: {} },
+  },
+  computed: {
+    ...mapState(useAbilityStore, ["can"]),
+  },
+});
+</script>
diff --git a/src/components/admin/unit/respiratoryGear/CreateRespiratoryGearModal.vue b/src/components/admin/unit/respiratoryGear/CreateRespiratoryGearModal.vue
new file mode 100644
index 0000000..4f934d7
--- /dev/null
+++ b/src/components/admin/unit/respiratoryGear/CreateRespiratoryGearModal.vue
@@ -0,0 +1,157 @@
+<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-sm 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-none 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-none 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 = {
+        salutationId: this.selectedSalutation.id,
+        firstname: formData.firstname.value,
+        lastname: formData.lastname.value,
+        nameaffix: formData.nameaffix.value,
+        birthdate: formData.birthdate.value,
+        internalId: formData.internalId.value,
+      };
+      this.status = "loading";
+      this.createEquipment(createEquipment)
+        .then(() => {
+          this.status = { status: "success" };
+          this.timeout = setTimeout(() => {
+            this.closeModal();
+          }, 1500);
+        })
+        .catch(() => {
+          this.status = { status: "failed" };
+        });
+    },
+  },
+});
+</script>
diff --git a/src/components/admin/unit/respiratoryGear/RespiratoryGearListItem.vue b/src/components/admin/unit/respiratoryGear/RespiratoryGearListItem.vue
new file mode 100644
index 0000000..9e2cb47
--- /dev/null
+++ b/src/components/admin/unit/respiratoryGear/RespiratoryGearListItem.vue
@@ -0,0 +1,34 @@
+<template>
+  <RouterLink
+    :to="{ name: 'admin-unit-respiratoryGear-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.lastname }}, {{ respiratoryGear.firstname }}
+        {{ respiratoryGear.nameaffix ? `- ${respiratoryGear.nameaffix}` : "" }}
+      </p>
+    </div>
+    <div class="p-2">
+      <p v-if="respiratoryGear.internalId">ID: {{ respiratoryGear.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 { RespiratoryGearViewModel } from "@/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models";
+</script>
+
+<script lang="ts">
+export default defineComponent({
+  props: {
+    respiratoryGear: { type: Object as PropType<RespiratoryGearViewModel>, default: {} },
+  },
+  computed: {
+    ...mapState(useAbilityStore, ["can"]),
+  },
+});
+</script>
diff --git a/src/components/admin/unit/respiratoryMission/CreateRespiratoryMissionModal.vue b/src/components/admin/unit/respiratoryMission/CreateRespiratoryMissionModal.vue
new file mode 100644
index 0000000..4f934d7
--- /dev/null
+++ b/src/components/admin/unit/respiratoryMission/CreateRespiratoryMissionModal.vue
@@ -0,0 +1,157 @@
+<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-sm 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-none 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-none 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 = {
+        salutationId: this.selectedSalutation.id,
+        firstname: formData.firstname.value,
+        lastname: formData.lastname.value,
+        nameaffix: formData.nameaffix.value,
+        birthdate: formData.birthdate.value,
+        internalId: formData.internalId.value,
+      };
+      this.status = "loading";
+      this.createEquipment(createEquipment)
+        .then(() => {
+          this.status = { status: "success" };
+          this.timeout = setTimeout(() => {
+            this.closeModal();
+          }, 1500);
+        })
+        .catch(() => {
+          this.status = { status: "failed" };
+        });
+    },
+  },
+});
+</script>
diff --git a/src/components/admin/unit/respiratoryMission/RespiratoryMissionListItem.vue b/src/components/admin/unit/respiratoryMission/RespiratoryMissionListItem.vue
new file mode 100644
index 0000000..324265a
--- /dev/null
+++ b/src/components/admin/unit/respiratoryMission/RespiratoryMissionListItem.vue
@@ -0,0 +1,34 @@
+<template>
+  <RouterLink
+    :to="{ name: 'admin-unit-respiratoryMission-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.lastname }}, {{ respiratoryMission.firstname }}
+        {{ respiratoryMission.nameaffix ? `- ${respiratoryMission.nameaffix}` : "" }}
+      </p>
+    </div>
+    <div class="p-2">
+      <p v-if="respiratoryMission.internalId">ID: {{ respiratoryMission.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 { RespiratoryMissionViewModel } from "@/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models";
+</script>
+
+<script lang="ts">
+export default defineComponent({
+  props: {
+    respiratoryMission: { type: Object as PropType<RespiratoryMissionViewModel>, default: {} },
+  },
+  computed: {
+    ...mapState(useAbilityStore, ["can"]),
+  },
+});
+</script>
diff --git a/src/components/admin/unit/respiratoryWearer/CreateRespiratoryWearerModal.vue b/src/components/admin/unit/respiratoryWearer/CreateRespiratoryWearerModal.vue
new file mode 100644
index 0000000..4f934d7
--- /dev/null
+++ b/src/components/admin/unit/respiratoryWearer/CreateRespiratoryWearerModal.vue
@@ -0,0 +1,157 @@
+<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-sm 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-none 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-none 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 = {
+        salutationId: this.selectedSalutation.id,
+        firstname: formData.firstname.value,
+        lastname: formData.lastname.value,
+        nameaffix: formData.nameaffix.value,
+        birthdate: formData.birthdate.value,
+        internalId: formData.internalId.value,
+      };
+      this.status = "loading";
+      this.createEquipment(createEquipment)
+        .then(() => {
+          this.status = { status: "success" };
+          this.timeout = setTimeout(() => {
+            this.closeModal();
+          }, 1500);
+        })
+        .catch(() => {
+          this.status = { status: "failed" };
+        });
+    },
+  },
+});
+</script>
diff --git a/src/components/admin/unit/respiratoryWearer/RespiratoryWearerListItem.vue b/src/components/admin/unit/respiratoryWearer/RespiratoryWearerListItem.vue
new file mode 100644
index 0000000..69d8628
--- /dev/null
+++ b/src/components/admin/unit/respiratoryWearer/RespiratoryWearerListItem.vue
@@ -0,0 +1,34 @@
+<template>
+  <RouterLink
+    :to="{ name: 'admin-unit-respiratoryWearer-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.lastname }}, {{ respiratoryWearer.firstname }}
+        {{ respiratoryWearer.nameaffix ? `- ${respiratoryWearer.nameaffix}` : "" }}
+      </p>
+    </div>
+    <div class="p-2">
+      <p v-if="respiratoryWearer.internalId">ID: {{ respiratoryWearer.internalId }}</p>
+    </div>
+  </RouterLink>
+</template>
+
+<script setup lang="ts">
+import { defineComponent, type PropType } from "vue";
+import { mapState, mapActions } from "pinia";
+import { useAbilityStore } from "@/stores/ability";
+import type { RespiratoryWearerViewModel } from "@/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models";
+</script>
+
+<script lang="ts">
+export default defineComponent({
+  props: {
+    respiratoryWearer: { type: Object as PropType<RespiratoryWearerViewModel>, default: {} },
+  },
+  computed: {
+    ...mapState(useAbilityStore, ["can"]),
+  },
+});
+</script>
diff --git a/src/helpers/codeScanner.ts b/src/helpers/codeScanner.ts
index bf3c026..6894088 100644
--- a/src/helpers/codeScanner.ts
+++ b/src/helpers/codeScanner.ts
@@ -13,7 +13,7 @@ export const defaultConstraintOptions: Array<Camera> = [
   { label: "front camera", constraints: { facingMode: "user" } },
 ];
 
-export async function getAvailableCameras(): Promise<Array<Camera>> {
+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
@@ -22,9 +22,9 @@ export async function getAvailableCameras(): Promise<Array<Camera>> {
   const videoDevices = devices.filter(({ kind }) => kind === "videoinput");
 
   return [
-    ...defaultConstraintOptions,
+    ...(useDefault ? defaultConstraintOptions : []),
     ...videoDevices.map(({ deviceId, label }) => ({
-      label: `${label} (ID: ${deviceId})`,
+      label: `${label}`, //(ID: ${deviceId})
       constraints: { deviceId, facingMode: "custom" },
     })),
   ];
diff --git a/src/router/index.ts b/src/router/index.ts
index b0d0cfb..e72d6a3 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -404,17 +404,10 @@ const router = createRouter({
             },
             {
               path: "equipment-type",
-              name: "admin-unit-equipment_type-route",
-              component: () => import("@/views/RouterView.vue"),
+              name: "admin-unit-equipment_type",
+              component: () => import("@/views/admin/unit/equipmentType/EquipmentType.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"),
-                },
-              ],
             },
           ],
         },
diff --git a/src/stores/admin/unit/damageReport/damageReport.ts b/src/stores/admin/unit/damageReport/damageReport.ts
new file mode 100644
index 0000000..aa761e7
--- /dev/null
+++ b/src/stores/admin/unit/damageReport/damageReport.ts
@@ -0,0 +1,99 @@
+import { defineStore } from "pinia";
+import type {
+  DamageReportViewModel,
+  CreateDamageReportViewModel,
+  UpdateDamageReportViewModel,
+} from "@/viewmodels/admin/unit/damageReport/damageReport.models";
+import { http } from "@/serverCom";
+import type { AxiosResponse } from "axios";
+
+export const useDamageReportStore = defineStore("damageReport", {
+  state: () => {
+    return {
+      damageReports: [] as Array<DamageReportViewModel & { tab_pos: number }>,
+      totalCount: 0 as number,
+      loading: "loading" as "loading" | "fetched" | "failed",
+      loadingActive: "loading" as "loading" | "fetched" | "failed",
+    };
+  },
+  actions: {
+    fetchDamageReports(offset = 0, count = 25, search = "", clear = false) {
+      if (clear) this.damageReports = [];
+      this.loading = "loading";
+      http
+        .get(`/admin/damageReport?offset=${offset}&count=${count}${search != "" ? "&search=" + search : ""}`)
+        .then((result) => {
+          this.totalCount = result.data.total;
+          result.data.damageReports
+            .filter((elem: DamageReportViewModel) => this.damageReports.findIndex((m) => m.id == elem.id) == -1)
+            .map((elem: DamageReportViewModel, index: number): DamageReportViewModel & { tab_pos: number } => {
+              return {
+                ...elem,
+                tab_pos: index + offset,
+              };
+            })
+            .forEach((elem: DamageReportViewModel & { tab_pos: number }) => {
+              this.damageReports.push(elem);
+            });
+          this.loading = "fetched";
+        })
+        .catch((err) => {
+          this.loading = "failed";
+        });
+    },
+    async getAllDamageReports(): Promise<AxiosResponse<any, any>> {
+      return await http.get(`/admin/damageReport?noLimit=true`).then((res) => {
+        return { ...res, data: res.data.damageReports };
+      });
+    },
+    async getDamageReportsByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
+      return await http
+        .post(`/admin/damageReport/ids`, {
+          ids,
+        })
+        .then((res) => {
+          return { ...res, data: res.data.damageReports };
+        });
+    },
+    async searchDamageReports(search: string): Promise<AxiosResponse<any, any>> {
+      return await http.get(`/admin/damageReport?search=${search}&noLimit=true`).then((res) => {
+        return { ...res, data: res.data.damageReports };
+      });
+    },
+    fetchDamageReportById(id: string) {
+      return http.get(`/admin/damageReport/${id}`);
+    },
+    fetchDamageReportStatisticsById(id: string) {
+      return http.get(`/admin/damageReport/${id}/statistics`);
+    },
+    async createDamageReport(damageReport: CreateDamageReportViewModel): Promise<AxiosResponse<any, any>> {
+      const result = await http.post(`/admin/damageReport`, {
+        salutationId: damageReport.salutationId,
+        firstname: damageReport.firstname,
+        lastname: damageReport.lastname,
+        nameaffix: damageReport.nameaffix,
+        birthdate: damageReport.birthdate,
+        internalId: damageReport.internalId,
+      });
+      this.fetchDamageReports();
+      return result;
+    },
+    async updateDamageReport(damageReport: UpdateDamageReportViewModel): Promise<AxiosResponse<any, any>> {
+      const result = await http.patch(`/admin/damageReport/${damageReport.id}`, {
+        salutationId: damageReport.salutationId,
+        firstname: damageReport.firstname,
+        lastname: damageReport.lastname,
+        nameaffix: damageReport.nameaffix,
+        birthdate: damageReport.birthdate,
+        internalId: damageReport.internalId,
+      });
+      this.fetchDamageReports();
+      return result;
+    },
+    async deleteDamageReport(damageReport: number): Promise<AxiosResponse<any, any>> {
+      const result = await http.delete(`/admin/damageReport/${damageReport}`);
+      this.fetchDamageReports();
+      return result;
+    },
+  },
+});
diff --git a/src/stores/admin/unit/respiratoryGear/respiratoryGear.ts b/src/stores/admin/unit/respiratoryGear/respiratoryGear.ts
new file mode 100644
index 0000000..94bdc98
--- /dev/null
+++ b/src/stores/admin/unit/respiratoryGear/respiratoryGear.ts
@@ -0,0 +1,120 @@
+import { defineStore } from "pinia";
+import type {
+  RespiratoryGearViewModel,
+  CreateRespiratoryGearViewModel,
+  UpdateRespiratoryGearViewModel,
+} from "@/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models";
+import { http } from "@/serverCom";
+import type { AxiosResponse } from "axios";
+
+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 getRespiratoryGearsByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
+      return await http
+        .post(`/admin/respiratoryGear/ids`, {
+          ids,
+        })
+        .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 printRespiratoryGearByActiveId() {
+      return http.get(`/admin/respiratoryGear/${this.activeRespiratoryGear}/print`, {
+        responseType: "blob",
+      });
+    },
+    fetchRespiratoryGearStatisticsById(id: string) {
+      return http.get(`/admin/respiratoryGear/${id}/statistics`);
+    },
+    async createRespiratoryGear(respiratoryGear: CreateRespiratoryGearViewModel): Promise<AxiosResponse<any, any>> {
+      const result = await http.post(`/admin/respiratoryGear`, {
+        salutationId: respiratoryGear.salutationId,
+        firstname: respiratoryGear.firstname,
+        lastname: respiratoryGear.lastname,
+        nameaffix: respiratoryGear.nameaffix,
+        birthdate: respiratoryGear.birthdate,
+        internalId: respiratoryGear.internalId,
+      });
+      this.fetchRespiratoryGears();
+      return result;
+    },
+    async updateActiveRespiratoryGear(
+      respiratoryGear: UpdateRespiratoryGearViewModel
+    ): Promise<AxiosResponse<any, any>> {
+      const result = await http.patch(`/admin/respiratoryGear/${respiratoryGear.id}`, {
+        salutationId: respiratoryGear.salutationId,
+        firstname: respiratoryGear.firstname,
+        lastname: respiratoryGear.lastname,
+        nameaffix: respiratoryGear.nameaffix,
+        birthdate: respiratoryGear.birthdate,
+        internalId: respiratoryGear.internalId,
+      });
+      this.fetchRespiratoryGears();
+      return result;
+    },
+    async deleteRespiratoryGear(respiratoryGear: number): Promise<AxiosResponse<any, any>> {
+      const result = await http.delete(`/admin/respiratoryGear/${respiratoryGear}`);
+      this.fetchRespiratoryGears();
+      return result;
+    },
+  },
+});
diff --git a/src/stores/admin/unit/respiratoryMission/respiratoryMission.ts b/src/stores/admin/unit/respiratoryMission/respiratoryMission.ts
new file mode 100644
index 0000000..712a843
--- /dev/null
+++ b/src/stores/admin/unit/respiratoryMission/respiratoryMission.ts
@@ -0,0 +1,126 @@
+import { defineStore } from "pinia";
+import type {
+  RespiratoryMissionViewModel,
+  CreateRespiratoryMissionViewModel,
+  UpdateRespiratoryMissionViewModel,
+} from "@/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models";
+import { http } from "@/serverCom";
+import type { AxiosResponse } from "axios";
+
+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 getRespiratoryMissionsByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
+      return await http
+        .post(`/admin/respiratoryMission/ids`, {
+          ids,
+        })
+        .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 printRespiratoryMissionByActiveId() {
+      return http.get(`/admin/respiratoryMission/${this.activeRespiratoryMission}/print`, {
+        responseType: "blob",
+      });
+    },
+    fetchRespiratoryMissionStatisticsById(id: string) {
+      return http.get(`/admin/respiratoryMission/${id}/statistics`);
+    },
+    async createRespiratoryMission(
+      respiratoryMission: CreateRespiratoryMissionViewModel
+    ): Promise<AxiosResponse<any, any>> {
+      const result = await http.post(`/admin/respiratoryMission`, {
+        salutationId: respiratoryMission.salutationId,
+        firstname: respiratoryMission.firstname,
+        lastname: respiratoryMission.lastname,
+        nameaffix: respiratoryMission.nameaffix,
+        birthdate: respiratoryMission.birthdate,
+        internalId: respiratoryMission.internalId,
+      });
+      this.fetchRespiratoryMissions();
+      return result;
+    },
+    async updateActiveRespiratoryMission(
+      respiratoryMission: UpdateRespiratoryMissionViewModel
+    ): Promise<AxiosResponse<any, any>> {
+      const result = await http.patch(`/admin/respiratoryMission/${respiratoryMission.id}`, {
+        salutationId: respiratoryMission.salutationId,
+        firstname: respiratoryMission.firstname,
+        lastname: respiratoryMission.lastname,
+        nameaffix: respiratoryMission.nameaffix,
+        birthdate: respiratoryMission.birthdate,
+        internalId: respiratoryMission.internalId,
+      });
+      this.fetchRespiratoryMissions();
+      return result;
+    },
+    async deleteRespiratoryMission(respiratoryMission: number): Promise<AxiosResponse<any, any>> {
+      const result = await http.delete(`/admin/respiratoryMission/${respiratoryMission}`);
+      this.fetchRespiratoryMissions();
+      return result;
+    },
+  },
+});
diff --git a/src/stores/admin/unit/respiratoryWearer/respiratoryWearer.ts b/src/stores/admin/unit/respiratoryWearer/respiratoryWearer.ts
new file mode 100644
index 0000000..18bd220
--- /dev/null
+++ b/src/stores/admin/unit/respiratoryWearer/respiratoryWearer.ts
@@ -0,0 +1,126 @@
+import { defineStore } from "pinia";
+import type {
+  RespiratoryWearerViewModel,
+  CreateRespiratoryWearerViewModel,
+  UpdateRespiratoryWearerViewModel,
+} from "@/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models";
+import { http } from "@/serverCom";
+import type { AxiosResponse } from "axios";
+
+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 getRespiratoryWearersByIds(ids: Array<string>): Promise<AxiosResponse<any, any>> {
+      return await http
+        .post(`/admin/respiratoryWearer/ids`, {
+          ids,
+        })
+        .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 printRespiratoryWearerByActiveId() {
+      return http.get(`/admin/respiratoryWearer/${this.activeRespiratoryWearer}/print`, {
+        responseType: "blob",
+      });
+    },
+    fetchRespiratoryWearerStatisticsById(id: string) {
+      return http.get(`/admin/respiratoryWearer/${id}/statistics`);
+    },
+    async createRespiratoryWearer(
+      respiratoryWearer: CreateRespiratoryWearerViewModel
+    ): Promise<AxiosResponse<any, any>> {
+      const result = await http.post(`/admin/respiratoryWearer`, {
+        salutationId: respiratoryWearer.salutationId,
+        firstname: respiratoryWearer.firstname,
+        lastname: respiratoryWearer.lastname,
+        nameaffix: respiratoryWearer.nameaffix,
+        birthdate: respiratoryWearer.birthdate,
+        internalId: respiratoryWearer.internalId,
+      });
+      this.fetchRespiratoryWearers();
+      return result;
+    },
+    async updateActiveRespiratoryWearer(
+      respiratoryWearer: UpdateRespiratoryWearerViewModel
+    ): Promise<AxiosResponse<any, any>> {
+      const result = await http.patch(`/admin/respiratoryWearer/${respiratoryWearer.id}`, {
+        salutationId: respiratoryWearer.salutationId,
+        firstname: respiratoryWearer.firstname,
+        lastname: respiratoryWearer.lastname,
+        nameaffix: respiratoryWearer.nameaffix,
+        birthdate: respiratoryWearer.birthdate,
+        internalId: respiratoryWearer.internalId,
+      });
+      this.fetchRespiratoryWearers();
+      return result;
+    },
+    async deleteRespiratoryWearer(respiratoryWearer: number): Promise<AxiosResponse<any, any>> {
+      const result = await http.delete(`/admin/respiratoryWearer/${respiratoryWearer}`);
+      this.fetchRespiratoryWearers();
+      return result;
+    },
+  },
+});
diff --git a/src/viewmodels/admin/unit/damageReport/damageReport.models.ts b/src/viewmodels/admin/unit/damageReport/damageReport.models.ts
new file mode 100644
index 0000000..7551f19
--- /dev/null
+++ b/src/viewmodels/admin/unit/damageReport/damageReport.models.ts
@@ -0,0 +1,39 @@
+export interface DamageReportViewModel {
+  id: string;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
+
+export interface DamageReportStatisticsViewModel {
+  id: string;
+  salutation: string;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  todayAge: number;
+  ageThisYear: number;
+  exactAge: string;
+}
+
+export interface CreateDamageReportViewModel {
+  salutationId: number;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
+
+export interface UpdateDamageReportViewModel {
+  id: string;
+  salutationId: number;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
diff --git a/src/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models.ts b/src/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models.ts
new file mode 100644
index 0000000..b37c28b
--- /dev/null
+++ b/src/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models.ts
@@ -0,0 +1,39 @@
+export interface RespiratoryGearViewModel {
+  id: string;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
+
+export interface RespiratoryGearStatisticsViewModel {
+  id: string;
+  salutation: string;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  todayAge: number;
+  ageThisYear: number;
+  exactAge: string;
+}
+
+export interface CreateRespiratoryGearViewModel {
+  salutationId: number;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
+
+export interface UpdateRespiratoryGearViewModel {
+  id: string;
+  salutationId: number;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
diff --git a/src/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models.ts b/src/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models.ts
new file mode 100644
index 0000000..2a19bae
--- /dev/null
+++ b/src/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models.ts
@@ -0,0 +1,39 @@
+export interface RespiratoryMissionViewModel {
+  id: string;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
+
+export interface RespiratoryMissionStatisticsViewModel {
+  id: string;
+  salutation: string;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  todayAge: number;
+  ageThisYear: number;
+  exactAge: string;
+}
+
+export interface CreateRespiratoryMissionViewModel {
+  salutationId: number;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
+
+export interface UpdateRespiratoryMissionViewModel {
+  id: string;
+  salutationId: number;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
diff --git a/src/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models.ts b/src/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models.ts
new file mode 100644
index 0000000..113ee71
--- /dev/null
+++ b/src/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models.ts
@@ -0,0 +1,39 @@
+export interface RespiratoryWearerViewModel {
+  id: string;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
+
+export interface RespiratoryWearerStatisticsViewModel {
+  id: string;
+  salutation: string;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  todayAge: number;
+  ageThisYear: number;
+  exactAge: string;
+}
+
+export interface CreateRespiratoryWearerViewModel {
+  salutationId: number;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
+
+export interface UpdateRespiratoryWearerViewModel {
+  id: string;
+  salutationId: number;
+  firstname: string;
+  lastname: string;
+  nameaffix: string;
+  birthdate: Date;
+  internalId?: string;
+}
diff --git a/src/views/admin/unit/damageReport/DamageReport.vue b/src/views/admin/unit/damageReport/DamageReport.vue
index 8d99d16..99aabac 100644
--- a/src/views/admin/unit/damageReport/DamageReport.vue
+++ b/src/views/admin/unit/damageReport/DamageReport.vue
@@ -6,7 +6,38 @@
       </div>
     </template>
     <template #diffMain>
-      <div class="flex flex-col w-full h-full gap-2 justify-center px-7"></div>
+      <div class="flex flex-col w-full h-full gap-2 justify-center px-7">
+        <div class="w-full flex flex-row max-lg:flex-wrap justify-center">
+          <div
+            v-for="tab in tabs"
+            :key="tab.route"
+            @click="isActive = tab.route"
+            class="w-1/2 md:w-1/3 lg:w-full p-0.5 first:pl-0 last:pr-0 cursor-pointer"
+          >
+            <p
+              :class="[
+                'w-full rounded-lg py-2.5 text-sm text-center font-medium leading-5 focus:ring-0 outline-none',
+                isActive == tab.route
+                  ? 'bg-red-200 shadow border-b-2 border-primary rounded-b-none'
+                  : ' hover:bg-red-200',
+              ]"
+            >
+              {{ tab.title }}
+            </p>
+          </div>
+        </div>
+        <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>
   </MainTemplate>
 </template>
@@ -15,12 +46,35 @@
 import { defineComponent } from "vue";
 import { mapActions, mapState } from "pinia";
 import MainTemplate from "@/templates/Main.vue";
+import { useAbilityStore } from "@/stores/ability";
+import { useDamageReportStore } from "@/stores/admin/unit/damageReport/damageReport";
+import type { DamageReportViewModel } from "@/viewmodels/admin/unit/damageReport/damageReport.models";
+import Pagination from "@/components/Pagination.vue";
+import DamageReportListItem from "../../../../components/admin/unit/damageReport/DamageReportListItem.vue";
 </script>
 
 <script lang="ts">
 export default defineComponent({
   data() {
-    return {};
+    return {
+      tabs: [
+        { route: "overview", title: "offen" },
+        { route: "membership", title: "bearbeitet" },
+      ],
+      isActive: "overview",
+      currentPage: 0,
+      maxEntriesPerPage: 25,
+    };
+  },
+  computed: {
+    ...mapState(useDamageReportStore, ["damageReports", "totalCount", "loading"]),
+    ...mapState(useAbilityStore, ["can"]),
+  },
+  mounted() {
+    this.fetchDamageReports(0, this.maxEntriesPerPage, "", true);
+  },
+  methods: {
+    ...mapActions(useDamageReportStore, ["fetchDamageReports"]),
   },
 });
 </script>
diff --git a/src/views/admin/unit/equipmentType/EquipmentType.vue b/src/views/admin/unit/equipmentType/EquipmentType.vue
index 3c09340..921ee77 100644
--- a/src/views/admin/unit/equipmentType/EquipmentType.vue
+++ b/src/views/admin/unit/equipmentType/EquipmentType.vue
@@ -12,7 +12,6 @@
           :totalCount="totalCount"
           :indicateLoading="loading == 'loading'"
           useSearch
-          useScanner
           @load-data="(offset, count, search) => fetchEquipmentTypes(offset, count, search)"
           @search="(search) => fetchEquipmentTypes(0, maxEntriesPerPage, search, true)"
         >
diff --git a/src/views/admin/unit/respiratoryGear/RespiratoryGear.vue b/src/views/admin/unit/respiratoryGear/RespiratoryGear.vue
index c1531cc..ebf2f4f 100644
--- a/src/views/admin/unit/respiratoryGear/RespiratoryGear.vue
+++ b/src/views/admin/unit/respiratoryGear/RespiratoryGear.vue
@@ -8,16 +8,15 @@
     <template #diffMain>
       <div class="flex flex-col w-full h-full gap-2 justify-center px-7">
         <Pagination
-          :items="equipments"
+          :items="respiratoryGears"
           :totalCount="totalCount"
           :indicateLoading="loading == 'loading'"
           useSearch
-          useScanner
-          @load-data="(offset, count, search) => fetchEquipments(offset, count, search)"
-          @search="(search) => fetchEquipments(0, maxEntriesPerPage, search, true)"
+          @load-data="(offset, count, search) => fetchRespiratoryGears(offset, count, search)"
+          @search="(search) => fetchRespiratoryGears(0, maxEntriesPerPage, search, true)"
         >
-          <template #pageRow="{ row }: { row: EquipmentViewModel }">
-            <EquipmentListItem :equipment="row" />
+          <template #pageRow="{ row }: { row: RespiratoryGearViewModel }">
+            <RespiratoryGearListItem :respiratoryGear="row" />
           </template>
         </Pagination>
 
@@ -35,12 +34,12 @@
 import { defineAsyncComponent, defineComponent, markRaw } from "vue";
 import { mapActions, mapState } from "pinia";
 import MainTemplate from "@/templates/Main.vue";
-import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
+import { useRespiratoryGearStore } from "@/stores/admin/unit/respiratoryGear/respiratoryGear";
 import { useModalStore } from "@/stores/modal";
 import Pagination from "@/components/Pagination.vue";
 import { useAbilityStore } from "@/stores/ability";
-import type { EquipmentViewModel } from "../../../../viewmodels/admin/unit/equipment/equipment.models";
-import EquipmentListItem from "../../../../components/admin/unit/equipment/EquipmentListItem.vue";
+import type { RespiratoryGearViewModel } from "@/viewmodels/admin/unit/respiratoryGear/respiratoryGear.models";
+import RespiratoryGearListItem from "@/components/admin/unit/respiratoryGear/RespiratoryGearListItem.vue";
 </script>
 
 <script lang="ts">
@@ -52,18 +51,20 @@ export default defineComponent({
     };
   },
   computed: {
-    ...mapState(useEquipmentStore, ["equipments", "totalCount", "loading"]),
+    ...mapState(useRespiratoryGearStore, ["respiratoryGears", "totalCount", "loading"]),
     ...mapState(useAbilityStore, ["can"]),
   },
   mounted() {
-    this.fetchEquipments(0, this.maxEntriesPerPage, "", true);
+    this.fetchRespiratoryGears(0, this.maxEntriesPerPage, "", true);
   },
   methods: {
-    ...mapActions(useEquipmentStore, ["fetchEquipments"]),
+    ...mapActions(useRespiratoryGearStore, ["fetchRespiratoryGears"]),
     ...mapActions(useModalStore, ["openModal"]),
     openCreateModal() {
       this.openModal(
-        markRaw(defineAsyncComponent(() => import("@/components/admin/unit/equipment/CreateEquipmentModal.vue")))
+        markRaw(
+          defineAsyncComponent(() => import("@/components/admin/unit/respiratoryGear/CreateRespiratoryGearModal.vue"))
+        )
       );
     },
   },
diff --git a/src/views/admin/unit/respiratoryMission/RespiratoryMission.vue b/src/views/admin/unit/respiratoryMission/RespiratoryMission.vue
index 0d2d5db..9ed1c0b 100644
--- a/src/views/admin/unit/respiratoryMission/RespiratoryMission.vue
+++ b/src/views/admin/unit/respiratoryMission/RespiratoryMission.vue
@@ -8,16 +8,15 @@
     <template #diffMain>
       <div class="flex flex-col w-full h-full gap-2 justify-center px-7">
         <Pagination
-          :items="equipments"
+          :items="respiratoryMissions"
           :totalCount="totalCount"
           :indicateLoading="loading == 'loading'"
           useSearch
-          useScanner
-          @load-data="(offset, count, search) => fetchEquipments(offset, count, search)"
-          @search="(search) => fetchEquipments(0, maxEntriesPerPage, search, true)"
+          @load-data="(offset, count, search) => fetchRespiratoryMissions(offset, count, search)"
+          @search="(search) => fetchRespiratoryMissions(0, maxEntriesPerPage, search, true)"
         >
-          <template #pageRow="{ row }: { row: EquipmentViewModel }">
-            <EquipmentListItem :equipment="row" />
+          <template #pageRow="{ row }: { row: RespiratoryMissionViewModel }">
+            <RespiratoryMissionListItem :respiratoryMission="row" />
           </template>
         </Pagination>
 
@@ -35,12 +34,12 @@
 import { defineAsyncComponent, defineComponent, markRaw } from "vue";
 import { mapActions, mapState } from "pinia";
 import MainTemplate from "@/templates/Main.vue";
-import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
+import { useRespiratoryMissionStore } from "@/stores/admin/unit/respiratoryMission/respiratoryMission";
 import { useModalStore } from "@/stores/modal";
 import Pagination from "@/components/Pagination.vue";
 import { useAbilityStore } from "@/stores/ability";
-import type { EquipmentViewModel } from "../../../../viewmodels/admin/unit/equipment/equipment.models";
-import EquipmentListItem from "../../../../components/admin/unit/equipment/EquipmentListItem.vue";
+import type { RespiratoryMissionViewModel } from "@/viewmodels/admin/unit/respiratoryMission/respiratoryMission.models";
+import RespiratoryMissionListItem from "@/components/admin/unit/respiratoryMission/RespiratoryMissionListItem.vue";
 </script>
 
 <script lang="ts">
@@ -52,18 +51,22 @@ export default defineComponent({
     };
   },
   computed: {
-    ...mapState(useEquipmentStore, ["equipments", "totalCount", "loading"]),
+    ...mapState(useRespiratoryMissionStore, ["respiratoryMissions", "totalCount", "loading"]),
     ...mapState(useAbilityStore, ["can"]),
   },
   mounted() {
-    this.fetchEquipments(0, this.maxEntriesPerPage, "", true);
+    this.fetchRespiratoryMissions(0, this.maxEntriesPerPage, "", true);
   },
   methods: {
-    ...mapActions(useEquipmentStore, ["fetchEquipments"]),
+    ...mapActions(useRespiratoryMissionStore, ["fetchRespiratoryMissions"]),
     ...mapActions(useModalStore, ["openModal"]),
     openCreateModal() {
       this.openModal(
-        markRaw(defineAsyncComponent(() => import("@/components/admin/unit/equipment/CreateEquipmentModal.vue")))
+        markRaw(
+          defineAsyncComponent(
+            () => import("@/components/admin/unit/respiratoryMission/CreateRespiratoryMissionModal.vue")
+          )
+        )
       );
     },
   },
diff --git a/src/views/admin/unit/respiratoryWearer/RespiratoryWearer.vue b/src/views/admin/unit/respiratoryWearer/RespiratoryWearer.vue
index c7d4112..30f48d2 100644
--- a/src/views/admin/unit/respiratoryWearer/RespiratoryWearer.vue
+++ b/src/views/admin/unit/respiratoryWearer/RespiratoryWearer.vue
@@ -8,16 +8,15 @@
     <template #diffMain>
       <div class="flex flex-col w-full h-full gap-2 justify-center px-7">
         <Pagination
-          :items="equipments"
+          :items="respiratoryWearers"
           :totalCount="totalCount"
           :indicateLoading="loading == 'loading'"
           useSearch
-          useScanner
-          @load-data="(offset, count, search) => fetchEquipments(offset, count, search)"
-          @search="(search) => fetchEquipments(0, maxEntriesPerPage, search, true)"
+          @load-data="(offset, count, search) => fetchRespiratoryWearers(offset, count, search)"
+          @search="(search) => fetchRespiratoryWearers(0, maxEntriesPerPage, search, true)"
         >
-          <template #pageRow="{ row }: { row: EquipmentViewModel }">
-            <EquipmentListItem :equipment="row" />
+          <template #pageRow="{ row }: { row: RespiratoryWearerViewModel }">
+            <RespiratoryWearerListItem :respiratoryWearer="row" />
           </template>
         </Pagination>
 
@@ -35,12 +34,12 @@
 import { defineAsyncComponent, defineComponent, markRaw } from "vue";
 import { mapActions, mapState } from "pinia";
 import MainTemplate from "@/templates/Main.vue";
-import { useEquipmentStore } from "@/stores/admin/unit/equipment/equipment";
+import { useRespiratoryWearerStore } from "@/stores/admin/unit/respiratoryWearer/respiratoryWearer";
 import { useModalStore } from "@/stores/modal";
 import Pagination from "@/components/Pagination.vue";
 import { useAbilityStore } from "@/stores/ability";
-import type { EquipmentViewModel } from "../../../../viewmodels/admin/unit/equipment/equipment.models";
-import EquipmentListItem from "../../../../components/admin/unit/equipment/EquipmentListItem.vue";
+import type { RespiratoryWearerViewModel } from "@/viewmodels/admin/unit/respiratoryWearer/respiratoryWearer.models";
+import RespiratoryWearerListItem from "@/components/admin/unit/respiratoryWearer/RespiratoryWearerListItem.vue";
 </script>
 
 <script lang="ts">
@@ -52,18 +51,22 @@ export default defineComponent({
     };
   },
   computed: {
-    ...mapState(useEquipmentStore, ["equipments", "totalCount", "loading"]),
+    ...mapState(useRespiratoryWearerStore, ["respiratoryWearers", "totalCount", "loading"]),
     ...mapState(useAbilityStore, ["can"]),
   },
   mounted() {
-    this.fetchEquipments(0, this.maxEntriesPerPage, "", true);
+    this.fetchRespiratoryWearers(0, this.maxEntriesPerPage, "", true);
   },
   methods: {
-    ...mapActions(useEquipmentStore, ["fetchEquipments"]),
+    ...mapActions(useRespiratoryWearerStore, ["fetchRespiratoryWearers"]),
     ...mapActions(useModalStore, ["openModal"]),
     openCreateModal() {
       this.openModal(
-        markRaw(defineAsyncComponent(() => import("@/components/admin/unit/equipment/CreateEquipmentModal.vue")))
+        markRaw(
+          defineAsyncComponent(
+            () => import("@/components/admin/unit/respiratoryWearer/CreateRespiratoryWearerModal.vue")
+          )
+        )
       );
     },
   },
diff --git a/src/views/admin/unit/vehicle/Vehicle.vue b/src/views/admin/unit/vehicle/Vehicle.vue
index 538e2db..3998025 100644
--- a/src/views/admin/unit/vehicle/Vehicle.vue
+++ b/src/views/admin/unit/vehicle/Vehicle.vue
@@ -12,7 +12,6 @@
           :totalCount="totalCount"
           :indicateLoading="loading == 'loading'"
           useSearch
-          useScanner
           @load-data="(offset, count, search) => fetchVehicles(offset, count, search)"
           @search="(search) => fetchVehicles(0, maxEntriesPerPage, search, true)"
         >