Compare commits

...

5 Commits

Author SHA1 Message Date
philipp lang 391de0edde Add mobile version for member filter
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2023-08-20 23:37:11 +02:00
philipp lang 232917a8e6 Add composable dir to tailwind content 2023-08-20 23:36:38 +02:00
philipp lang 74b162cc23 Lint 2023-08-20 23:36:26 +02:00
philipp lang 37107915a1 Eslint: ignore multi word component rule 2023-08-20 23:35:57 +02:00
philipp lang 1ef4689d15 Fix: Load member list in mobile view 2023-08-20 22:47:17 +02:00
7 changed files with 156 additions and 59 deletions

View File

@ -3,11 +3,26 @@
"browser": true, "browser": true,
"es2021": true "es2021": true
}, },
"extends": ["eslint:recommended", "plugin:vue/vue3-recommended", "prettier"], "extends": [
"eslint:recommended",
"plugin:vue/vue3-recommended",
"prettier"
],
"parserOptions": { "parserOptions": {
"ecmaVersion": "latest", "ecmaVersion": "latest",
"sourceType": "module" "sourceType": "module"
}, },
"plugins": ["vue"], "plugins": [
"rules": {} "vue"
],
"overrides": [
{
"files": [
"*.vue"
],
"rules": {
"vue/multi-word-component-names": "off"
}
}
]
} }

1
resources/img/svg/filter.svg Executable file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12 12l8-8V0H0v4l8 8v8l4-4v-4z"/></svg>

After

Width:  |  Height:  |  Size: 108 B

View File

@ -0,0 +1,29 @@
<template>
<ui-popup v-if="visible === true" heading="Filtern" @close="visible = false">
<div class="grid gap-3 md:grid-cols-2">
<slot></slot>
</div>
</ui-popup>
<div class="px-6 py-2 border-b border-gray-600 items-center space-x-3" :class="visibleDesktop">
<slot></slot>
</div>
<div class="px-6 py-2 border-b border-gray-600 items-center space-x-3" :class="visibleMobile">
<ui-icon-button icon="filter" @click="visible = true">Filtern</ui-icon-button>
</div>
</template>
<script setup>
import {defineProps, ref} from 'vue';
import useBreakpoints from '../../composables/useBreakpoints.js';
const visible = ref(false);
const props = defineProps({
breakpoint: {
type: String,
required: true,
},
});
const {visibleDesktop, visibleMobile} = useBreakpoints(props);
</script>

View File

