Commit 85d1d50d authored by schrieveslaach's avatar schrieveslaach

WIP

parent dbdef21c
Pipeline #146800736 passed with stages
in 1 minute and 49 seconds
......@@ -7,13 +7,10 @@ module.exports = {
browser: true,
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard'
'standard',
'@nextcloud'
],
// required to lint *.vue files
plugins: [
'vue'
],
......
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
presets: [
'@vue/cli-plugin-babel/preset',
],
};
......@@ -1069,6 +1069,21 @@
"core-js": "^3.6.4"
}
},
"@nextcloud/eslint-config": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@nextcloud/eslint-config/-/eslint-config-2.0.0.tgz",
"integrity": "sha512-rpBCwFm4/UpJUhGf38CHbOGzoQikvht90JqqbI0GtbpP2Ty1F8Pvr/3ntg+OVeu6utkJL1hybtD9pQswiZfWCg==",
"dev": true
},
"@nextcloud/eslint-plugin": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@nextcloud/eslint-plugin/-/eslint-plugin-1.4.0.tgz",
"integrity": "sha512-w3k04Rj1lBHO4MNhiO4e4WPnijsqTYJhBJ3v+8bYlBi83L5OG+oqu7UHq4ETeDrHVC8QLweu/8vx6iGah00img==",
"dev": true,
"requires": {
"requireindex": "^1.2.0"
}
},
"@nextcloud/event-bus": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@nextcloud/event-bus/-/event-bus-1.1.4.tgz",
......@@ -9808,6 +9823,12 @@
"integrity": "sha1-0LMp7MfMD2Fkn2IhW+aa9UqomJs=",
"dev": true
},
"requireindex": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
"integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==",
"dev": true
},
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/requires-port/download/requires-port-1.0.0.tgz",
......
This diff is collapsed.
<template>
<modal @close="close()">
<template slot="header">
Etiketten<span v-if="cities.length === 1">{{ cities[0] }}</span>
</template>
<div class="label-parameters">
<div>
<label for="group-by-checkbox" style="word-wrap:break-word">
<input id="group-by-checkbox" type="checkbox" @input="groupMembersInput"
:checked="groupMembers" style="vertical-align: middle;">
Gruppieren über Mitgliedsnummer
</label>
<label for="resigned-members-checkbox" style="word-wrap:break-word">
<input id="resigned-members-checkbox" type="checkbox" @input="resignedMembersInput"
:checked="resignedMembers" style="vertical-align: middle;">
Ausgetretene Mitglieder
</label>
</div>
<div>
<label for="addressline-input" class="text-box-label">
Adresszeile
</label>
<input placeholder="Adresszeile" id="addressline-input" @input="addressLineInput"
:value="addressLine" class="text-box-input">
</div>
<div>
<div style="width: 49%; float: left;">
<label for="label-format-select" class="text-box-label">
Etikettenformat
</label>
<select id="label-format-select" v-model="selectedLabelFormat" class="text-box-input">
<option :key="item" v-for="(item, index) in labelFormats" :value="item.id" :selected="index === 0">
{{item.id}} ({{item.size}}, {{item.rows}}&#215;{{item.columns}})
</option>
</select>
</div>
<div style="width: 49%; float: right;">
<label for="label-offset" class="text-box-label">
Anfang leere Etiketten
</label>
<input type="number" min="0" v-model="labelOffset" :max="maxLabelOffset" step="1"
id="label-offset" class="text-box-input">
</div>
</div>
</div>
<object :data="labelsUrl" type="application/pdf" class="labels">
<embed :src="labelsUrl" type="application/pdf"/>
</object>
<p>
<action-link icon="icon-download" :href="labelsUrl" target="_blank">
Download
</action-link>
</p>
</modal>
</template>
<style scoped>
.labels {
padding-top: 25px;
min-width: 600px;
min-height: 250px;
}
.label-parameters {
display: flex;
flex-direction: column;
}
.text-box-label {
display: block;
}
.text-box-input {
width: 100%;
}
</style>
<script>
import { generateUrl } from '@nextcloud/router';
import debounce from 'debounce';
import ActionLink from '@nextcloud/vue/dist/Components/ActionLink';
import Modal from '@nextcloud/vue/dist/Components/Modal';
export default {
data () {
return {
addressLine: '',
groupMembers: false,
resignedMembers: false,
labelFormatData: {},
selectedLabelFormat: null,
labelOffset: 0
};
},
components: {
ActionLink,
Modal
},
mounted () {
if (localStorage.addressLine) {
this.addressLine = localStorage.addressLine;
}
if (localStorage.groupMembers) {
this.groupMembers = localStorage.groupMembers;
}
fetch(generateUrl('/apps/spgverein/labels/formats'))
.then(response => response.json())
.then(formats => {
this.labelFormatData = formats;
if (localStorage.selectedLabelFormat != null) {
this.selectedLabelFormat = localStorage.selectedLabelFormat;
}
});
},
computed: {
maxLabelOffset () {
if (this.labelFormatData == null || this.selectedLabelFormat == null) {
return 0;
}
const format = this.labelFormatData[this.selectedLabelFormat];
return format.columns * format.rows - 1;
},
labelFormats () {
return Object.keys(this.labelFormatData)
.map(id => ({ id, ...this.labelFormatData[id] }));
},
labelsUrl () {
const params = {};
if (this.addressLine.length > 0) {
params.addressLine = this.addressLine;
}
if (this.groupMembers) {
params.groupMembers = this.groupMembers;
}
if (this.resignedMembers) {
params.resignedMembers = this.resignedMembers;
}
if (this.selectedLabelFormat != null) {
params.format = this.selectedLabelFormat;
}
if (this.labelOffset > 0) {
params.offset = this.labelOffset;
}
if (this.cities.length > 0) {
params.cities = this.cities
.map(v => encodeURIComponent(v))
.join(',');
}
const query = Object.keys(params)
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
.join('&');
return generateUrl(`/apps/spgverein/labels/${this.club}?${query}`);
}
},
props: {
cities: {
type: Array
},
club: {
type: String
}
},
methods: {
close () {
this.$emit('close');
},
addressLineInput: debounce(function (e) {
this.addressLine = e.target.value;
}, 750),
groupMembersInput: debounce(function (e) {
const checked = e.target.checked;
this.groupMembers = checked != null && checked;
}, 750),
resignedMembersInput: debounce(function (e) {
const checked = e.target.checked;
this.resignedMembers = checked != null && checked;
}, 750)
},
watch: {
addressLine (newAddressLine) {
localStorage.addressLine = newAddressLine;
},
groupMembers (newGroupMembers) {
localStorage.groupMembers = newGroupMembers;
},
resignedMembers (newResignedMembers) {
localStorage.resignedMembers = newResignedMembers;
},
selectedLabelFormat (newSelectedLabelFormat) {
localStorage.selectedLabelFormat = newSelectedLabelFormat;
}
}
};
</script>
<template>
<div>
Etiketten<span v-if="cities.length === 1">{{ cities[0] }}</span>
<div class="label-parameters">
<div>
<label for="group-by-checkbox" style="word-wrap:break-word">
<input id="group-by-checkbox"
type="checkbox"
:checked="groupMembers"
style="vertical-align: middle;"
@input="groupMembersInput">
Gruppieren über Mitgliedsnummer
</label>
<label for="resigned-members-checkbox" style="word-wrap:break-word">
<input id="resigned-members-checkbox"
type="checkbox"
:checked="resignedMembers"
style="vertical-align: middle;"
@input="resignedMembersInput">
Ausgetretene Mitglieder
</label>
</div>
<div>
<label for="addressline-input" class="text-box-label">
Adresszeile
</label>
<input id="addressline-input"
placeholder="Adresszeile"
:value="addressLine"
class="text-box-input"
@input="addressLineInput">
</div>
<div>
<div style="width: 49%; float: left;">
<label for="label-format-select" class="text-box-label">
Etikettenformat
</label>
<select id="label-format-select" v-model="selectedLabelFormat" class="text-box-input">
<option v-for="(item, index) in labelFormats"
:key="item"
:value="item.id"
:selected="index === 0">
{{ item.id }} ({{ item.size }}, {{ item.rows }}&#215;{{ item.columns }})
</option>
</select>
</div>
<div style="width: 49%; float: right;">
<label for="label-offset" class="text-box-label">
Anfang leere Etiketten
</label>
<input id="label-offset"
v-model="labelOffset"
type="number"
min="0"
:max="maxLabelOffset"
step="1"
class="text-box-input">
</div>
</div>
</div>
<object :data="labelsUrl" type="application/pdf" class="labels">
<embed :src="labelsUrl" type="application/pdf">
</object>
<p>
<ActionLink icon="icon-download" :href="labelsUrl" target="_blank">
Download
</ActionLink>
</p>
</div>
</template>
<style scoped>
.labels {
padding-top: 25px;
min-width: 600px;
min-height: 250px;
}
.label-parameters {
display: flex;
flex-direction: column;
}
.text-box-label {
display: block;
}
.text-box-input {
width: 100%;
}
</style>
<script>
import { generateUrl } from '@nextcloud/router';
import debounce from 'debounce';
import ActionLink from '@nextcloud/vue/dist/Components/ActionLink';
export default {
components: {
ActionLink,
},
props: {
cities: {
type: Array,
required: true,
},
club: {
type: String,
required: true,
},
},
data() {
return {
addressLine: '',
groupMembers: false,
resignedMembers: false,
labelFormatData: {},
selectedLabelFormat: null,
labelOffset: 0,
};
},
computed: {
maxLabelOffset() {
if (this.labelFormatData == null || this.selectedLabelFormat == null) {
return 0;
}
const format = this.labelFormatData[this.selectedLabelFormat];
return format.columns * format.rows - 1;
},
labelFormats() {
return Object.keys(this.labelFormatData)
.map(id => ({ id, ...this.labelFormatData[id] }));
},
labelsUrl() {
const params = {};
if (this.addressLine.length > 0) {
params.addressLine = this.addressLine;
}
if (this.groupMembers) {
params.groupMembers = this.groupMembers;
}
if (this.resignedMembers) {
params.resignedMembers = this.resignedMembers;
}
if (this.selectedLabelFormat != null) {
params.format = this.selectedLabelFormat;
}
if (this.labelOffset > 0) {
params.offset = this.labelOffset;
}
if (this.cities.length > 0) {
params.cities = this.cities
.map(v => encodeURIComponent(v))
.join(',');
}
const query = Object.keys(params)
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k]))
.join('&');
return generateUrl(`/apps/spgverein/labels/${this.club}?${query}`);
},
},
mounted() {
if (localStorage.addressLine) {
this.addressLine = localStorage.addressLine;
}
if (localStorage.groupMembers) {
this.groupMembers = localStorage.groupMembers;
}
fetch(generateUrl('/apps/spgverein/labels/formats'))
.then(response => response.json())
.then(formats => {
this.labelFormatData = formats;
if (localStorage.selectedLabelFormat != null) {
this.selectedLabelFormat = localStorage.selectedLabelFormat;
}
});
},
methods: {
close() {
this.$emit('close');
},
addressLineInput: debounce(function(e) {
this.addressLine = e.target.value;
}, 750),
groupMembersInput: debounce(function(e) {
const checked = e.target.checked;
this.groupMembers = checked != null && checked;
}, 750),
resignedMembersInput: debounce(function(e) {
const checked = e.target.checked;
this.resignedMembers = checked != null && checked;
}, 750),
},
watch: {
addressLine(newAddressLine) {
localStorage.addressLine = newAddressLine;
},
groupMembers(newGroupMembers) {
localStorage.groupMembers = newGroupMembers;
},
resignedMembers(newResignedMembers) {
localStorage.resignedMembers = newResignedMembers;
},
selectedLabelFormat(newSelectedLabelFormat) {
localStorage.selectedLabelFormat = newSelectedLabelFormat;
},
},
};
</script>
import { library } from '@fortawesome/fontawesome-svg-core';
import { faDownload, faFile, faFileExcel, faFilePdf, faPrint, faTimes, faSpinner } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import Vue from 'vue';
import App from './app.vue';
library.add(faDownload);
library.add(faFile);
library.add(faFileExcel);
library.add(faFilePdf);
library.add(faPrint);
library.add(faSpinner);
library.add(faTimes);
Vue.component('font-awesome-icon', FontAwesomeIcon);
Vue.component('app', App);
export default new Vue({
el: '#app',
render: h => h(App)
el: '#content',
render: h => h(App),
});
<template>
<tr :id="member.id">
<td v-for="name in member.fullnames" :key="name">
{{ name }}
</td>
<td>{{ member.street }}</td>
<td>{{ member.zipcode }}</td>
<td>{{ member.city }}</td>
<tr :id="member.id">
<td v-for="name in member.fullnames" :key="name">
{{ name }}
</td>
<td>{{ member.street }}</td>
<td>{{ member.zipcode }}</td>
<td>{{ member.city }}</td>
<td>
<ul>
<li>Geburtsdatum: {{ member.birth }}</li>
<li>Eintrittsdatum: {{ member.admissionDate }}</li>
<li v-if="member.resignationDate">Austrittsdatum: {{ member.resignationDate }}</li>
</ul>
</td>
<td>
<ul>
<li>Geburtsdatum: {{ member.birth }}</li>
<li>Eintrittsdatum: {{ member.admissionDate }}</li>
<li v-if="member.resignationDate">
Austrittsdatum: {{ member.resignationDate }}
</li>
</ul>
</td>
<td>
<template v-if="Object.keys(member.files).length > 0">
<a class="attachment-link" :href="getFileUrl(file)" v-for="file in member.files" :key="file">
<font-awesome-icon icon="file"/>
{{ file }}
</a>
</template>
<template v-else>
<span>&nbsp;</span>
</template>
</td>
</tr>
<td>
<template v-if="Object.keys(member.files).length > 0">
<a v-for="file in member.files"
:key="file"
class="attachment-link"
:href="getFileUrl(file)">
<font-awesome-icon icon="file" />
{{ file }}
</a>
</template>
<template v-else>
<span>&nbsp;</span>
</template>
</td>
</tr>
</template>
<style scoped>
......@@ -43,23 +48,23 @@
import { generateUrl } from '@nextcloud/router';
export default {
data () {
return {};
},
props: {
member: {
type: Object
},
club: {
type: String
}
},
props: {
member: {
type: Object,
},
club: {
type: String,
},
},
data() {
return {};
},
methods: {
getFileUrl (file) {
return generateUrl(`/apps/spgverein/files/${this.club}/${this.member.id}/${file}`);
}
}
methods: {
getFileUrl(file) {
return generateUrl(`/apps/spgverein/files/${this.club}/${this.member.id}/${file}`);
},
},
};
</script>
<template>
<div style="width: 100%">
<template v-for="city in cities">
<section :key="city" class="city" v-if="getMembersOf(city).length > 0">
<div class="city-header">
<h2> {{ city }} </h2>
<a class="button" v-on:click="openLabelsDialog(city)">
<font-awesome-icon icon="print"/>
</a>
</div>
<table>
<colgroup>
<col style="width:25%">
<col style="width:20%">
<col style="width:5%">
<col style="width:20%">
<col style="width:30%">
</colgroup>
<thead>
<tr>
<th>Name</th>
<th>Straße</th>
<th>Postleitzahl</th>
<th>Ort</th>
<th>Datümer</th>
<th>Anhänge</th>
</tr>
</thead>
<tbody>
<member :club="club" v-bind:member="member" v-for="member in getMembersOf(city)" :key="member.id"/>
</tbody>
</table>
</section>
</template>
<labels-modal :club="club" :cities="[selectedCityForLabels]" v-if="showModal" @close="closeLabelsDialog" />
</div>
<div style="width: 100%">
<div class="city-filter">
<Multiselect
v-model="selectedCities"
style="width: 100%"
:options="cities"
:multiple="true"
placeholder="Ort" />
</div>
<table>
<colgroup>
<col style="width:20%">
<col style="width:15%">
<col style="width:5%">
<col style="width:25%">