feat: add date math for filters #1342

Merged
konrad merged 88 commits from feature/date-math into main 2022-03-28 17:30:43 +00:00
2 changed files with 77 additions and 66 deletions
Showing only changes of commit 7408c37dec - Show all commits

View File

@ -532,9 +532,8 @@
"titleCurrent": "Current Tasks",
"titleDates": "Tasks from {from} until {to}",
"noDates": "Show tasks without dates",
"current": "Current tasks",
"from": "Tasks from",
"until": "until",
"fromuntil": "Tasks from {from} until {until}",
"select": "Select a range:",
"today": "Today",
"nextWeek": "Next Week",
"nextMonth": "Next Month",

View File

@ -8,37 +8,34 @@
>
{{ $t('task.show.noDates') }}
</fancycheckbox>
<h3 v-if="showAll && tasks.length > 0">
{{ $t('task.show.current') }}
<h3 class="mb-2">
{{ pageTitle }}
</h3>
<h3 v-else-if="!showAll" class="mb-2">
{{ $t('task.show.from') }}
<!-- FIXME: Styling, maybe in combination with the buttons? -->
<p class="is-flex" v-if="!showAll">
{{ $t('task.show.select') }}
<flat-pickr
:class="{ 'disabled': loading}"
:config="flatPickerConfig"
:disabled="loading"
@on-close="setDate"
class="input"
v-model="cStartDate"
v-model="dateRange"
/>
{{ $t('task.show.until') }}
<flat-pickr
:class="{ 'disabled': loading}"
:config="flatPickerConfig"
:disabled="loading"
@on-close="setDate"
class="input"
v-model="cEndDate"
/>
</h3>
<div v-if="!showAll" class="mb-4">
<x-button type="secondary" @click="showTodaysTasks()" class="mr-2">{{ $t('task.show.today') }}</x-button>
<x-button type="secondary" @click="setDatesToNextWeek()" class="mr-2">{{ $t('task.show.nextWeek') }}</x-button>
<x-button type="secondary" @click="setDatesToNextMonth()">{{ $t('task.show.nextMonth') }}</x-button>
</p>
<div v-if="!showAll" class="mb-4 mt-2">
<x-button type="secondary" @click="showTodaysTasks()" class="mr-2">
{{ $t('task.show.today') }}
</x-button>
<x-button type="secondary" @click="setDatesToNextWeek()" class="mr-2">
{{ $t('task.show.nextWeek') }}
</x-button>
<x-button type="secondary" @click="setDatesToNextMonth()">
{{ $t('task.show.nextMonth') }}
</x-button>
</div>
<template v-if="!loading && (!tasks || tasks.length === 0) && showNothingToDo">
<h3 class="nothing">{{ $t('task.show.noTasks') }}</h3>
<LlamaCool class="llama-cool" />
<LlamaCool class="llama-cool"/>
</template>
<div :class="{ 'is-loading': loading}" class="spinner"></div>
@ -56,16 +53,20 @@
</div>
</template>
<script>
import SingleTaskInList from '../../components/tasks/partials/singleTaskInList'
import SingleTaskInList from '@/components/tasks/partials/singleTaskInList'
import {mapState} from 'vuex'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import Fancycheckbox from '../../components/input/fancycheckbox'
import {LOADING, LOADING_MODULE} from '../../store/mutation-types'
import Fancycheckbox from '@/components/input/fancycheckbox'
import {LOADING, LOADING_MODULE} from '@/store/mutation-types'
import LlamaCool from '@/assets/llama-cool.svg?component'
function formatDate(date) {
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`
}
export default {
name: 'ShowTasks',
components: {
@ -80,8 +81,7 @@ export default {
showNulls: true,
showOverdue: false,
cStartDate: null,
cEndDate: null,
dateRange: null,
showNothingToDo: false,
}
konrad marked this conversation as resolved Outdated

get value from props to remove dependency on route. Removing dependency from router makes the components easier reusable nested inside another view (which is what we do).

get value from props to remove dependency on route. Removing dependency from router makes the components easier reusable nested inside another view (which is what we do).
@ -92,11 +92,10 @@ export default {
showAll: Boolean,
},
created() {
this.cStartDate = this.startDate
this.cEndDate = this.endDate
this.loadPendingTasks()
},
mounted() {
konrad marked this conversation as resolved Outdated

Unsure: I think eslint doesn't complain here if you use underscore (_) to spread the unused variable.

Unsure: I think eslint doesn't complain here if you use underscore (`_`) to spread the unused variable.

Add ?.[0] so we return the key, which is what we actually want to use and adjust variable name.

Add `?.[0]` so we return the key, which is what we actually want to use and adjust variable name.

Unsure: I think eslint doesn't complain here if you use underscore (_) to spread the unused variable.

Nope, does not seem to have an effect.

> Unsure: I think eslint doesn't complain here if you use underscore (_) to spread the unused variable. Nope, does not seem to have an effect.

Add ?.[0] so we return the key, which is what we actually want to use and adjust variable name.

Done.

> Add ?.[0] so we return the key, which is what we actually want to use and adjust variable name. Done.

Nope, does not seem to have an effect.

Interesting!

> Nope, does not seem to have an effect. Interesting!
// FIXME
konrad marked this conversation as resolved Outdated

Remove title var and return directly:

if (typeof predefinedRange !== 'undefined') {
    return t(`input.datepickerRange.ranges.${predefinedRangeKey}`)
} else {
    return showAll
			? t('task.show.titleCurrent')
			: t('task.show.fromuntil', {
				from: formatDate(dateFrom, 'PPP'),
				until: formatDate(dateTo, 'PPP'),
			})
}
Remove title var and return directly: ```js if (typeof predefinedRange !== 'undefined') { return t(`input.datepickerRange.ranges.${predefinedRangeKey}`) } else { return showAll ? t('task.show.titleCurrent') : t('task.show.fromuntil', { from: formatDate(dateFrom, 'PPP'), until: formatDate(dateTo, 'PPP'), }) }

Done.

Done.
setTimeout(() => this.showNothingToDo = true, 100)
},
watch: {
@ -104,12 +103,6 @@ export default {
handler: 'loadPendingTasks',
deep: true,
},
startDate(newVal) {
this.cStartDate = newVal
},
endDate(newVal) {
this.cEndDate = newVal
},
},
computed: {
flatPickerConfig() {
@ -119,11 +112,38 @@ export default {
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
mode: 'range',
konrad marked this conversation as resolved Outdated

This is a sideeffect inside a computed. Remove this.
Better watch the computed and react to that:

watchEffect(() => setTitle(pageTitle.value))
This is a sideeffect inside a computed. Remove this. Better watch the computed and react to that: ```js watchEffect(() => setTitle(pageTitle.value)) ```

Done.

Done.
locale: {
firstDayOfWeek: this.$store.state.auth.settings.weekStart,
},
}
},
dateFrom() {
const d = new Date(Number(this.$route.query.from))
return !isNaN(d)
? d
: this.startDate
},
dateTo() {
const d = new Date(Number(this.$route.query.to))
return !isNaN(d)
? d
: this.endDate
},
pageTitle() {
const title = this.showAll
? this.$t('task.show.titleCurrent')
: this.$t('task.show.fromuntil', {
from: this.formatDateShort(this.dateFrom),
until: this.formatDateShort(this.dateTo)
})
this.setTitle(title)
return title
},
...mapState({
userAuthenticated: state => state.auth.authenticated,
dpschen marked this conversation as resolved Outdated

Mhh that is a bit annoying that we have to convert to strings here :/
But I also don't have a better idea for now.
Right now this might be still fine, but I remember similar usecases where the conversion from state object to the url representation was quite complex.

We might need to abstract this in the future, so let's keep that in the back of our head =)

Mhh that is a bit annoying that we have to convert to strings here :/ But I also don't have a better idea for now. Right now this might be still fine, but I remember similar usecases where the conversion from state object to the url representation was quite complex. We might need to abstract this in the future, so let's keep that in the back of our head =)
loading: state => state[LOADING] && state[LOADING_MODULE] === 'tasks',
@ -131,11 +151,17 @@ export default {
},
methods: {
setDate() {
if (this.dateRange === null) {
return
}
const [fromDate, toDate] = this.dateRange.split(' to ')
this.$router.push({
name: this.$route.name,
query: {
from: +new Date(this.cStartDate),
to: +new Date(this.cEndDate),
from: +new Date(fromDate),
to: +new Date(toDate),
showOverdue: this.showOverdue,
showNulls: this.showNulls,
},
@ -149,26 +175,9 @@ export default {
return
}
// Make sure all dates are date objects
if (typeof this.$route.query.from !== 'undefined' && typeof this.$route.query.to !== 'undefined') {
this.cStartDate = new Date(Number(this.$route.query.from))
this.cEndDate = new Date(Number(this.$route.query.to))
} else {
this.cStartDate = new Date(this.cStartDate)
this.cEndDate = new Date(this.cEndDate)
}
this.showOverdue = this.$route.query.showOverdue
this.showNulls = this.$route.query.showNulls
if (this.showAll) {
this.setTitle(this.$t('task.show.titleCurrent'))
} else {
this.setTitle(this.$t('task.show.titleDates', {
from: this.cStartDate.toLocaleDateString(),
to: this.cEndDate.toLocaleDateString(),
}))
}
const params = {
sort_by: ['due_date', 'id'],
order_by: ['desc', 'desc'],
@ -181,21 +190,21 @@ export default {
if (!this.showAll) {
if (this.showNulls) {
params.filter_by.push('start_date')
params.filter_value.push(this.cStartDate)
params.filter_value.push(this.dateFrom)
params.filter_comparator.push('greater')
params.filter_by.push('end_date')
params.filter_value.push(this.cEndDate)
params.filter_value.push(this.dateTo)
params.filter_comparator.push('less')
}
params.filter_by.push('due_date')
params.filter_value.push(this.cEndDate)
params.filter_value.push(this.dateFrom)
params.filter_comparator.push('less')
if (!this.showOverdue) {
params.filter_by.push('due_date')
params.filter_value.push(this.cStartDate)
params.filter_value.push(this.dateTo)
params.filter_comparator.push('greater')
}
}
@ -231,23 +240,26 @@ export default {
},
setDatesToNextWeek() {
this.cStartDate = new Date()
this.cEndDate = new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
const startDate = new Date()
const endDate = new Date((new Date()).getTime() + 7 * 24 * 60 * 60 * 1000)
this.dateRange = `${formatDate(startDate)} to ${formatDate(endDate)}`
this.showOverdue = false
this.setDate()
},
setDatesToNextMonth() {
this.cStartDate = new Date()
this.cEndDate = new Date((new Date()).setMonth((new Date()).getMonth() + 1))
const startDate = new Date()
const endDate = new Date((new Date()).setMonth((new Date()).getMonth() + 1))
this.dateRange = `${formatDate(startDate)} to ${formatDate(endDate)}`
this.showOverdue = false
this.setDate()
},
showTodaysTasks() {
const d = new Date()
this.cStartDate = new Date()
this.cEndDate = new Date(d.setDate(d.getDate() + 1))
const startDate = new Date()
const endDate = new Date(d.setDate(d.getDate() + 1))
this.dateRange = `${formatDate(startDate)} to ${formatDate(endDate)}`
this.showOverdue = true
this.setDate()
},