[manage] Merge types management into one component

This commit is contained in:
Ferdinand Thiessen 2021-11-15 00:33:28 +01:00
parent 9331006db3
commit 3dc108656a
5 changed files with 66 additions and 207 deletions

View File

@ -1,125 +0,0 @@
<template>
<div>
<q-dialog v-model="edittype">
<q-card>
<q-card-section>
<div class="text-h6">Editere Diensttyp {{ actualJob.name }}</div>
</q-card-section>
<q-card-section>
<q-input v-model="newJobName" dense label="name" filled />
</q-card-section>
<q-card-actions>
<q-btn flat color="danger" label="Abbrechen" @click="discardChanges()" />
<q-btn flat color="primary" label="Speichern" @click="saveChanges()" />
</q-card-actions>
</q-card>
</q-dialog>
<q-card>
<q-card-section>
<q-table title="Diensttypen" :rows="rows" row-key="jobid" :columns="columns">
<template #top-right>
<q-input v-model="newJob" dense placeholder="Neuer Typ" />
<div></div>
<q-btn color="primary" icon="mdi-plus" label="Hinzufügen" @click="addType" />
</template>
<template #body-cell-actions="props">
<!-- <q-btn :label="item"> -->
<!-- {{ item.row.name }} -->
<q-td :props="props" align="right" :auto-width="true">
<q-btn
round
icon="mdi-pencil"
@click="editType({ id: props.row.id, name: props.row.name })"
/>
<q-btn round icon="mdi-delete" @click="deleteType(props.row.id)" />
</q-td>
</template>
</q-table>
</q-card-section>
</q-card>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed, onBeforeMount } from 'vue';
import { useScheduleStore } from '../../store';
export default defineComponent({
name: 'JobTypes',
components: {},
setup() {
const store = useScheduleStore();
const newJob = ref('');
const edittype = ref(false);
const emptyJob: FG.JobType = { id: -1, name: '' };
const actualJob = ref(emptyJob);
const newJobName = ref('');
onBeforeMount(() => store.getJobTypes());
const rows = computed(() => store.jobTypes);
const columns = [
{
name: 'jobname',
label: 'Name',
field: 'name',
align: 'left',
sortable: true,
},
{
name: 'actions',
label: 'Aktionen',
field: 'actions',
align: 'right',
},
];
async function addType() {
await store.addJobType(newJob.value);
newJob.value = '';
}
function editType(job: FG.JobType) {
edittype.value = true;
actualJob.value = job;
}
async function saveChanges() {
try {
await store.renameJobType(actualJob.value.id, newJobName.value);
} finally {
discardChanges();
}
}
function discardChanges() {
actualJob.value = emptyJob;
newJobName.value = '';
edittype.value = false;
}
function deleteType(id: number) {
void store.removeJobType(id);
}
return {
columns,
rows,
addType,
newJob,
deleteType,
edittype,
editType,
actualJob,
newJobName,
discardChanges,
saveChanges,
};
},
});
</script>
<style scoped></style>

View File

