Compare commits
3 Commits
3f02afde85
...
c20ce31f04
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c20ce31f04 | ||
|
|
55b6305a5e | ||
|
|
0026f68320 |
@@ -132,8 +132,8 @@ filterByColumn: Spaltenfilter
|
|||||||
filterByColumnValue: Spaltenwerte
|
filterByColumnValue: Spaltenwerte
|
||||||
saveAsDefault: Aus Standard spichere
|
saveAsDefault: Aus Standard spichere
|
||||||
day: Tag
|
day: Tag
|
||||||
MondayShort: Mäntig
|
MondayShort: Mo
|
||||||
Monday: Mo
|
Monday: Mäntig
|
||||||
Tuesday: Zistig
|
Tuesday: Zistig
|
||||||
TuesdayShort: Di
|
TuesdayShort: Di
|
||||||
Wednesday: Mittwuch
|
Wednesday: Mittwuch
|
||||||
@@ -165,3 +165,7 @@ hintFilterEventName: "*'IIgabe'* * oder % als filler vorher oder nächer"
|
|||||||
total: Gesamt
|
total: Gesamt
|
||||||
exportPdf: PDF exportiere
|
exportPdf: PDF exportiere
|
||||||
print: Drucke
|
print: Drucke
|
||||||
|
today: Hüt
|
||||||
|
week: Wuche
|
||||||
|
month: Monat
|
||||||
|
year: Jahr
|
||||||
|
|||||||
@@ -165,3 +165,7 @@ hintFilterEventName: "*'Eingabe'* * oder % als Filler vorher oder nachher"
|
|||||||
total: Gesamt
|
total: Gesamt
|
||||||
exportPdf: PDF exportieren
|
exportPdf: PDF exportieren
|
||||||
print: Drucken
|
print: Drucken
|
||||||
|
today: Heute
|
||||||
|
week: Woche
|
||||||
|
month: Monat
|
||||||
|
year: Jahr
|
||||||
|
|||||||
@@ -165,3 +165,7 @@ hintFilterEventName: "*'Input'* * or % as filler before oder after"
|
|||||||
total: Total
|
total: Total
|
||||||
exportPdf: export PDF
|
exportPdf: export PDF
|
||||||
print: Print
|
print: Print
|
||||||
|
today: Today
|
||||||
|
week: Week
|
||||||
|
month: Month
|
||||||
|
year: Year
|
||||||
|
|||||||
@@ -165,3 +165,7 @@ hintFilterEventName: "*''Entrada'* * o % como relleno antes o después"
|
|||||||
total: Total
|
total: Total
|
||||||
exportPdf: exportar PDF
|
exportPdf: exportar PDF
|
||||||
print: Imprimir
|
print: Imprimir
|
||||||
|
today: Hoy
|
||||||
|
week: Semana
|
||||||
|
month: Mes
|
||||||
|
year: Año
|
||||||
|
|||||||
@@ -14,6 +14,21 @@
|
|||||||
:val="opt.value"
|
:val="opt.value"
|
||||||
:label="opt.label"
|
:label="opt.label"
|
||||||
/>
|
/>
|
||||||
|
<q-tabs
|
||||||
|
v-model="activeTab"
|
||||||
|
dense
|
||||||
|
class="text-primary"
|
||||||
|
active-color="primary"
|
||||||
|
indicator-color="primary"
|
||||||
|
align="justify"
|
||||||
|
narrow-indicator
|
||||||
|
@update:model-value="onTabChange"
|
||||||
|
>
|
||||||
|
<q-tab no-caps name="today" :label="$t('today')" />
|
||||||
|
<q-tab no-caps name="week" :label="$t('week')" />
|
||||||
|
<q-tab no-caps name="month" :label="$t('month')" />
|
||||||
|
<q-tab no-caps name="year" :label="$t('year')" />
|
||||||
|
</q-tabs>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<q-date v-model="dateRange" range flat />
|
<q-date v-model="dateRange" range flat />
|
||||||
@@ -22,7 +37,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, watch } from 'vue';
|
import { ref, onMounted, watch, type PropType } from 'vue';
|
||||||
import { date } from 'quasar';
|
import { date } from 'quasar';
|
||||||
import { i18n } from 'src/boot/lang';
|
import { i18n } from 'src/boot/lang';
|
||||||
|
|
||||||
@@ -32,11 +47,17 @@ const props = defineProps({
|
|||||||
width: { type: Number, default: 300 },
|
width: { type: Number, default: 300 },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const weekdays = defineModel('weekdays', {
|
||||||
|
type: Array as PropType<number[]>,
|
||||||
|
default: () => [0, 3],
|
||||||
|
});
|
||||||
|
|
||||||
const startDate = new Date();
|
const startDate = new Date();
|
||||||
|
|
||||||
|
const activeTab = ref('');
|
||||||
// Initial range (format: YYYY-MM-DD)
|
// Initial range (format: YYYY-MM-DD)
|
||||||
const dateRange = ref<string | { to: string; from: string }>('');
|
const dateRange = ref<string | { to: string; from: string }>('');
|
||||||
const selectedWeekdays = ref([0, 3]); // Default to weekdays
|
const selectedWeekdays = ref(weekdays); // Default to weekdays
|
||||||
|
|
||||||
const emit = defineEmits(['update:dates']);
|
const emit = defineEmits(['update:dates']);
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -53,6 +74,14 @@ const weekdayOptions = [
|
|||||||
{ label: i18n.global.t('SundayShort'), value: 0 },
|
{ label: i18n.global.t('SundayShort'), value: 0 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const onTabChange = (val: 'today' | 'week' | 'month' | 'year') => {
|
||||||
|
if (val) setRange(val);
|
||||||
|
// Optional: Reset tab to empty so user can click the same tab again later
|
||||||
|
setTimeout(() => {
|
||||||
|
activeTab.value = '';
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
// The Logic: Calculate all specific dates within the range that match weekdays
|
// The Logic: Calculate all specific dates within the range that match weekdays
|
||||||
watch(dateRange, () => {
|
watch(dateRange, () => {
|
||||||
if (!dateRange.value) {
|
if (!dateRange.value) {
|
||||||
@@ -81,4 +110,81 @@ watch(dateRange, () => {
|
|||||||
emit('update:dates', result);
|
emit('update:dates', result);
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logic for 1 Week / 1 Month / 1 Year
|
||||||
|
*/
|
||||||
|
const setRange = (type: 'today' | 'week' | 'month' | 'year') => {
|
||||||
|
const anchor =
|
||||||
|
typeof dateRange.value === 'string'
|
||||||
|
? new Date(dateRange.value)
|
||||||
|
: new Date(dateRange.value.from);
|
||||||
|
|
||||||
|
let from: Date;
|
||||||
|
let to: Date;
|
||||||
|
|
||||||
|
if (type === 'today') {
|
||||||
|
const now = new Date();
|
||||||
|
dateRange.value = date.formatDate(date.startOfDate(now, 'day'), 'YYYY-MM-DD');
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else if (type === 'week') {
|
||||||
|
// getDay() returns 0 for Sunday.
|
||||||
|
// We calculate how many days to subtract to get to Monday (1).
|
||||||
|
const day = anchor.getDay();
|
||||||
|
const diffToMonday = day === 0 ? -6 : 1 - day;
|
||||||
|
|
||||||
|
from = date.addToDate(anchor, { days: diffToMonday });
|
||||||
|
from = date.startOfDate(from, 'day'); // Reset time to 00:00
|
||||||
|
|
||||||
|
to = date.addToDate(from, { days: 6 });
|
||||||
|
to = date.endOfDate(to, 'day'); // Set time to 23:59
|
||||||
|
} else if (type === 'month') {
|
||||||
|
// 'month' is a valid unit for startOfDate
|
||||||
|
from = date.startOfDate(anchor, 'month');
|
||||||
|
to = date.endOfDate(anchor, 'month');
|
||||||
|
} else {
|
||||||
|
// 'year' is a valid unit for startOfDate
|
||||||
|
from = date.startOfDate(anchor, 'year');
|
||||||
|
to = date.endOfDate(anchor, 'year');
|
||||||
|
}
|
||||||
|
|
||||||
|
dateRange.value = {
|
||||||
|
from: date.formatDate(from, 'YYYY/MM/DD'),
|
||||||
|
to: date.formatDate(to, 'YYYY/MM/DD'),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[dateRange, selectedWeekdays],
|
||||||
|
() => {
|
||||||
|
if (!dateRange.value) {
|
||||||
|
emit('update:dates', []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let start: Date, end: Date;
|
||||||
|
|
||||||
|
if (typeof dateRange.value === 'string') {
|
||||||
|
start = new Date(dateRange.value);
|
||||||
|
end = new Date(dateRange.value);
|
||||||
|
} else {
|
||||||
|
start = new Date(dateRange.value.from);
|
||||||
|
end = new Date(dateRange.value.to);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = [];
|
||||||
|
let current = start;
|
||||||
|
|
||||||
|
while (current <= end) {
|
||||||
|
if (selectedWeekdays.value.includes(current.getDay())) {
|
||||||
|
result.push(date.formatDate(current, 'YYYY-MM-DD'));
|
||||||
|
}
|
||||||
|
current = date.addToDate(current, { days: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('update:dates', result);
|
||||||
|
},
|
||||||
|
{ deep: true },
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<q-item dense>
|
<q-item dense>
|
||||||
<q-item-section>
|
<q-item-section>
|
||||||
<q-item-label class="text-bold text-primary text-center">
|
<q-item-label class="text-bold text-primary text-center">
|
||||||
{{ opt.name }}
|
{{ $t(opt.name) }}
|
||||||
</q-item-label>
|
</q-item-label>
|
||||||
</q-item-section>
|
</q-item-section>
|
||||||
</q-item>
|
</q-item>
|
||||||
@@ -46,7 +46,7 @@ import type { PropType } from 'vue';
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
amounts: {
|
amounts: {
|
||||||
type: Object as PropType<Amount[]>,
|
type: Array as PropType<Amount[]>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Dark } from 'quasar';
|
import { Dark } from 'quasar';
|
||||||
import { appName, databaseName, type Settings } from 'src/vueLib/models/settings';
|
import { appName, databaseName, type Settings } from 'src/vueLib/models/settings';
|
||||||
import { updateOrAddObject } from 'src/vueLib/utils/utils';
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
export function setLocalSettings(settings: Settings) {
|
export function setLocalSettings(settings: Settings) {
|
||||||
@@ -79,30 +78,19 @@ export function getLocalLanguage(): string | null {
|
|||||||
|
|
||||||
type pageDefault = {
|
type pageDefault = {
|
||||||
page: string;
|
page: string;
|
||||||
stringValue: string;
|
data: unknown;
|
||||||
filteredValues: string[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type pageDefaults = pageDefault[];
|
type pageDefaults = pageDefault[];
|
||||||
|
|
||||||
const pageDefaults = ref<pageDefaults>([]);
|
const pageDefaults = ref<pageDefaults>([]);
|
||||||
|
|
||||||
export function setLocalPageDefaults(
|
export function setLocalPageDefaults(page: string, data: unknown) {
|
||||||
page: string,
|
localStorage.setItem(page + 'Defaults', JSON.stringify(data));
|
||||||
filteredColumn?: string,
|
|
||||||
filteredValue?: string[],
|
|
||||||
) {
|
|
||||||
updateOrAddObject(
|
|
||||||
pageDefaults.value,
|
|
||||||
{ page: page, stringValue: filteredColumn, filteredValues: filteredValue },
|
|
||||||
'page',
|
|
||||||
);
|
|
||||||
localStorage.setItem('pageDefaults', JSON.stringify(pageDefaults.value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocalPageDefaults(page: string): pageDefault | null {
|
export function getLocalPageDefaults(page: string): unknown {
|
||||||
const defaults = localStorage.getItem('pageDefaults');
|
const defaults = localStorage.getItem(page + 'Defaults');
|
||||||
if (!defaults) return null;
|
if (!defaults) return null;
|
||||||
pageDefaults.value = JSON.parse(defaults);
|
return JSON.parse(defaults);
|
||||||
return pageDefaults.value.find((e) => e.page === page) || null;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
:label="$t('selectDates')"
|
:label="$t('selectDates')"
|
||||||
@show="loadSettings"
|
@show="loadSettings"
|
||||||
>
|
>
|
||||||
<DateDaySelect @update:dates="updateReport" />
|
<DateDaySelect @update:dates="updateReport" v-model:weekdays="weekdays" />
|
||||||
<div class="column justify-end q-pa-md">
|
<div class="column justify-end q-pa-md">
|
||||||
<div class="row q-ma-md">
|
<div class="row q-ma-md">
|
||||||
<q-input
|
<q-input
|
||||||
@@ -136,6 +136,7 @@ import ReportStat from 'src/components/ReportStat.vue';
|
|||||||
import type { Group, Groups } from 'src/vueLib/models/group';
|
import type { Group, Groups } from 'src/vueLib/models/group';
|
||||||
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
|
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
|
||||||
import html2pdf from 'html2pdf.js';
|
import html2pdf from 'html2pdf.js';
|
||||||
|
import type { PageDefault } from 'src/vueLib/models/pagedefaults';
|
||||||
|
|
||||||
const filter = ref<string>('');
|
const filter = ref<string>('');
|
||||||
const group = ref<Group[]>([]);
|
const group = ref<Group[]>([]);
|
||||||
@@ -148,6 +149,7 @@ const dropdownRef = ref();
|
|||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const amounts = ref<Amount[]>([]);
|
const amounts = ref<Amount[]>([]);
|
||||||
const reportExportRef = ref<HTMLElement | null>(null);
|
const reportExportRef = ref<HTMLElement | null>(null);
|
||||||
|
const weekdays = ref<number[]>([0, 3]);
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
@@ -186,10 +188,13 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function loadSettings() {
|
function loadSettings() {
|
||||||
const settings = getLocalPageDefaults('report');
|
const settings = getLocalPageDefaults('report') as PageDefault;
|
||||||
if (!settings) return;
|
if (!settings) return;
|
||||||
if (settings.filteredValues.length > 0) {
|
if (settings.groups) {
|
||||||
group.value = JSON.parse(settings?.filteredValues[0] || '') as Groups;
|
group.value = settings.groups;
|
||||||
|
}
|
||||||
|
if (settings.weekdays) {
|
||||||
|
weekdays.value = settings.weekdays;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function applyDateChoice() {
|
function applyDateChoice() {
|
||||||
@@ -214,7 +219,7 @@ function applyDateChoice() {
|
|||||||
}
|
}
|
||||||
payload.date = allDates.value;
|
payload.date = allDates.value;
|
||||||
|
|
||||||
setLocalPageDefaults('report', filter.value || '', [JSON.stringify(group.value || '')]);
|
setLocalPageDefaults('report', <PageDefault>{ groups: group.value, weekdays: weekdays.value });
|
||||||
|
|
||||||
appApi
|
appApi
|
||||||
.post('report', payload)
|
.post('report', payload)
|
||||||
@@ -248,7 +253,7 @@ function applyDateChoice() {
|
|||||||
.filter((day) => data.data[day]) // Only include days that exist in the response
|
.filter((day) => data.data[day]) // Only include days that exist in the response
|
||||||
.map((day) => ({
|
.map((day) => ({
|
||||||
...data.data[day],
|
...data.data[day],
|
||||||
name: i18n.global.t(day), // Dynamically translate the name
|
name: day, // Dynamically translate the name
|
||||||
}));
|
}));
|
||||||
if (amounts.value.length == 2) {
|
if (amounts.value.length == 2) {
|
||||||
amounts.value.splice(1);
|
amounts.value.splice(1);
|
||||||
|
|||||||
8
src/vueLib/models/pageDefaults.ts
Normal file
8
src/vueLib/models/pageDefaults.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type { Group } from './group';
|
||||||
|
|
||||||
|
export type PageDefault = {
|
||||||
|
groups?: Group[];
|
||||||
|
selectedColumnFilter?: string;
|
||||||
|
selectedColumnOptions?: string[];
|
||||||
|
weekdays?: number[];
|
||||||
|
};
|
||||||
@@ -66,6 +66,7 @@ import { ref } from 'vue';
|
|||||||
import { useAttendeesTable } from './AttendeesTable';
|
import { useAttendeesTable } from './AttendeesTable';
|
||||||
import { useMemberTable } from '../members/MembersTable';
|
import { useMemberTable } from '../members/MembersTable';
|
||||||
import { getLocalPageDefaults } from 'src/localstorage/localStorage';
|
import { getLocalPageDefaults } from 'src/localstorage/localStorage';
|
||||||
|
import type { PageDefault } from 'src/vueLib/models/pagedefaults';
|
||||||
|
|
||||||
//use constants and function of imports
|
//use constants and function of imports
|
||||||
const { attendees, updateAttendees } = useAttendeesTable();
|
const { attendees, updateAttendees } = useAttendeesTable();
|
||||||
@@ -95,8 +96,10 @@ const open = async (eventArray: number, event: Event) => {
|
|||||||
await updateMembers(event.attendees);
|
await updateMembers(event.attendees);
|
||||||
|
|
||||||
// set custom filter
|
// set custom filter
|
||||||
const defaults = getLocalPageDefaults('attendance');
|
const settings = getLocalPageDefaults('attendance') as PageDefault;
|
||||||
setNewFilter(defaults?.stringValue || '', ...(defaults?.filteredValues ?? []));
|
if (settings) {
|
||||||
|
setNewFilter(settings.selectedColumnFilter || '', ...(settings.selectedColumnOptions ?? []));
|
||||||
|
}
|
||||||
|
|
||||||
// set amount of missing attendace
|
// set amount of missing attendace
|
||||||
missingAttendanceAmount.value = filteredMembers.value.length;
|
missingAttendanceAmount.value = filteredMembers.value.length;
|
||||||
|
|||||||
@@ -209,12 +209,13 @@ import AddToEvent from 'src/components/AddToEvent.vue';
|
|||||||
import { databaseName } from 'src/vueLib/models/settings';
|
import { databaseName } from 'src/vueLib/models/settings';
|
||||||
import { useUserStore } from 'src/vueLib/login/userStore';
|
import { useUserStore } from 'src/vueLib/login/userStore';
|
||||||
import { i18n } from 'src/boot/lang';
|
import { i18n } from 'src/boot/lang';
|
||||||
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
|
|
||||||
import type { Responsible } from 'src/vueLib/models/responsible';
|
import type { Responsible } from 'src/vueLib/models/responsible';
|
||||||
import type { QTableColumn } from 'quasar';
|
import type { QTableColumn } from 'quasar';
|
||||||
import SearchableInput from '../components/SearchableInput.vue';
|
import SearchableInput from '../components/SearchableInput.vue';
|
||||||
import FilterSelect from '../components/FilterSelect.vue';
|
import FilterSelect from '../components/FilterSelect.vue';
|
||||||
import TopButtonGroup from '../components/TopButtonGroup.vue';
|
import TopButtonGroup from '../components/TopButtonGroup.vue';
|
||||||
|
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
|
||||||
|
import type { PageDefault } from 'src/vueLib/models/pagedefaults';
|
||||||
|
|
||||||
const inProps = defineProps({
|
const inProps = defineProps({
|
||||||
addAttendees: { type: Boolean },
|
addAttendees: { type: Boolean },
|
||||||
@@ -285,9 +286,11 @@ onMounted(() => {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
localCompareMembers.value = inProps.compareMembers;
|
localCompareMembers.value = inProps.compareMembers;
|
||||||
|
|
||||||
const defaults = getLocalPageDefaults(page.value);
|
const settings = getLocalPageDefaults(page.value) as PageDefault;
|
||||||
selectedColumnFilter.value = defaults?.stringValue || '';
|
if (settings) {
|
||||||
selectedColumnOptions.value = defaults?.filteredValues ?? [];
|
selectedColumnFilter.value = settings.selectedColumnFilter || '';
|
||||||
|
selectedColumnOptions.value = settings.selectedColumnOptions || [];
|
||||||
|
}
|
||||||
|
|
||||||
// set custom filter
|
// set custom filter
|
||||||
setNewFilter(selectedColumnFilter.value, ...selectedColumnOptions.value);
|
setNewFilter(selectedColumnFilter.value, ...selectedColumnOptions.value);
|
||||||
@@ -369,7 +372,11 @@ function setColumnOptions(columnName: string) {
|
|||||||
|
|
||||||
async function filterMembers() {
|
async function filterMembers() {
|
||||||
setNewFilter(selectedColumnFilter.value, ...(selectedColumnOptions.value || []));
|
setNewFilter(selectedColumnFilter.value, ...(selectedColumnOptions.value || []));
|
||||||
setLocalPageDefaults(page.value, selectedColumnFilter.value, selectedColumnOptions.value || []);
|
const settings = <PageDefault>{
|
||||||
|
selectedColumnFilter: selectedColumnFilter.value,
|
||||||
|
selectedColumnOptions: selectedColumnOptions.value,
|
||||||
|
};
|
||||||
|
setLocalPageDefaults(page.value, settings);
|
||||||
await updateTable();
|
await updateTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user