@ -1,7 +1,9 @@
<template> <template>
<button v-bind="$attrs" type="button" class="btn label btn-primary"> <button v-bind="$attrs" type="button" class="btn label btn-primary">
<ui-sprite class="w-3 h-3 mr-2" :src="icon"></ui-sprite> <ui-sprite class="w-3 h-3 mr-2" :src="icon"></ui-sprite>
<span><slot></slot></span> <span>
<slot></slot>
</span>
</button> </button>
</template> </template>
@ -9,6 +11,7 @@
export default { export default {
props: { props: {
icon: { icon: {
type: String,
required: true, required: true,
}, },
}, },

View File

@ -0,0 +1,26 @@
import {computed} from 'vue';
export default function (props) {
const visibleMobile = computed(() => {
return {
sm: 'flex sm:hidden',
md: 'flex md:hidden',
lg: 'flex lg:hidden',
xl: 'flex xl:hidden',
}[props.breakpoint];
});
const visibleDesktop = computed(() => {
return {
sm: 'hidden sm:flex',
md: 'hidden md:flex',
lg: 'hidden lg:flex',
xl: 'hidden xl:flex',
}[props.breakpoint];
});
return {
visibleMobile,
visibleDesktop,
};
}

View File

@ -1,23 +1,17 @@
<template> <template>
<page-layout page-class="pb-6"> <page-layout page-class="pb-6">
<template #toolbar> <template #toolbar>
<page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Mitglied <page-toolbar-button :href="meta.links.create" color="primary" icon="plus">Mitglied anlegen</page-toolbar-button>
anlegen</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 v-if="hasModule('bill')" :href="meta.links.allpayment" color="primary" <page-toolbar-button v-if="hasModule('bill')" :href="meta.links.sendpayment" color="info" icon="envelope">Rechnungen versenden</page-toolbar-button>
icon="invoice">Rechnungen erstellen</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 v-if="deleting !== null" heading="Mitglied löschen?" @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 <p class="mt-2">Alle Zuordnungen (Ausbildungen, Rechnungen, Zahlungen, Tätigkeiten) werden ebenfalls entfernt.</p>
entfernt.</p> <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 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 v-if="deleting.member.has_nami" class="mt-5" type="danger"> <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 Dieses Mitglied ist in NaMi vorhanden und wird daher in NaMi abgemeldet werden. Sofern "Datenweiterverwendung" eingeschaltet ist, wird das Mitglied auf inaktiv gesetzt.
"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="#" class="text-center btn btn-danger" @click.prevent="deleting.resolve">Mitglied loschen</a> <a href="#" class="text-center btn btn-danger" @click.prevent="deleting.resolve">Mitglied loschen</a>
@ -25,38 +19,61 @@
</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"> <page-filter breakpoint="xl">
<f-text id="search" :model-value="getFilter('search')" name="search" label="Suchen …" size="sm" <f-text id="search" :model-value="getFilter('search')" name="search" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text>
@update:model-value="setFilter('search', $event)"></f-text> <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-switch v-show="hasModule('bill')" id="ausstand" :model-value="getFilter('ausstand')" label="Nur Ausstände" <f-multipleselect
size="sm" @update:model-value="setFilter('ausstand', $event)"></f-switch> id="group_ids"
<f-multipleselect id="group_ids" :options="meta.groups" :model-value="getFilter('group_ids')" :options="meta.groups"
label="Gruppierungen" size="sm" name="group_ids" :model-value="getFilter('group_ids')"
@update:model-value="setFilter('group_ids', $event)"></f-multipleselect> label="Gruppierungen"
<f-select v-show="hasModule('bill')" id="billKinds" name="billKinds" :options="meta.billKinds" size="sm"
:model-value="getFilter('bill_kind')" label="Rechnung" size="sm" name="group_ids"
@update:model-value="setFilter('bill_kind', $event)"></f-select> @update:model-value="setFilter('group_ids', $event)"
<f-multipleselect id="activity_ids" :options="meta.filterActivities" :model-value="getFilter('activity_ids')" ></f-multipleselect>
label="Tätigkeiten" size="sm" name="activity_ids" <f-select
@update:model-value="setFilter('activity_ids', $event)"></f-multipleselect> v-show="hasModule('bill')"
<f-multipleselect id="subactivity_ids" :options="meta.filterSubactivities" id="billKinds"
:model-value="getFilter('subactivity_ids')" label="Untertätigkeiten" size="sm" name="subactivity_ids" name="billKinds"
@update:model-value="setFilter('subactivity_ids', $event)"></f-multipleselect> :options="meta.billKinds"
:model-value="getFilter('bill_kind')"
label="Rechnung"
size="sm"
@update:model-value="setFilter('bill_kind', $event)"
></f-select>
<f-multipleselect
id="activity_ids"
:options="meta.filterActivities"
:model-value="getFilter('activity_ids')"
label="Tätigkeiten"
size="sm"
name="activity_ids"
@update:model-value="setFilter('activity_ids', $event)"
></f-multipleselect>
<f-multipleselect
id="subactivity_ids"
:options="meta.filterSubactivities"
:model-value="getFilter('subactivity_ids')"
label="Untertätigkeiten"
size="sm"
name="subactivity_ids"
@update:model-value="setFilter('subactivity_ids', $event)"
></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>
<span class="hidden xl:inline">Exportieren</span> <span class="hidden xl:inline">Exportieren</span>
</button> </button>
</div> </page-filter>
<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>
<th>Nachname</th> <th>Nachname</th>
<th>Vorname</th> <th>Vorname</th>
<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 v-show="hasModule('bill')" class="hidden xl:table-cell">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>
@ -65,12 +82,12 @@
<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> <td>
<tags :member="member"></tags> <tags :member="member"></tags>
</td> </td>
<td class="hidden xl:table-cell" v-text="member.age"></td> <td class="!hidden xl:!table-cell" v-text="member.age"></td>
<td v-show="hasModule('bill')" class="hidden xl:table-cell"> <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')">
@ -83,21 +100,18 @@
</table> </table>
<div class="md:hidden p-3 grid gap-3"> <div class="md:hidden p-3 grid gap-3">
<ui-box v-for="(member, index) in data.data" :key="index" class="relative" :heading="member.fullname"> <ui-box v-for="(member, index) in data" :key="index" class="relative" :heading="member.fullname">
<template #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>
</template> </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 v-show="hasModule('bill')" class="text-gray-100 block" :value="member.pending_payment" <ui-label v-show="hasModule('bill')" class="text-gray-100 block" :value="member.pending_payment" fallback=""></ui-label>
fallback=""></ui-label>
</div> </div>
<actions class="mt-2" :member="member" @sidebar="openSidebar(index, $event)" @remove="remove(member)"> <actions class="mt-2" :member="member" @sidebar="openSidebar(index, $event)" @remove="remove(member)"> </actions>
</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 v-tooltip="`Details`" :href="member.links.show"><ui-sprite src="chevron-down" <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>
class="w-6 h-6 text-teal-100 -rotate-90"></ui-sprite></i-link>
</div> </div>
</ui-box> </ui-box>
</div> </div>
@ -106,13 +120,22 @@
<ui-pagination class="mt-4" :value="meta" :only="['data']"></ui-pagination> <ui-pagination class="mt-4" :value="meta" :only="['data']"></ui-pagination>
</div> </div>
<member-payments v-if="single !== null && sidebar === 'payment.index'" :subscriptions="meta.subscriptions" <member-payments
:statuses="meta.statuses" :value="data[single]" @close="closeSidebar"></member-payments> v-if="single !== null && sidebar === 'payment.index'"
<member-memberships v-if="single !== null && sidebar === 'membership.index'" :groups="meta.groups" :subscriptions="meta.subscriptions"
:activities="meta.formActivities" :subactivities="meta.formSubactivities" :value="data[single]" :statuses="meta.statuses"
@close="closeSidebar"></member-memberships> :value="data[single]"
<member-courses v-if="single !== null && sidebar === 'courses.index'" :courses="meta.courses" :value="data[single]" @close="closeSidebar"
@close="closeSidebar"></member-courses> ></member-payments>
<member-memberships
v-if="single !== null && sidebar === 'membership.index'"
:groups="meta.groups"
:activities="meta.formActivities"
:subactivities="meta.formSubactivities"
:value="data[single]"
@close="closeSidebar"
></member-memberships>
<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>

2
tailwind.config.js vendored
View File

@ -1,7 +1,7 @@
const {colors} = require('tailwindcss/defaultTheme'); const {colors} = require('tailwindcss/defaultTheme');
module.exports = { module.exports = {
content: ['resources/js/views/**/*.vue', 'resources/js/components/**/*.vue', 'resources/js/layouts/**/*.vue', 'resources/views/**/*.blade.php'], content: ['resources/js/views/**/*.vue', 'resources/js/components/**/*.vue', 'resources/js/layouts/**/*.vue', 'resources/views/**/*.blade.php', 'resources/js/composables/**/*.js'],
theme: { theme: {
extend: { extend: {
colors: { colors: {