@ -3,10 +3,10 @@
<q-dialog v-model="dialogOpen"> <q-dialog v-model="dialogOpen">
<q-card> <q-card>
<q-card-section> <q-card-section>
<div class="text-h6">Editere Diensttyp {{ actualEvent.name }}</div> <div class="text-h6">Editere {{title}} {{ actualType.name }}</div>
</q-card-section> </q-card-section>
<q-card-section> <q-card-section>
<q-input v-model="newEventName" :rules="rules" ref="dialogInput" dense label="name" filled /> <q-input v-model="actualType.name" :rules="rules" ref="dialogInput" dense label="name" filled />
</q-card-section> </q-card-section>
<q-card-actions> <q-card-actions>
<q-btn flat color="danger" label="Abbrechen" @click="discardChanges()" /> <q-btn flat color="danger" label="Abbrechen" @click="discardChanges()" />
@ -17,10 +17,10 @@
<q-card> <q-card>
<q-card-section> <q-card-section>
<q-table title="Veranstaltungstypen" :rows="rows" row-key="jobid" :columns="columns"> <q-table :title="title" :rows="rows" row-key="id" :columns="columns">
<template #top-right> <template #top-right>
<q-input <q-input
v-model="newEventType" v-model="actualType.name"
ref="input" ref="input"
:rules="rules" :rules="rules"
dense dense
@ -32,13 +32,11 @@
</q-input> </q-input>
</template> </template>
<template #body-cell-actions="props"> <template #body-cell-actions="props">
<!-- <q-btn :label="item"> -->
<!-- {{ item.row.name }} -->
<q-td :props="props" align="right" :auto-width="true"> <q-td :props="props" align="right" :auto-width="true">
<q-btn <q-btn
round round
icon="mdi-pencil" icon="mdi-pencil"
@click="editType({ id: props.row.id, name: props.row.name })" @click="editType(props.row.id)"
/> />
<q-btn round icon="mdi-delete" @click="deleteType(props.row.id)" /> <q-btn round icon="mdi-delete" @click="deleteType(props.row.id)" />
</q-td> </q-td>
@ -51,38 +49,42 @@
<script lang="ts"> <script lang="ts">
import { isAxiosError } from '@flaschengeist/api'; import { isAxiosError } from '@flaschengeist/api';
import { defineComponent, ref, computed, onBeforeMount } from 'vue'; import { defineComponent, ref, computed, PropType, onBeforeMount } from 'vue';
import { useScheduleStore } from '../../store'; import { useScheduleStore } from '../../store';
import { Notify, QInput } from 'quasar'; import { Notify, QInput } from 'quasar';
export default defineComponent({ export default defineComponent({
name: 'EventTypes', name: 'ManageTypes',
components: {}, components: {},
setup() { props:{
type: {type: String as PropType<'EventType' | 'JobType'>, required: true},
title: {type: String, required: true}
},
setup(props) {
const store = useScheduleStore(); const store = useScheduleStore();
const newEventType = ref(''); const newType = ref('');
const dialogOpen = ref(false); const dialogOpen = ref(false);
const emptyEvent: FG.EventType = { id: -1, name: '' }; const emptyType = { id: -1, name: '' };
const actualEvent = ref(emptyEvent); const actualType = ref(emptyType);
const newEventName = ref('');
const input = ref<QInput>(null); const input = ref<QInput>(null);
const dialogInput = ref<QInput>(null); const dialogInput = ref<QInput>(null);
const storeName = computed(() => props.type.charAt(0).toLowerCase() + props.type.slice(1) + 's')
onBeforeMount(async () => await store.getEventTypes()); onBeforeMount(async () => await store[`get${props.type}s`]());
const rows = computed(() => store.eventTypes); const rows = computed(() => store[storeName.value]);
const rules = [ const rules = [
(s: any) => !!s || 'Darf nicht leer sein!', (s: any) => !!s || 'Darf nicht leer sein!',
(s: string) => (s: string) =>
store.eventTypes.find((e) => e.name === s) === undefined || store[storeName.value].find((e) => e.name === s) === undefined ||
'Der Name wird bereits verwendet', 'Der Name wird bereits verwendet',
]; ];
const columns = [ const columns = [
{ {
name: 'name', name: 'name',
label: 'Veranstaltungstyp', label: props.title,
field: 'name', field: 'name',
align: 'left', align: 'left',
sortable: true, sortable: true,
@ -98,9 +100,9 @@ export default defineComponent({
function addType() { function addType() {
if (input.value === null || input.value.validate()) if (input.value === null || input.value.validate())
store store
.addEventType(newEventType.value) [`add${props.type}`](actualType.value.name)
.then(() => { .then(() => {
newEventType.value = ''; actualType.value.name = '';
}) })
.catch((e) => { .catch((e) => {
if (isAxiosError(e, 409)) if (isAxiosError(e, 409))
@ -116,28 +118,27 @@ export default defineComponent({
}); });
} }
function editType(event: FG.EventType) { function editType(id: number) {
dialogOpen.value = true; dialogOpen.value = true;
actualEvent.value = event; actualType.value = Object.assign({}, rows.value.find((v) => v.id === id));
} }
function saveChanges() { function saveChanges() {
if (dialogInput.value === null || dialogInput.value.validate()) if (dialogInput.value === null || dialogInput.value.validate())
store.renameEventType(actualEvent.value.id, newEventName.value).then(() => discardChanges()); store[`rename${props.type}`](actualType.value.id, actualType.value.name).then(() => discardChanges());
} }
function discardChanges() { function discardChanges() {
actualEvent.value = emptyEvent; actualType.value = emptyType;
newEventName.value = '';
dialogOpen.value = false; dialogOpen.value = false;
} }
async function deleteType(id: number) { async function deleteType(id: number) {
await store.removeEventType(id); await store[`remove${props.type}`](id);
} }
return { return {
actualEvent, actualType,
addType, addType,
columns, columns,
deleteType, deleteType,
@ -148,8 +149,6 @@ export default defineComponent({
input, input,
rows, rows,
rules, rules,
newEventType,
newEventName,
saveChanges, saveChanges,
}; };
}, },

View File

@ -35,10 +35,10 @@
<EditEvent /> <EditEvent />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="eventtypes"> <q-tab-panel name="eventtypes">
<EventTypes /> <ManageTypes title="Veranstaltungstyp" type="EventType" />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="jobtypes"> <q-tab-panel name="jobtypes">
<JobTypes /> <ManageTypes title="Dienstart" type="JobType" />
</q-tab-panel> </q-tab-panel>
</q-tab-panels> </q-tab-panels>
</q-page> </q-page>
@ -47,8 +47,7 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref } from 'vue'; import { computed, defineComponent, ref } from 'vue';
import EventTypes from '../components/management/EventTypes.vue'; import ManageTypes from '../components/management/ManageTypes.vue';
import JobTypes from '../components/management/JobTypes.vue';
import EditEvent from '../components/management/EditEvent.vue'; import EditEvent from '../components/management/EditEvent.vue';
import { hasPermission } from '@flaschengeist/api'; import { hasPermission } from '@flaschengeist/api';
import { PERMISSIONS } from '../permissions'; import { PERMISSIONS } from '../permissions';
@ -56,7 +55,7 @@ import { Screen } from 'quasar';
export default defineComponent({ export default defineComponent({
name: 'EventManagement', name: 'EventManagement',
components: { EditEvent, EventTypes, JobTypes }, components: { EditEvent, ManageTypes },
setup() { setup() {
const tabs = computed(() => [ const tabs = computed(() => [
{ name: 'create', label: 'Veranstaltungen' }, { name: 'create', label: 'Veranstaltungen' },

View File

@ -35,7 +35,6 @@
<AgendaView /> <AgendaView />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="eventtypes"> <q-tab-panel name="eventtypes">
<EventTypes />
</q-tab-panel> </q-tab-panel>
</q-tab-panels> </q-tab-panels>
</q-page> </q-page>
@ -44,14 +43,12 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, ref } from 'vue'; import { computed, defineComponent, ref } from 'vue';
import EventTypes from '../components/management/EventTypes.vue';
//import CreateEvent from '../components/management/CreateEvent.vue';
import AgendaView from '../components/overview/AgendaView.vue'; import AgendaView from '../components/overview/AgendaView.vue';
import { Screen } from 'quasar'; import { Screen } from 'quasar';
export default defineComponent({ export default defineComponent({
name: 'EventOverview', name: 'EventOverview',
components: { AgendaView, EventTypes }, components: { AgendaView },
setup() { setup() {
interface Tab { interface Tab {
name: string; name: string;

View File

@ -1,4 +1,4 @@
import { api } from '@flaschengeist/api'; import { api, isAxiosError } from '@flaschengeist/api';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
@ -19,7 +19,7 @@ function fixEvent(event: FG.Event) {
} }
export const useScheduleStore = defineStore({ export const useScheduleStore = defineStore({
id: 'schedule', id: 'events',
state: () => ({ state: () => ({
jobTypes: [] as FG.JobType[], jobTypes: [] as FG.JobType[],
@ -41,24 +41,25 @@ export const useScheduleStore = defineStore({
return this.jobTypes; return this.jobTypes;
}, },
async addJobType(name: string) { addJobType(name: string) {
try { return api
const { data } = await api.post<FG.JobType>('/events/job-types', { name: name }); .post<FG.JobType>('/events/job-types', { name: name })
this.jobTypes.push(data); .then(({ data }) => this.jobTypes.push(data));
} catch (error) {
// TODO: HANDLE ERROR
throw error;
}
}, },
async removeJobType(id: number) { removeJobType(id: number) {
await api.delete(`/events/job-types/${id}`); return api
//Todo Handle delete JT .delete(`/events/job-types/${id}`)
.then(() => this.jobTypes = this.jobTypes.filter(v => v.id !== id));
}, },
async renameJobType(id: number, newName: string) { renameJobType(id: number, newName: string) {
await api.put(`/events/job-types/${id}`, { name: newName }); return api
// TODO handle rename .put(`/events/job-types/${id}`, { name: newName })
.then(() => {
const idx = this.jobTypes.findIndex(v=>v.id===id);
if (idx >= 0) this.jobTypes[idx].name = newName;
})
}, },
async getEventTypes(force = false) { async getEventTypes(force = false) {
@ -75,21 +76,13 @@ export const useScheduleStore = defineStore({
/** Add new EventType /** Add new EventType
* *
* @param name Name of new EventType * @param name Name of new EventType
* @returns EventType object or null if name already exists
* @throws Exception if requests fails because of an other reason
*/ */
async addEventType(name: string) { addEventType(name: string) {
try { return api
const { data } = await api.post<FG.EventType>('/events/event-types', { name: name }); .post<FG.EventType>('/events/event-types', { name: name })
return data; .then(({data}) => this.eventTypes.push(data))
} catch (error) {
if ('response' in error) {
const ae = <AxiosError>error;
if (ae.response && ae.response.status == 409 /* CONFLICT */) return null;
}
throw error;
}
}, },
async removeEvent(id: number) { async removeEvent(id: number) {
try { try {
await api.delete(`/events/${id}`); await api.delete(`/events/${id}`);
@ -103,23 +96,19 @@ export const useScheduleStore = defineStore({
return true; return true;
}, },
async removeEventType(id: number) { removeEventType(id: number) {
await api.delete(`/events/event-types/${id}`); return api
// TODO handle delete .delete(`/events/event-types/${id}`)
.then(() => this.eventTypes = this.eventTypes.filter(v => v.id !== id));
}, },
async renameEventType(id: number, newName: string) { renameEventType(id: number, newName: string) {
try { return api
await api.put(`/events/event-types/${id}`, { name: newName }); .put(`/events/event-types/${id}`, { name: newName })
// TODO handle rename .then(() => {
return true; const idx = this.eventTypes.findIndex(v=>v.id===id);
} catch (error) { if (idx >= 0) this.eventTypes[idx].name = newName;
if ('response' in error) { })
const ae = <AxiosError>error;
if (ae.response && ae.response.status == 409 /* CONFLICT */) return false;
}
throw error;
}
}, },
async getTemplates(force = false) { async getTemplates(force = false) {