Add composable for index

This commit is contained in:
philipp lang 2023-07-27 17:22:06 +02:00 committed by Philipp Lang
parent 69afeeda65
commit 1ac30202b1
8 changed files with 262 additions and 276 deletions

1
resources/js/app.js vendored
View File

@ -51,6 +51,7 @@ createInertiaApp({
requireModules(import.meta.glob('./components/ui/*.vue'), app, 'ui'); requireModules(import.meta.glob('./components/ui/*.vue'), app, 'ui');
requireModules(import.meta.glob('./components/page/*.vue', {eager: true}), app, 'page'); requireModules(import.meta.glob('./components/page/*.vue', {eager: true}), app, 'page');
app.provide('axios', app.config.globalProperties.axios);
app.mount(el); app.mount(el);
}, },
}); });

View File

@ -3,7 +3,7 @@
<Head :title="$page.props.title"></Head> <Head :title="$page.props.title"></Head>
<page-header :title="$page.props.title"> <page-header :title="$page.props.title">
<template #before-title> <template #before-title>
<a href="#" @click.prevent="menuStore.toggle()" class="mr-2 lg:hidden"> <a href="#" class="mr-2 lg:hidden" @click.prevent="menuStore.toggle()">
<ui-sprite src="menu" class="text-gray-100 w-5 h-5"></ui-sprite> <ui-sprite src="menu" class="text-gray-100 w-5 h-5"></ui-sprite>
</a> </a>
</template> </template>

85
resources/js/composables/useIndex.js vendored Normal file
View File

@ -0,0 +1,85 @@
import {ref, computed} from 'vue';
import {router} from '@inertiajs/vue3';
export function useIndex(props) {
const rawProps = JSON.parse(JSON.stringify(props));
const inner = {
data: ref(rawProps.data),
meta: ref(rawProps.meta),
};
function toFilterString(data) {
return btoa(encodeURIComponent(JSON.stringify(data)));
}
const filterString = computed(() => toFilterString(inner.meta.value.filter));
function reload(resetPage = true) {
var data = {
filter: filterString.value,
page: 1,
};
data['page'] = resetPage ? 1 : inner.meta.value.current_page;
router.visit(window.location.pathname, {
data,
preserveState: true,
onSuccess: (page) => {
inner.data.value = page.props.data.data;
inner.meta.value = page.props.data.meta;
},
});
}
function can(permission) {
return inner.meta.value.can[permission];
}
function getFilter(value) {
return inner.meta.value.filter[value];
}
function setFilter(key, value) {
inner.meta.value.filter[key] = value;
reload();
}
function requestCallback(successMessage, failureMessage) {
return {
onSuccess: () => {
this.$success(successMessage);
reload(false);
},
onFailure: () => {
this.$error(failureMessage);
reload(false);
},
preserveState: true,
};
}
return {
data: inner.data,
reload,
can,
getFilter,
setFilter,
requestCallback,
meta: inner.meta,
filterString,
router,
toFilterString,
};
}
const indexProps = {
data: {
default: () => {
return {data: [], meta: {}};
},
type: Object,
},
};
export {indexProps};

View File

@ -1,60 +0,0 @@
export default {
data: function () {
return {
inner: {...this.data},
};
},
props: {
data: {},
},
computed: {
filterString() {
return this.toFilterString(this.inner.meta.filter);
},
},
methods: {
toFilterString(data) {
return btoa(encodeURIComponent(JSON.stringify(data)));
},
reload(resetPage = true) {
var _self = this;
var data = {
filter: this.filterString,
page: 1,
};
data['page'] = resetPage ? 1 : this.inner.meta.current_page;
this.$inertia.visit(window.location.pathname, {
data,
preserveState: true,
onSuccess(page) {
_self.inner = page.props.data;
},
});
},
can(permission) {
return this.inner.meta.can[permission];
},
getFilter(value) {
return this.inner.meta.filter[value];
},
setFilter(key, value) {
this.inner.meta.filter[key] = value;
this.reload();
},
requestCallback(successMessage, failureMessage) {
return {
onSuccess: () => {
this.$success(successMessage);
this.reload(false);
},
onFailure: () => {
this.$error(failureMessage);
this.reload(false);
},
preserveState: true,
};
},
},
};

View File

@ -1,14 +1,14 @@
<template> <template>
<page-layout page-class="pb-6"> <page-layout page-class="pb-6">
<template #toolbar> <template #toolbar>
<page-toolbar-button :href="data.meta.links.create" color="primary" icon="plus">Tätigkeit erstellen</page-toolbar-button> <page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Tätigkeit erstellen</page-toolbar-button>
</template> </template>
<ui-popup heading="Bitte bestätigen" v-if="deleting !== null"> <ui-popup v-if="deleting !== null" heading="Bitte bestätigen" @close="deleting = null">
<div> <div>
<p class="mt-4">Diese Aktivität löschen?</p> <p class="mt-4">Diese Aktivität löschen?</p>
<div class="grid grid-cols-2 gap-3 mt-6"> <div class="grid grid-cols-2 gap-3 mt-6">
<a href="#" @click.prevent="remove" class="text-center btn btn-danger">Löschen</a> <a href="#" class="text-center btn btn-danger" @click.prevent="remove">Löschen</a>
<a href="#" @click.prevent="deleting = null" class="text-center btn btn-primary">Abbrechen</a> <a href="#" class="text-center btn btn-primary" @click.prevent="deleting = null">Abbrechen</a>
</div> </div>
</div> </div>
</ui-popup> </ui-popup>
@ -18,46 +18,39 @@
<th></th> <th></th>
</thead> </thead>
<tr v-for="(activity, index) in inner.data" :key="index"> <tr v-for="(activity, index) in data" :key="index">
<td v-text="activity.name"></td> <td v-text="activity.name"></td>
<td> <td>
<div class="flex space-x-1"> <div class="flex space-x-1">
<i-link :href="activity.links.edit" class="inline-flex btn btn-warning btn-sm" v-tooltip="`bearbeiten`"><ui-sprite src="pencil"></ui-sprite></i-link> <i-link v-tooltip="`bearbeiten`" :href="activity.links.edit" class="inline-flex btn btn-warning btn-sm"><ui-sprite src="pencil"></ui-sprite></i-link>
<i-link href="#" @click.prevent="deleting = activity" class="inline-flex btn btn-danger btn-sm" v-tooltip="`Entfernen`"><ui-sprite src="trash"></ui-sprite></i-link> <a v-tooltip="`Entfernen`" href="#" class="inline-flex btn btn-danger btn-sm" @click.prevent="deleting = activity"><ui-sprite src="trash"></ui-sprite></a>
</div> </div>
</td> </td>
</tr> </tr>
</table> </table>
<div class="px-6"> <div class="px-6">
<ui-pagination class="mt-4" :value="data.meta" :only="['data']"></ui-pagination> <ui-pagination class="mt-4" :value="meta" :only="['data']"></ui-pagination>
</div> </div>
</page-layout> </page-layout>
</template> </template>
<script> <script setup>
import indexHelpers from '../../mixins/indexHelpers'; import {ref, defineProps} from 'vue';
import {indexProps, useIndex} from '../../composables/useIndex.js';
export default { const props = defineProps(indexProps);
data: function () { const {router, data, meta} = useIndex(props.data);
return { const deleting = ref(null);
deleting: null,
};
},
methods: { function remove() {
remove() { router.delete(deleting.value.links.destroy, {
var _self = this; preserveState: true,
this.$inertia.delete(this.deleting.links.destroy, { onSuccess: (page) => {
preserveState: true, data.value = page.props.data.data;
onSuccess(page) { meta.value = page.props.data.meta;
_self.inner = page.props.data; deleting.value = null;
_self.deleting = null;
},
});
}, },
}, });
}
mixins: [indexHelpers],
};
</script> </script>

View File

@ -9,60 +9,60 @@
<form id="form" class="p-3 grid gap-3" @submit.prevent="submit"> <form id="form" class="p-3 grid gap-3" @submit.prevent="submit">
<ui-box heading="Metadatem"> <ui-box heading="Metadatem">
<div class="grid gap-4 sm:grid-cols-2"> <div class="grid gap-4 sm:grid-cols-2">
<f-text id="name" name="name" v-model="model.name" label="Name" size="sm" required></f-text> <f-text id="name" v-model="model.name" name="name" label="Name" size="sm" required></f-text>
<f-select id="gateway_id" name="gateway_id" :options="meta.gateways" v-model="model.gateway_id" label="Verbindung" size="sm" required></f-select> <f-select id="gateway_id" v-model="model.gateway_id" name="gateway_id" :options="meta.gateways" label="Verbindung" size="sm" required></f-select>
</div> </div>
</ui-box> </ui-box>
<ui-box heading="Filterregeln" v-if="members !== null"> <ui-box v-if="members !== null" heading="Filterregeln">
<div class="grid gap-4 sm:grid-cols-2"> <div class="grid gap-4 sm:grid-cols-2">
<f-multipleselect <f-multipleselect
id="activity_ids" id="activity_ids"
v-model="model.filter.activity_ids"
name="activity_ids" name="activity_ids"
:options="members.meta.filterActivities" :options="members.meta.filterActivities"
v-model="model.filter.activity_ids"
@update:modelValue="reload(1)"
label="Tätigkeit" label="Tätigkeit"
size="sm" size="sm"
@update:model-value="reload(1)"
></f-multipleselect> ></f-multipleselect>
<f-multipleselect <f-multipleselect
id="subactivity_ids" id="subactivity_ids"
v-model="model.filter.subactivity_ids"
name="subactivity_ids" name="subactivity_ids"
:options="members.meta.filterSubactivities" :options="members.meta.filterSubactivities"
v-model="model.filter.subactivity_ids"
@update:modelValue="reload(1)"
label="Unterttätigkeit" label="Unterttätigkeit"
size="sm" size="sm"
@update:model-value="reload(1)"
></f-multipleselect> ></f-multipleselect>
<f-multipleselect <f-multipleselect
id="include" id="include"
v-model="model.filter.include"
name="include" name="include"
:options="members.meta.members" :options="members.meta.members"
v-model="model.filter.include"
@update:modelValue="reload(1)"
label="Zusätzliche Mitglieder" label="Zusätzliche Mitglieder"
size="sm" size="sm"
@update:model-value="reload(1)"
></f-multipleselect> ></f-multipleselect>
<f-multipleselect <f-multipleselect
id="exclude" id="exclude"
v-model="model.filter.exclude"
name="exclude" name="exclude"
:options="members.meta.members" :options="members.meta.members"
v-model="model.filter.exclude"
@update:modelValue="reload(1)"
label="Mitglieder ausschließen" label="Mitglieder ausschließen"
size="sm" size="sm"
@update:model-value="reload(1)"
></f-multipleselect> ></f-multipleselect>
<f-multipleselect <f-multipleselect
id="groupIds" id="groupIds"
v-model="model.filter.group_ids"
name="groupIds" name="groupIds"
:options="members.meta.groups" :options="members.meta.groups"
v-model="model.filter.group_ids"
@update:modelValue="reload(1)"
label="Gruppierungen" label="Gruppierungen"
size="sm" size="sm"
@update:model-value="reload(1)"
></f-multipleselect> ></f-multipleselect>
</div> </div>
</ui-box> </ui-box>
<ui-box heading="Mitglieder" v-if="members !== null"> <ui-box v-if="members !== null" heading="Mitglieder">
<table cellspacing="0" cellpadding="0" border="0" class="custom-table custom-table-sm hidden md:table"> <table cellspacing="0" cellpadding="0" border="0" class="custom-table custom-table-sm hidden md:table">
<thead> <thead>
<th></th> <th></th>
@ -80,49 +80,46 @@
<td v-text="member.email_parents"></td> <td v-text="member.email_parents"></td>
</tr> </tr>
</table> </table>
<ui-pagination class="mt-4" @reload="reload" :value="members.meta" :only="['data']"></ui-pagination> <ui-pagination class="mt-4" :value="members.meta" :only="['data']" @reload="reload"></ui-pagination>
</ui-box> </ui-box>
</form> </form>
</page-layout> </page-layout>
</template> </template>
<script> <script setup>
import indexHelpers from '../../mixins/indexHelpers.js'; import {ref, inject, defineProps} from 'vue';
import hasFlash from '../../mixins/hasFlash.js'; import {useIndex} from '../../composables/useIndex.js';
export default { const props = defineProps({
mixins: [indexHelpers, hasFlash], data: {
default: () => undefined,
data: function () { type: Object,
return {
model: this.data === undefined ? {...this.meta.default_model} : {...this.data},
members: null,
};
}, },
meta: {
props: { type: Object,
data: {}, default: () => {},
meta: {},
}, },
});
methods: { const {toFilterString, router} = useIndex({data: [], meta: {}});
async reload(page) {
this.members = (
await this.axios.post('/api/member/search', {
page: page || 1,
filter: this.toFilterString(this.model.filter),
})
).data;
},
async submit() { const model = ref(props.data === undefined ? {...props.meta.default_model} : {...props.data});
this.model.id ? await this.axios.patch(this.model.links.update, this.model) : await this.axios.post('/maildispatcher', this.model); const members = ref(null);
this.$inertia.visit(this.meta.links.index); const axios = inject('axios');
},
},
async created() { async function reload(page) {
this.reload(); members.value = (
}, await axios.post('/api/member/search', {
}; page: page || 1,
filter: toFilterString(model.value.filter),
})
).data;
}
reload();
async function submit() {
model.value.id ? await axios.patch(model.value.links.update, model.value) : await axios.post('/maildispatcher', model.value);
router.visit(props.meta.links.index);
}
</script> </script>

View File

@ -1,44 +1,44 @@
<template> <template>
<page-layout> <page-layout>
<template #toolbar> <template #toolbar>
<page-toolbar-button @click.prevent="model = {...data.meta.default}" color="primary" icon="plus">Neue Verbindung</page-toolbar-button> <page-toolbar-button color="primary" icon="plus" @click.prevent="model = {...meta.default}">Neue Verbindung</page-toolbar-button>
</template> </template>
<ui-popup :heading="model.id ? 'Verbindung bearbeiten' : 'Neue Verbindung'" v-if="model !== null" @close="model = null"> <ui-popup v-if="model !== null" :heading="model.id ? 'Verbindung bearbeiten' : 'Neue Verbindung'" @close="model = null">
<form @submit.prevent="submit"> <form @submit.prevent="submit">
<section class="grid grid-cols-2 gap-3 mt-6"> <section class="grid grid-cols-2 gap-3 mt-6">
<f-text v-model="model.name" name="name" id="name" label="Bezeichnung" required></f-text> <f-text id="name" v-model="model.name" name="name" label="Bezeichnung" required></f-text>
<f-text v-model="model.domain" name="domain" id="domain" label="Domain" required></f-text> <f-text id="domain" v-model="model.domain" name="domain" label="Domain" required></f-text>
<f-select <f-select
:modelValue="model.type.cls" id="type"
@update:modelValue=" :model-value="model.type.cls"
label="Typ"
name="type"
:options="meta.types"
:placeholder="''"
required
@update:model-value="
model.type = { model.type = {
cls: $event, cls: $event,
params: {...getType($event).defaults}, params: {...getType($event).defaults},
} }
" "
label="Typ"
name="type"
id="type"
:options="data.meta.types"
:placeholder="''"
required
></f-select> ></f-select>
<template v-for="(field, index) in getType(model.type.cls).fields"> <template v-for="(field, index) in getType(model.type.cls).fields">
<f-text <f-text
:key="index"
v-if="field.type === 'text' || field.type === 'password' || field.type === 'email'" v-if="field.type === 'text' || field.type === 'password' || field.type === 'email'"
:id="field.name"
:key="index"
v-model="model.type.params[field.name]"
:label="field.label" :label="field.label"
:type="field.type" :type="field.type"
:name="field.name" :name="field.name"
:id="field.name"
v-model="model.type.params[field.name]"
:required="field.is_required" :required="field.is_required"
></f-text> ></f-text>
</template> </template>
</section> </section>
<section class="flex mt-4 space-x-2"> <section class="flex mt-4 space-x-2">
<ui-button type="submit" class="btn-danger">Speichern</ui-button> <ui-button type="submit" class="btn-danger">Speichern</ui-button>
<ui-button @click.prevent="model = null" class="btn-primary">Abbrechen</ui-button> <ui-button class="btn-primary" @click.prevent="model = null">Abbrechen</ui-button>
</section> </section>
</form> </form>
</ui-popup> </ui-popup>
@ -53,7 +53,7 @@
<th>Aktion</th> <th>Aktion</th>
</thead> </thead>
<tr v-for="(gateway, index) in inner.data" :key="index"> <tr v-for="(gateway, index) in data" :key="index">
<td v-text="gateway.name"></td> <td v-text="gateway.name"></td>
<td v-text="gateway.domain"></td> <td v-text="gateway.domain"></td>
<td v-text="gateway.type_human"></td> <td v-text="gateway.type_human"></td>
@ -65,49 +65,36 @@
></ui-boolean-display> ></ui-boolean-display>
</td> </td>
<td> <td>
<a href="#" v-tooltip="`Bearbeiten`" @click.prevent="model = {...gateway}" class="inline-flex btn btn-warning btn-sm"><ui-sprite src="pencil"></ui-sprite></a> <a v-tooltip="`Bearbeiten`" href="#" class="inline-flex btn btn-warning btn-sm" @click.prevent="model = {...gateway}"><ui-sprite src="pencil"></ui-sprite></a>
</td> </td>
</tr> </tr>
</table> </table>
<div class="px-6"> <div class="px-6">
<ui-pagination class="mt-4" :value="data.meta" :only="['data']"></ui-pagination> <ui-pagination class="mt-4" :value="meta" :only="['data']"></ui-pagination>
</div> </div>
</div> </div>
</setting-layout> </setting-layout>
</page-layout> </page-layout>
</template> </template>
<script> <script setup>
import {ref, inject} from 'vue';
import {indexProps, useIndex} from '../../composables/useIndex.js';
import SettingLayout from '../setting/Layout.vue'; import SettingLayout from '../setting/Layout.vue';
import indexHelpers from '../../mixins/indexHelpers.js';
export default { const props = defineProps(indexProps);
mixins: [indexHelpers], const {meta, data, reload} = useIndex(props.data);
const model = ref(null);
const axios = inject('axios');
data: function () { function getType(type) {
return { return meta.value.types.find((t) => t.id === type);
model: null, }
inner: {...this.data}, async function submit() {
}; await axios[model.value.id ? 'patch' : 'post'](model.value.id ? model.value.links.update : meta.value.links.store, model.value);
},
props: {
data: {},
},
methods: { reload();
getType(type) { model.value = null;
return this.data.meta.types.find((t) => t.id === type); }
},
async submit() {
await this.axios[this.model.id ? 'patch' : 'post'](this.model.id ? this.model.links.update : this.data.meta.links.store, this.model);
this.reload();
this.model = null;
},
},
components: {
SettingLayout,
},
};
</script> </script>

View File

@ -1,63 +1,63 @@
<template> <template>
<page-layout page-class="pb-6"> <page-layout page-class="pb-6">
<template #toolbar> <template #toolbar>
<page-toolbar-button :href="data.meta.links.create" color="primary" icon="plus">Mitglied anlegen</page-toolbar-button> <page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Mitglied anlegen</page-toolbar-button>
<page-toolbar-button :href="data.meta.links.allpayment" color="primary" icon="invoice" v-if="hasModule('bill')">Rechnungen erstellen</page-toolbar-button> <page-toolbar-button v-if="hasModule('bill')" :href="meta.links.allpayment" color="primary" icon="invoice">Rechnungen erstellen</page-toolbar-button>
<page-toolbar-button :href="data.meta.links.sendpayment" color="info" icon="envelope" v-if="hasModule('bill')">Rechnungen versenden</page-toolbar-button> <page-toolbar-button v-if="hasModule('bill')" :href="meta.links.sendpayment" color="info" icon="envelope">Rechnungen versenden</page-toolbar-button>
</template> </template>
<ui-popup heading="Mitglied löschen?" v-if="deleting !== null" @close="deleting.reject()"> <ui-popup v-if="deleting !== null" heading="Mitglied löschen?" @close="deleting.reject()">
<div> <div>
<p class="mt-4">Das Mitglied "{{ deleting.member.fullname }}" löschen?</p> <p class="mt-4">Das Mitglied "{{ deleting.member.fullname }}" löschen?</p>
<p class="mt-2">Alle Zuordnungen (Ausbildungen, Rechnungen, Zahlungen, Tätigkeiten) werden ebenfalls entfernt.</p> <p class="mt-2">Alle Zuordnungen (Ausbildungen, Rechnungen, Zahlungen, Tätigkeiten) werden ebenfalls entfernt.</p>
<ui-note class="mt-5" type="warning" v-if="!deleting.member.has_nami"> Dieses Mitglied ist nicht in NaMi vorhanden und wird daher nur in der AdReMa gelöscht werden. </ui-note> <ui-note v-if="!deleting.member.has_nami" class="mt-5" type="warning"> Dieses Mitglied ist nicht in NaMi vorhanden und wird daher nur in der AdReMa gelöscht werden. </ui-note>
<ui-note class="mt-5" type="danger" v-if="deleting.member.has_nami"> <ui-note v-if="deleting.member.has_nami" class="mt-5" type="danger">
Dieses Mitglied ist in NaMi vorhanden und wird daher in NaMi abgemeldet werden. Sofern "Datenweiterverwendung" eingeschaltet ist, wird das Mitglied auf inaktiv gesetzt. Dieses Mitglied ist in NaMi vorhanden und wird daher in NaMi abgemeldet werden. Sofern "Datenweiterverwendung" eingeschaltet ist, wird das Mitglied auf inaktiv gesetzt.
</ui-note> </ui-note>
<div class="grid grid-cols-2 gap-3 mt-6"> <div class="grid grid-cols-2 gap-3 mt-6">
<a href="#" @click.prevent="deleting.resolve" class="text-center btn btn-danger">Mitglied loschen</a> <a href="#" class="text-center btn btn-danger" @click.prevent="deleting.resolve">Mitglied loschen</a>
<a href="#" @click.prevent="deleting.reject" class="text-center btn btn-primary">Abbrechen</a> <a href="#" class="text-center btn btn-primary" @click.prevent="deleting.reject">Abbrechen</a>
</div> </div>
</div> </div>
</ui-popup> </ui-popup>
<div class="px-6 py-2 flex border-b border-gray-600 items-center space-x-3"> <div class="px-6 py-2 flex border-b border-gray-600 items-center space-x-3">
<f-text :modelValue="getFilter('search')" @update:modelValue="setFilter('search', $event)" id="search" name="search" label="Suchen …" size="sm"></f-text> <f-text id="search" :model-value="getFilter('search')" name="search" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text>
<f-switch v-show="hasModule('bill')" id="ausstand" @update:modelValue="setFilter('ausstand', $event)" :modelValue="getFilter('ausstand')" label="Nur Ausstände" size="sm"></f-switch> <f-switch v-show="hasModule('bill')" id="ausstand" :model-value="getFilter('ausstand')" label="Nur Ausstände" size="sm" @update:model-value="setFilter('ausstand', $event)"></f-switch>
<f-multipleselect <f-multipleselect
id="group_ids" id="group_ids"
@update:modelValue="setFilter('group_ids', $event)" :options="meta.groups"
:options="data.meta.groups" :model-value="getFilter('group_ids')"
:modelValue="getFilter('group_ids')"
label="Gruppierungen" label="Gruppierungen"
size="sm" size="sm"
name="group_ids" name="group_ids"
@update:model-value="setFilter('group_ids', $event)"
></f-multipleselect> ></f-multipleselect>
<f-select <f-select
v-show="hasModule('bill')" v-show="hasModule('bill')"
name="billKinds"
id="billKinds" id="billKinds"
@update:modelValue="setFilter('bill_kind', $event)" name="billKinds"
:options="data.meta.billKinds" :options="meta.billKinds"
:modelValue="getFilter('bill_kind')" :model-value="getFilter('bill_kind')"
label="Rechnung" label="Rechnung"
size="sm" size="sm"
@update:model-value="setFilter('bill_kind', $event)"
></f-select> ></f-select>
<f-multipleselect <f-multipleselect
id="activity_ids" id="activity_ids"
@update:modelValue="setFilter('activity_ids', $event)" :options="meta.filterActivities"
:options="data.meta.filterActivities" :model-value="getFilter('activity_ids')"
:modelValue="getFilter('activity_ids')"
label="Tätigkeiten" label="Tätigkeiten"
size="sm" size="sm"
name="activity_ids" name="activity_ids"
@update:model-value="setFilter('activity_ids', $event)"
></f-multipleselect> ></f-multipleselect>
<f-multipleselect <f-multipleselect
id="subactivity_ids" id="subactivity_ids"
@update:modelValue="setFilter('subactivity_ids', $event)" :options="meta.filterSubactivities"
:options="data.meta.filterSubactivities" :model-value="getFilter('subactivity_ids')"
:modelValue="getFilter('subactivity_ids')"
label="Untertätigkeiten" label="Untertätigkeiten"
size="sm" size="sm"
name="subactivity_ids" name="subactivity_ids"
@update:model-value="setFilter('subactivity_ids', $event)"
></f-multipleselect> ></f-multipleselect>
<button class="btn btn-primary label mr-2" @click.prevent="exportMembers"> <button class="btn btn-primary label mr-2" @click.prevent="exportMembers">
<ui-sprite class="w-3 h-3 xl:mr-2" src="save"></ui-sprite> <ui-sprite class="w-3 h-3 xl:mr-2" src="save"></ui-sprite>
@ -73,19 +73,21 @@
<th class="hidden 2xl:table-cell">Ort</th> <th class="hidden 2xl:table-cell">Ort</th>
<th>Tags</th> <th>Tags</th>
<th class="hidden xl:table-cell">Alter</th> <th class="hidden xl:table-cell">Alter</th>
<th class="hidden xl:table-cell" v-show="hasModule('bill')">Rechnung</th> <th v-show="hasModule('bill')" class="hidden xl:table-cell">Rechnung</th>
<th v-show="hasModule('bill')">Ausstand</th> <th v-show="hasModule('bill')">Ausstand</th>
<th></th> <th></th>
</thead> </thead>
<tr v-for="(member, index) in inner.data" :key="index"> <tr v-for="(member, index) in data" :key="index">
<td><ui-age-groups :member="member"></ui-age-groups></td> <td><ui-age-groups :member="member"></ui-age-groups></td>
<td v-text="member.lastname"></td> <td v-text="member.lastname"></td>
<td v-text="member.firstname"></td> <td v-text="member.firstname"></td>
<td class="hidden 2xl:table-cell" v-text="member.full_address"></td> <td class="hidden 2xl:table-cell" v-text="member.full_address"></td>
<td><tags :member="member"></tags></td> <td>
<tags :member="member"></tags>
</td>
<td class="hidden xl:table-cell" v-text="member.age"></td> <td class="hidden xl:table-cell" v-text="member.age"></td>
<td class="hidden xl:table-cell" v-show="hasModule('bill')"> <td v-show="hasModule('bill')" class="hidden xl:table-cell">
<ui-label :value="member.bill_kind_name" fallback="kein"></ui-label> <ui-label :value="member.bill_kind_name" fallback="kein"></ui-label>
</td> </td>
<td v-show="hasModule('bill')"> <td v-show="hasModule('bill')">
@ -98,101 +100,82 @@
</table> </table>
<div class="md:hidden p-3 grid gap-3"> <div class="md:hidden p-3 grid gap-3">
<ui-box class="relative" :heading="member.fullname" v-for="(member, index) in data.data" :key="index"> <ui-box v-for="(member, index) in data.data" :key="index" class="relative" :heading="member.fullname">
<div slot="in-title"> <template #in-title>
<ui-age-groups class="ml-2" :member="member" icon-class="w-4 h-4"></ui-age-groups> <ui-age-groups class="ml-2" :member="member" icon-class="w-4 h-4"></ui-age-groups>
</div> </template>
<div class="text-xs text-gray-200" v-text="member.full_address"></div> <div class="text-xs text-gray-200" v-text="member.full_address"></div>
<div class="flex items-center mt-1 space-x-4"> <div class="flex items-center mt-1 space-x-4">
<tags :member="member"></tags> <tags :member="member"></tags>
<ui-label class="text-gray-100 block" v-show="hasModule('bill')" :value="member.pending_payment" fallback=""></ui-label> <ui-label v-show="hasModule('bill')" class="text-gray-100 block" :value="member.pending_payment" fallback=""></ui-label>
</div> </div>
<actions class="mt-2" :member="member" @sidebar="openSidebar(index, $event)" @remove="remove(member)"></actions> <actions class="mt-2" :member="member" @sidebar="openSidebar(index, $event)" @remove="remove(member)"> </actions>
<div class="absolute right-0 top-0 h-full flex items-center mr-2"> <div class="absolute right-0 top-0 h-full flex items-center mr-2">
<i-link :href="member.links.show" v-tooltip="`Details`"><ui-sprite src="chevron-down" class="w-6 h-6 text-teal-100 -rotate-90"></ui-sprite></i-link> <i-link v-tooltip="`Details`" :href="member.links.show"><ui-sprite src="chevron-down" class="w-6 h-6 text-teal-100 -rotate-90"></ui-sprite></i-link>
</div> </div>
</ui-box> </ui-box>
</div> </div>
<div class="px-6"> <div class="px-6">
<ui-pagination class="mt-4" :value="data.meta" :only="['data']"></ui-pagination> <ui-pagination class="mt-4" :value="meta" :only="['data']"></ui-pagination>
</div> </div>
<member-payments <member-payments
v-if="single !== null && sidebar === 'payment.index'" v-if="single !== null && sidebar === 'payment.index'"
:subscriptions="meta.subscriptions"
:statuses="meta.statuses"
:value="data[single]"
@close="closeSidebar" @close="closeSidebar"
:subscriptions="data.meta.subscriptions"
:statuses="data.meta.statuses"
:value="data.data[single]"
></member-payments> ></member-payments>
<member-memberships <member-memberships
v-if="single !== null && sidebar === 'membership.index'" v-if="single !== null && sidebar === 'membership.index'"
:groups="meta.groups"
:activities="meta.formActivities"
:subactivities="meta.formSubactivities"
:value="data[single]"
@close="closeSidebar" @close="closeSidebar"
:groups="data.meta.groups"
:activities="data.meta.formActivities"
:subactivities="data.meta.formSubactivities"
:value="data.data[single]"
></member-memberships> ></member-memberships>
<member-courses v-if="single !== null && sidebar === 'courses.index'" @close="closeSidebar" :courses="data.meta.courses" :value="data.data[single]"></member-courses> <member-courses v-if="single !== null && sidebar === 'courses.index'" :courses="meta.courses" :value="data[single]" @close="closeSidebar"></member-courses>
</page-layout> </page-layout>
</template> </template>
<script> <script setup>
import MemberPayments from './MemberPayments.vue'; import MemberPayments from './MemberPayments.vue';
import MemberMemberships from './MemberMemberships.vue'; import MemberMemberships from './MemberMemberships.vue';
import MemberCourses from './MemberCourses.vue'; import MemberCourses from './MemberCourses.vue';
import indexHelpers from '../../mixins/indexHelpers.js';
import hasModule from '../../mixins/hasModule.js';
import Tags from './Tags.vue'; import Tags from './Tags.vue';
import Actions from './index/Actions.vue'; import Actions from './index/Actions.vue';
import {indexProps, useIndex} from '../../composables/useIndex.js';
import {ref, defineProps} from 'vue';
export default { const sidebar = ref(null);
data: function () { const single = ref(null);
return { const deleting = ref(null);
sidebar: null,
single: null,
deleting: null,
};
},
mixins: [indexHelpers, hasModule], const props = defineProps(indexProps);
var {router, data, meta, getFilter, setFilter, filterString} = useIndex(props.data);
components: { function exportMembers() {
MemberMemberships, window.open(`/member-export?filter=${filterString.value}`);
MemberPayments, }
MemberCourses,
Tags,
Actions,
},
methods: { async function remove(member) {
exportMembers() { new Promise((resolve, reject) => {
window.open(`/member-export?filter=${this.filterString}`); deleting.value = {resolve, reject, member};
}, })
async remove(member) { .then(() => {
new Promise((resolve, reject) => { router.delete(`/member/${member.id}`);
this.deleting = {resolve, reject, member}; deleting.value = null;
}) })
.then(() => { .catch(() => (deleting.value = null));
this.$inertia.delete(`/member/${member.id}`); }
this.deleting = null;
})
.catch(() => {
this.deleting = null;
});
},
openSidebar(index, name) {
this.single = index;
this.sidebar = name;
},
closeSidebar() {
this.single = null;
this.sidebar = null;
},
},
props: { function openSidebar(index, name) {
query: {}, single.value = index;
}, sidebar.value = name;
}; }
function closeSidebar() {
single.value = null;
sidebar.value = null;
}
</script> </script>