force management
This commit is contained in:
parent
f50dff99f3
commit
8353eca4a2
14 changed files with 453 additions and 30 deletions
|
@ -5,6 +5,10 @@
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<form class="flex flex-col gap-4 py-2" @submit.prevent="triggerCreate">
|
<form class="flex flex-col gap-4 py-2" @submit.prevent="triggerCreate">
|
||||||
|
<div>
|
||||||
|
<label for="internalId">Interne Id (optional)</label>
|
||||||
|
<input type="text" id="internalId" />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="firstname">Vorname</label>
|
<label for="firstname">Vorname</label>
|
||||||
<input type="text" id="firstname" required />
|
<input type="text" id="firstname" required />
|
||||||
|
@ -17,6 +21,14 @@
|
||||||
<label for="nameaffix">Nameaffix (optional)</label>
|
<label for="nameaffix">Nameaffix (optional)</label>
|
||||||
<input type="text" id="nameaffix" />
|
<input type="text" id="nameaffix" />
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="commissioned">verfügbar ab (optional)</label>
|
||||||
|
<input type="date" id="commissioned" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="decommissioned">verfügbar bis (optional)</label>
|
||||||
|
<input type="date" id="decommissioned" />
|
||||||
|
</div>
|
||||||
<div class="flex flex-row gap-2">
|
<div class="flex flex-row gap-2">
|
||||||
<button primary type="submit" :disabled="status == 'loading' || status?.status == 'success'">erstellen</button>
|
<button primary type="submit" :disabled="status == 'loading' || status?.status == 'success'">erstellen</button>
|
||||||
<Spinner v-if="status == 'loading'" class="my-auto" />
|
<Spinner v-if="status == 'loading'" class="my-auto" />
|
||||||
|
@ -67,9 +79,12 @@ export default defineComponent({
|
||||||
triggerCreate(e: any) {
|
triggerCreate(e: any) {
|
||||||
let formData = e.target.elements;
|
let formData = e.target.elements;
|
||||||
let createForce: CreateForceViewModel = {
|
let createForce: CreateForceViewModel = {
|
||||||
|
internalId: formData.internalId.value,
|
||||||
firstname: formData.firstname.value,
|
firstname: formData.firstname.value,
|
||||||
lastname: formData.lastname.value,
|
lastname: formData.lastname.value,
|
||||||
nameaffix: formData.nameaffix.value,
|
nameaffix: formData.nameaffix.value,
|
||||||
|
commissioned: formData.commissioned.value,
|
||||||
|
decommissioned: formData.decommissioned.value,
|
||||||
};
|
};
|
||||||
this.status = "loading";
|
this.status = "loading";
|
||||||
this.createForce(createForce)
|
this.createForce(createForce)
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full md:max-w-md">
|
<div class="w-full md:max-w-md">
|
||||||
<div class="flex flex-col items-center">
|
<div class="flex flex-col items-center">
|
||||||
<p class="text-xl font-medium">Mitglied löschen</p>
|
<p class="text-xl font-medium">Kraft löschen</p>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<p class="text-center">
|
<p class="text-center">
|
||||||
Mitglied {{ force?.lastname }}, {{ force?.firstname }}
|
Kraft {{ force?.lastname }}, {{ force?.firstname }} {{ force?.nameaffix ? `- ${force.nameaffix}` : "" }} löschen?
|
||||||
{{ force?.nameaffix ? `- ${force.nameaffix}` : "" }} löschen?
|
|
||||||
</p>
|
</p>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
@ -74,7 +73,7 @@ export default defineComponent({
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.status = { status: "success" };
|
this.status = { status: "success" };
|
||||||
this.timeout = setTimeout(() => {
|
this.timeout = setTimeout(() => {
|
||||||
this.$router.push({ name: "admin-club-force" });
|
this.$router.push({ name: "admin-configuration-force" });
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
}, 1500);
|
}, 1500);
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,22 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<RouterLink
|
<div class="flex flex-col h-fit w-full border border-primary rounded-md">
|
||||||
:to="{ name: 'admin-club-force-overview', params: { forceId: force.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">
|
<div class="bg-primary p-2 text-white flex flex-row justify-between items-center">
|
||||||
<p>{{ force.lastname }}, {{ force.firstname }} {{ force.nameaffix ? `- ${force.nameaffix}` : "" }}</p>
|
<p>{{ force.lastname }}, {{ force.firstname }} {{ force.nameaffix ? `- ${force.nameaffix}` : "" }}</p>
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<div v-if="can('update', 'configuration', 'force')" @click="openUpdateModal">
|
||||||
|
<PencilIcon class="w-5 h-5 p-1 box-content cursor-pointer" />
|
||||||
|
</div>
|
||||||
|
<div v-if="can('delete', 'configuration', 'force')" @click="openDeleteModal">
|
||||||
|
<TrashIcon class="w-5 h-5 p-1 box-content cursor-pointer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<p>Daten</p>
|
<p>verfügbar ab: {{ force.commissioned }}</p>
|
||||||
|
<p>verfügbar bis: {{ force?.decommissioned ?? "---" }}</p>
|
||||||
</div>
|
</div>
|
||||||
</RouterLink>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineComponent, type PropType } from "vue";
|
import { defineAsyncComponent, defineComponent, markRaw, type PropType } from "vue";
|
||||||
import { mapState, mapActions } from "pinia";
|
import { mapState, mapActions } from "pinia";
|
||||||
import { useAbilityStore } from "@/stores/ability";
|
import { useAbilityStore } from "@/stores/ability";
|
||||||
import type { ForceViewModel } from "@/viewmodels/admin/configuration/force.models";
|
import type { ForceViewModel } from "@/viewmodels/admin/configuration/force.models";
|
||||||
|
import { useModalStore } from "../../../../stores/modal";
|
||||||
|
import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -27,5 +35,20 @@ export default defineComponent({
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(useAbilityStore, ["can"]),
|
...mapState(useAbilityStore, ["can"]),
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["openModal"]),
|
||||||
|
openUpdateModal() {
|
||||||
|
this.openModal(
|
||||||
|
markRaw(defineAsyncComponent(() => import("@/components/admin/configuration/force/UpdateForceModal.vue"))),
|
||||||
|
this.force.id
|
||||||
|
);
|
||||||
|
},
|
||||||
|
openDeleteModal() {
|
||||||
|
this.openModal(
|
||||||
|
markRaw(defineAsyncComponent(() => import("@/components/admin/configuration/force/DeleteForceModal.vue"))),
|
||||||
|
this.force.id
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
145
src/components/admin/configuration/force/UpdateForceModal.vue
Normal file
145
src/components/admin/configuration/force/UpdateForceModal.vue
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full md:max-w-md">
|
||||||
|
<div class="flex flex-col items-center">
|
||||||
|
<p class="text-xl font-medium">Kraft aktualisieren</p>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<Spinner v-if="loading == 'loading' && force == null" class="mx-auto" />
|
||||||
|
<p v-else-if="loading == 'failed'" @click="fetchItem" class="cursor-pointer">↺ laden fehlgeschlagen</p>
|
||||||
|
<form v-if="force" class="flex flex-col gap-4 py-2" @submit.prevent="triggerUpdate">
|
||||||
|
<div>
|
||||||
|
<label for="internalId">Interne Id (optional)</label>
|
||||||
|
<input type="text" id="internalId" v-model="force.internalId" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="firstname">Vorname</label>
|
||||||
|
<input type="text" id="firstname" required v-model="force.firstname" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="lastname">Nachname</label>
|
||||||
|
<input type="text" id="lastname" required v-model="force.lastname" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="nameaffix">Nameaffix (optional)</label>
|
||||||
|
<input type="text" id="nameaffix" v-model="force.nameaffix" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="commissioned">verfügbar ab (optional)</label>
|
||||||
|
<input type="date" id="commissioned" v-model="force.commissioned" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="decommissioned">verfügbar bis (optional)</label>
|
||||||
|
<input type="date" id="decommissioned" v-model="force.decommissioned" />
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row gap-2">
|
||||||
|
<button primary-outline type="reset" :disabled="canSaveOrReset" @click="resetForm">verwerfen</button>
|
||||||
|
<button primary type="submit" :disabled="status == 'loading' || canSaveOrReset">speichern</button>
|
||||||
|
<Spinner v-if="status == 'loading'" class="my-auto" />
|
||||||
|
<SuccessCheckmark v-else-if="status?.status == 'success'" />
|
||||||
|
<FailureXMark v-else-if="status?.status == 'failed'" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div 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'">
|
||||||
|
schließen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import { mapState, mapActions } from "pinia";
|
||||||
|
import { useModalStore } from "@/stores/modal";
|
||||||
|
import 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 { useForceStore } from "@/stores/admin/configuration/forces";
|
||||||
|
import type {
|
||||||
|
CreateForceViewModel,
|
||||||
|
ForceViewModel,
|
||||||
|
UpdateForceViewModel,
|
||||||
|
} from "@/viewmodels/admin/configuration/force.models";
|
||||||
|
import isEqual from "lodash.isequal";
|
||||||
|
import cloneDeep from "lodash.clonedeep";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
data: { type: String, default: "" },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: "loading" as "loading" | "fetched" | "failed",
|
||||||
|
status: null as null | "loading" | { status: "success" | "failed"; reason?: string },
|
||||||
|
force: null as null | ForceViewModel,
|
||||||
|
origin: null as null | ForceViewModel,
|
||||||
|
timeout: null as any,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
canSaveOrReset(): boolean {
|
||||||
|
return isEqual(this.origin, this.force);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchItem();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
try {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
} catch (error) {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useModalStore, ["closeModal"]),
|
||||||
|
...mapActions(useForceStore, ["updateForce", "fetchForceById"]),
|
||||||
|
resetForm() {
|
||||||
|
this.force = cloneDeep(this.origin);
|
||||||
|
},
|
||||||
|
fetchItem() {
|
||||||
|
this.loading = "loading";
|
||||||
|
this.fetchForceById(this.data)
|
||||||
|
.then((res) => {
|
||||||
|
this.loading = "fetched";
|
||||||
|
this.origin = res.data;
|
||||||
|
this.force = cloneDeep(this.origin);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.loading = "failed";
|
||||||
|
});
|
||||||
|
},
|
||||||
|
triggerUpdate(e: any) {
|
||||||
|
let formData = e.target.elements;
|
||||||
|
let updateForce: UpdateForceViewModel = {
|
||||||
|
id: this.data,
|
||||||
|
internalId: formData.internalId.value,
|
||||||
|
firstname: formData.firstname.value,
|
||||||
|
lastname: formData.lastname.value,
|
||||||
|
nameaffix: formData.nameaffix.value,
|
||||||
|
commissioned: formData.commissioned.value,
|
||||||
|
decommissioned: formData.decommissioned.value,
|
||||||
|
};
|
||||||
|
this.status = "loading";
|
||||||
|
this.updateForce(updateForce)
|
||||||
|
.then(() => {
|
||||||
|
this.fetchItem();
|
||||||
|
this.status = { status: "success" };
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.status = { status: "failed" };
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.timeout = setTimeout(() => {
|
||||||
|
this.status = null;
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -119,10 +119,24 @@ const router = createRouter({
|
||||||
{
|
{
|
||||||
path: "force",
|
path: "force",
|
||||||
name: "admin-configuration-force",
|
name: "admin-configuration-force",
|
||||||
component: () => import("@/views/admin/ViewSelect.vue"),
|
component: () => import("@/views/admin/configuration/force/Force.vue"),
|
||||||
meta: { type: "read", section: "configuration", module: "force" },
|
meta: { type: "read", section: "configuration", module: "force" },
|
||||||
beforeEnter: [abilityAndNavUpdate],
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "equipment",
|
||||||
|
name: "admin-configuration-equipment",
|
||||||
|
component: () => import("@/views/admin/configuration/force/Force.vue"),
|
||||||
|
meta: { type: "read", section: "configuration", module: "equipment" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "vehicle",
|
||||||
|
name: "admin-configuration-vehicle",
|
||||||
|
component: () => import("@/views/admin/configuration/force/Force.vue"),
|
||||||
|
meta: { type: "read", section: "configuration", module: "vehicle" },
|
||||||
|
beforeEnter: [abilityAndNavUpdate],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
81
src/stores/admin/configuration/equipment.ts
Normal file
81
src/stores/admin/configuration/equipment.ts
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import type {
|
||||||
|
EquipmentViewModel,
|
||||||
|
CreateEquipmentViewModel,
|
||||||
|
UpdateEquipmentViewModel,
|
||||||
|
} from "../../../viewmodels/admin/configuration/equipment.models";
|
||||||
|
|
||||||
|
export const useEquipmentStore = defineStore("equipment", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
equipments: [] as Array<EquipmentViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "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 };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchEquipmentById(id: string) {
|
||||||
|
return http.get(`/admin/equipment/${id}`);
|
||||||
|
},
|
||||||
|
async createEquipment(equipment: CreateEquipmentViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/equipment`, equipment);
|
||||||
|
this.fetchEquipments();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveEquipment(equipment: UpdateEquipmentViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/equipment/${equipment.id}`, equipment);
|
||||||
|
this.fetchEquipments();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteEquipment(equipment: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/equipment/${equipment}`);
|
||||||
|
this.fetchEquipments();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -63,20 +63,12 @@ export const useForceStore = defineStore("force", {
|
||||||
return http.get(`/admin/force/${id}`);
|
return http.get(`/admin/force/${id}`);
|
||||||
},
|
},
|
||||||
async createForce(force: CreateForceViewModel): Promise<AxiosResponse<any, any>> {
|
async createForce(force: CreateForceViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
const result = await http.post(`/admin/force`, {
|
const result = await http.post(`/admin/force`, force);
|
||||||
firstname: force.firstname,
|
|
||||||
lastname: force.lastname,
|
|
||||||
nameaffix: force.nameaffix,
|
|
||||||
});
|
|
||||||
this.fetchForces();
|
this.fetchForces();
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
async updateActiveForce(force: UpdateForceViewModel): Promise<AxiosResponse<any, any>> {
|
async updateForce(force: UpdateForceViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
const result = await http.patch(`/admin/force/${force.id}`, {
|
const result = await http.patch(`/admin/force/${force.id}`, force);
|
||||||
firstname: force.firstname,
|
|
||||||
lastname: force.lastname,
|
|
||||||
nameaffix: force.nameaffix,
|
|
||||||
});
|
|
||||||
this.fetchForces();
|
this.fetchForces();
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
81
src/stores/admin/configuration/vehicle.ts
Normal file
81
src/stores/admin/configuration/vehicle.ts
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { http } from "@/serverCom";
|
||||||
|
import type { AxiosResponse } from "axios";
|
||||||
|
import type {
|
||||||
|
VehicleViewModel,
|
||||||
|
CreateVehicleViewModel,
|
||||||
|
UpdateVehicleViewModel,
|
||||||
|
} from "../../../viewmodels/admin/configuration/vehicle.models";
|
||||||
|
|
||||||
|
export const useVehicleStore = defineStore("vehicle", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
vehicles: [] as Array<VehicleViewModel & { tab_pos: number }>,
|
||||||
|
totalCount: 0 as number,
|
||||||
|
loading: "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 };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fetchVehicleById(id: string) {
|
||||||
|
return http.get(`/admin/vehicle/${id}`);
|
||||||
|
},
|
||||||
|
async createVehicle(vehicle: CreateVehicleViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.post(`/admin/vehicle`, vehicle);
|
||||||
|
this.fetchVehicles();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async updateActiveVehicle(vehicle: UpdateVehicleViewModel): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.patch(`/admin/vehicle/${vehicle.id}`, vehicle);
|
||||||
|
this.fetchVehicles();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
async deleteVehicle(vehicle: number): Promise<AxiosResponse<any, any>> {
|
||||||
|
const result = await http.delete(`/admin/vehicle/${vehicle}`);
|
||||||
|
this.fetchVehicles();
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -94,7 +94,11 @@ export const useNavigationStore = defineStore("navigation", {
|
||||||
},
|
},
|
||||||
configuration: {
|
configuration: {
|
||||||
mainTitle: "Konfiguration",
|
mainTitle: "Konfiguration",
|
||||||
main: [...(abilityStore.can("read", "configuration", "force") ? [{ key: "force", title: "Kräfte" }] : [])],
|
main: [
|
||||||
|
...(abilityStore.can("read", "configuration", "force") ? [{ key: "force", title: "Kräfte" }] : []),
|
||||||
|
...(abilityStore.can("read", "configuration", "equipment") ? [{ key: "equipment", title: "Geräte" }] : []),
|
||||||
|
...(abilityStore.can("read", "configuration", "vehicle") ? [{ key: "vehicle", title: "Fahrzeuge" }] : []),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
management: {
|
management: {
|
||||||
mainTitle: "Verwaltung",
|
mainTitle: "Verwaltung",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
export type PermissionSection = "operation" | "configuration" | "management";
|
export type PermissionSection = "operation" | "configuration" | "management";
|
||||||
|
|
||||||
export type PermissionModule = "mission" | "force" | "user" | "role" | "backup";
|
export type PermissionModule = "mission" | "force" | "vehicle" | "equipment" | "user" | "role" | "backup";
|
||||||
|
|
||||||
export type PermissionType = "read" | "create" | "update" | "delete";
|
export type PermissionType = "read" | "create" | "update" | "delete";
|
||||||
|
|
||||||
|
@ -24,10 +24,18 @@ export type SectionsAndModulesObject = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const permissionSections: Array<PermissionSection> = ["operation", "configuration", "management"];
|
export const permissionSections: Array<PermissionSection> = ["operation", "configuration", "management"];
|
||||||
export const permissionModules: Array<PermissionModule> = ["mission", "force", "user", "role", "backup"];
|
export const permissionModules: Array<PermissionModule> = [
|
||||||
|
"mission",
|
||||||
|
"force",
|
||||||
|
"vehicle",
|
||||||
|
"equipment",
|
||||||
|
"user",
|
||||||
|
"role",
|
||||||
|
"backup",
|
||||||
|
];
|
||||||
export const permissionTypes: Array<PermissionType> = ["read", "create", "update", "delete"];
|
export const permissionTypes: Array<PermissionType> = ["read", "create", "update", "delete"];
|
||||||
export const sectionsAndModules: SectionsAndModulesObject = {
|
export const sectionsAndModules: SectionsAndModulesObject = {
|
||||||
operation: ["mission"],
|
operation: ["mission"],
|
||||||
configuration: ["force"],
|
configuration: ["force", "vehicle", "equipment"],
|
||||||
management: ["user", "role", "backup"],
|
management: ["user", "role", "backup"],
|
||||||
};
|
};
|
||||||
|
|
25
src/viewmodels/admin/configuration/equipment.models.ts
Normal file
25
src/viewmodels/admin/configuration/equipment.models.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
export interface EquipmentViewModel {
|
||||||
|
id: string;
|
||||||
|
code?: string;
|
||||||
|
type?: string;
|
||||||
|
name: string;
|
||||||
|
commissioned: Date;
|
||||||
|
decommissioned?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateEquipmentViewModel {
|
||||||
|
code?: string;
|
||||||
|
type?: string;
|
||||||
|
name: string;
|
||||||
|
commissioned: Date;
|
||||||
|
decommissioned?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateEquipmentViewModel {
|
||||||
|
id: string;
|
||||||
|
code?: string;
|
||||||
|
type?: string;
|
||||||
|
name: string;
|
||||||
|
commissioned: Date;
|
||||||
|
decommissioned?: Date;
|
||||||
|
}
|
|
@ -1,19 +1,28 @@
|
||||||
export interface ForceViewModel {
|
export interface ForceViewModel {
|
||||||
id: string;
|
id: string;
|
||||||
|
internalId?: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
lastname: string;
|
lastname: string;
|
||||||
nameaffix: string;
|
nameaffix: string;
|
||||||
|
commissioned: Date;
|
||||||
|
decommissioned?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateForceViewModel {
|
export interface CreateForceViewModel {
|
||||||
|
internalId?: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
lastname: string;
|
lastname: string;
|
||||||
nameaffix: string;
|
nameaffix: string;
|
||||||
|
commissioned: Date;
|
||||||
|
decommissioned?: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateForceViewModel {
|
export interface UpdateForceViewModel {
|
||||||
id: string;
|
id: string;
|
||||||
|
internalId?: string;
|
||||||
firstname: string;
|
firstname: string;
|
||||||
lastname: string;
|
lastname: string;
|
||||||
nameaffix: string;
|
nameaffix: string;
|
||||||
|
commissioned: Date;
|
||||||
|
decommissioned?: Date;
|
||||||
}
|
}
|
||||||
|
|
25
src/viewmodels/admin/configuration/vehicle.models.ts
Normal file
25
src/viewmodels/admin/configuration/vehicle.models.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
export interface VehicleViewModel {
|
||||||
|
id: string;
|
||||||
|
code?: string;
|
||||||
|
type?: string;
|
||||||
|
name: string;
|
||||||
|
commissioned: Date;
|
||||||
|
decommissioned?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateVehicleViewModel {
|
||||||
|
code?: string;
|
||||||
|
type?: string;
|
||||||
|
name: string;
|
||||||
|
commissioned: Date;
|
||||||
|
decommissioned?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateVehicleViewModel {
|
||||||
|
id: string;
|
||||||
|
code?: string;
|
||||||
|
type?: string;
|
||||||
|
name: string;
|
||||||
|
commissioned: Date;
|
||||||
|
decommissioned?: Date;
|
||||||
|
}
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
<div class="flex flex-row gap-4">
|
<div class="flex flex-row gap-4">
|
||||||
<button v-if="can('create', 'operation', 'force')" primary class="!w-fit" @click="openCreateModal">
|
<button v-if="can('create', 'operation', 'force')" primary class="!w-fit" @click="openCreateModal">
|
||||||
Mitglied erstellen
|
Kraft erstellen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,7 +35,7 @@ import { defineAsyncComponent, defineComponent, markRaw } from "vue";
|
||||||
import { mapActions, mapState } from "pinia";
|
import { mapActions, mapState } from "pinia";
|
||||||
import MainTemplate from "@/templates/Main.vue";
|
import MainTemplate from "@/templates/Main.vue";
|
||||||
import { useForceStore } from "@/stores/admin/configuration/forces";
|
import { useForceStore } from "@/stores/admin/configuration/forces";
|
||||||
import ForceListItem from "@/components/admin/club/force/ForceListItem.vue";
|
import ForceListItem from "@/components/admin/configuration/force/ForceListItem.vue";
|
||||||
import { useModalStore } from "@/stores/modal";
|
import { useModalStore } from "@/stores/modal";
|
||||||
import Pagination from "@/components/Pagination.vue";
|
import Pagination from "@/components/Pagination.vue";
|
||||||
import type { ForceViewModel } from "@/viewmodels/admin/configuration/force.models";
|
import type { ForceViewModel } from "@/viewmodels/admin/configuration/force.models";
|
||||||
|
@ -62,7 +62,9 @@ export default defineComponent({
|
||||||
...mapActions(useForceStore, ["fetchForces"]),
|
...mapActions(useForceStore, ["fetchForces"]),
|
||||||
...mapActions(useModalStore, ["openModal"]),
|
...mapActions(useModalStore, ["openModal"]),
|
||||||
openCreateModal() {
|
openCreateModal() {
|
||||||
this.openModal(markRaw(defineAsyncComponent(() => import("@/components/admin/club/force/CreateForceModal.vue"))));
|
this.openModal(
|
||||||
|
markRaw(defineAsyncComponent(() => import("@/components/admin/configuration/force/CreateForceModal.vue")))
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue