WIP: feat: filters script setup #2474
|
@ -20,7 +20,7 @@
|
|||
:overflow="true"
|
||||
variant="hint-modal"
|
||||
>
|
||||
<filters
|
||||
<Filters
|
||||
:has-title="true"
|
||||
v-model="value"
|
||||
ref="filters"
|
||||
|
@ -30,14 +30,16 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, ref, watch} from 'vue'
|
||||
import {computed, ref, watch, type PropType} from 'vue'
|
||||
|
||||
import Filters from '@/components/list/partials/filters.vue'
|
||||
|
||||
import {getDefaultParams} from '@/composables/taskList'
|
||||
import type {IParams} from '@/types/IParams'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object as PropType<IParams>,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
@ -63,15 +65,15 @@ watch(
|
|||
const hasFilters = computed(() => {
|
||||
// this.value also contains the page parameter which we don't want to include in filters
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {filter_by, filter_value, filter_comparator, filter_concat, s} = value.value
|
||||
const {filterBy, filterValue, filterComparator, filterConcat, s} = value.value
|
||||
const def = {...getDefaultParams()}
|
||||
|
||||
const params = {filter_by, filter_value, filter_comparator, filter_concat, s}
|
||||
const params = {filterBy, filterValue, filterComparator, filterConcat, s}
|
||||
const defaultParams = {
|
||||
filter_by: def.filter_by,
|
||||
filter_value: def.filter_value,
|
||||
filter_comparator: def.filter_comparator,
|
||||
filter_concat: def.filter_concat,
|
||||
filterBy: def.filterBy,
|
||||
filterValue: def.filterValue,
|
||||
filterComparator: def.filterComparator,
|
||||
filterConcat: def.filterConcat,
|
||||
s: s ? def.s : undefined,
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,52 +1,63 @@
|
|||
import {ref, shallowReactive, watch, computed} from 'vue'
|
||||
import {ref, shallowReactive, watch, computed, type Ref} from 'vue'
|
||||
import {useRoute} from 'vue-router'
|
||||
|
||||
import TaskCollectionService from '@/services/taskCollection'
|
||||
import type { ITask } from '@/modelTypes/ITask'
|
||||
|
||||
import type {ITask} from '@/modelTypes/ITask'
|
||||
import type {IList} from '@/modelTypes/IList'
|
||||
import type {IParams, OrderBy, SortBy} from '@/types/IParams'
|
||||
|
||||
// FIXME: merge with DEFAULT_PARAMS in filters.vue
|
||||
export const getDefaultParams = () => ({
|
||||
sort_by: ['position', 'id'],
|
||||
order_by: ['asc', 'desc'],
|
||||
filter_by: ['done'],
|
||||
filter_value: ['false'],
|
||||
filter_comparator: ['equals'],
|
||||
filter_concat: 'and',
|
||||
})
|
||||
sortBy: ['position', 'id'],
|
||||
orderBy: ['asc', 'desc'],
|
||||
filterBy: ['done'],
|
||||
filterValue: ['false'],
|
||||
filterComparator: ['equals'],
|
||||
filterConcat: 'and',
|
||||
} as IParams)
|
||||
|
||||
const SORT_BY_DEFAULT = {
|
||||
type FilterSortOrderMap = Partial<{[key in SortBy]: OrderBy }>
|
||||
|
||||
const SORT_BY_DEFAULT: FilterSortOrderMap = {
|
||||
id: 'desc',
|
||||
}
|
||||
} as const
|
||||
|
||||
/**
|
||||
* This mixin provides a base set of methods and properties to get tasks on a list.
|
||||
*/
|
||||
export function useTaskList(listId, sortByDefault = SORT_BY_DEFAULT) {
|
||||
export function useTaskList(listId: Ref<IList['id']>, sortByDefault = SORT_BY_DEFAULT) {
|
||||
const params = ref({...getDefaultParams()})
|
||||
|
||||
const search = ref('')
|
||||
const page = ref(1)
|
||||
|
||||
const sortBy = ref({ ...sortByDefault })
|
||||
const sortBy = ref<FilterSortOrderMap>({ ...sortByDefault })
|
||||
|
||||
// This makes sure an id sort order is always sorted last.
|
||||
// When tasks would be sorted first by id and then by whatever else was specified, the id sort takes
|
||||
// precedence over everything else, making any other sort columns pretty useless.
|
||||
function formatSortOrder(params) {
|
||||
let hasIdFilter = false
|
||||
const sortKeys = Object.keys(sortBy.value)
|
||||
for (const s of sortKeys) {
|
||||
if (s === 'id') {
|
||||
sortKeys.splice(s, 1)
|
||||
hasIdFilter = true
|
||||
break
|
||||
function formatSortOrder(params: IParams) {
|
||||
const sortEntries = Object.entries(sortBy.value)
|
||||
const sortKeys: SortBy[] = []
|
||||
const orderByValue: OrderBy[] = []
|
||||
let idFilterValue
|
||||
sortEntries.forEach(([key, value], index) => {
|
||||
if (key === 'id') {
|
||||
idFilterValue = value
|
||||
sortEntries.splice(index, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (hasIdFilter) {
|
||||
sortKeys.push(key as SortBy)
|
||||
orderByValue.push(value)
|
||||
})
|
||||
|
||||
if (idFilterValue) {
|
||||
sortKeys.push('id')
|
||||
}
|
||||
params.sort_by = sortKeys
|
||||
params.order_by = sortKeys.map(s => sortBy.value[s])
|
||||
|
||||
params.sortBy = sortKeys
|
||||
params.orderBy = orderByValue
|
||||
|
||||
return params
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import {camelCase} from 'camel-case'
|
||||
import {snakeCase} from 'snake-case'
|
||||
|
||||
export {
|
||||
camelCase,
|
||||
snakeCase,
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms field names to camel case.
|
||||
* @param object
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export function parseDateOrString(rawValue: string | undefined, fallback: any): string | Date {
|
||||
export function parseDateOrString(rawValue: string | undefined, fallback: null) {
|
||||
if (typeof rawValue === 'undefined') {
|
||||
return fallback
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ export function findById<T extends {id: string | number}>(array : T[], id : stri
|
|||
return array.find(({id: currentId}) => currentId === id)
|
||||
}
|
||||
|
||||
export function includesById(array: [], id: string | number) {
|
||||
export function includesById(array: any[], id: string | number) {
|
||||
return array.some(({id: currentId}) => currentId === id)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import type {IAbstract} from './IAbstract'
|
||||
import type {IUser} from './IUser'
|
||||
import type {IFilter} from '@/types/IFilter'
|
||||
import type {IParams} from '@/types/IParams'
|
||||
|
||||
export interface ISavedFilter extends IAbstract {
|
||||
id: number
|
||||
title: string
|
||||
description: string
|
||||
filters: IFilter
|
||||
filters: IParams
|
||||
|
||||
owner: IUser
|
||||
created: Date
|
||||
|
|
|
@ -186,7 +186,7 @@ export default abstract class AbstractService<Model extends IAbstract = IAbstrac
|
|||
* This one here is the default one, usually the service definitions for a model will override this.
|
||||
*/
|
||||
modelFactory(data : Partial<Model>) {
|
||||
return new AbstractModel(data)
|
||||
return data as Model
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,9 +1,32 @@
|
|||
import type { Priority } from '@/constants/priorities'
|
||||
|
||||
export interface IFilter {
|
||||
sortBy: ('done' | 'id')[]
|
||||
orderBy: ('asc' | 'desc')[]
|
||||
filterBy: 'done'[]
|
||||
filterValue: 'false'[]
|
||||
filterComparator: 'equals'[]
|
||||
filterConcat: 'and'
|
||||
filterIncludeNulls: boolean
|
||||
}
|
||||
// where
|
||||
listId: string
|
||||
|
||||
// what
|
||||
assignees: string
|
||||
done: boolean,
|
||||
dueDate: string
|
||||
endDate: string
|
||||
labels: string
|
||||
namespace: string
|
||||
percentDone: number
|
||||
priority: Priority
|
||||
reminders: string
|
||||
startDate: string
|
||||
|
||||
// filter meta
|
||||
requireAllFilters: boolean
|
||||
|
||||
// toggle what
|
||||
usePercentDone: boolean
|
||||
usePriority: boolean
|
||||
}
|
||||
|
||||
export interface IFilterToggles {
|
||||
usePercentDone: boolean
|
||||
usePriority: boolean
|
||||
}
|
||||
|
||||
export type IFilterKey = keyof IFilter
|
|
@ -0,0 +1,38 @@
|
|||
import type { ITask } from '@/modelTypes/ITask'
|
||||
|
||||
export type SortBy =
|
||||
| 'id'
|
||||
| 'title'
|
||||
| 'description'
|
||||
| 'done'
|
||||
| 'doneAt'
|
||||
| 'dueDate'
|
||||
| 'createdById'
|
||||
| 'listId'
|
||||
| 'repeatAfter'
|
||||
| 'priority'
|
||||
| 'start_date'
|
||||
| 'end_date'
|
||||
| 'hex_color'
|
||||
| 'percent_done'
|
||||
| 'uid'
|
||||
| 'created'
|
||||
| 'updated'
|
||||
| 'position'
|
||||
|
||||
export type OrderBy = 'asc' | 'desc'
|
||||
export type FilterComparator = 'equals' | 'greater' | 'greaterEquals' | 'less' | 'lessEquals' | 'like' |'in'
|
||||
|
||||
export type FilterBy = keyof ITask | 'namespace'
|
||||
export type FilterValue = 'false' | string | number | boolean | Date | {dateFrom: string | Date, dateTo: string | Date}
|
||||
|
||||
export interface IParams {
|
||||
sortBy: SortBy[]
|
||||
orderBy: OrderBy[]
|
||||
filterBy: FilterBy[]
|
||||
filterValue: FilterValue[]
|
||||
filterComparator: FilterComparator[]
|
||||
filterConcat: 'and' | 'or'
|
||||
filterIncludeNulls: boolean
|
||||
s: '' // search
|
||||
}
|
|
@ -226,7 +226,7 @@ const {
|
|||
|
||||
|
||||
const isAlphabeticalSorting = computed(() => {
|
||||
return params.value.sort_by.find(sortBy => sortBy === ALPHABETICAL_SORT) !== undefined
|
||||
return params.value.sortBy.find(sortBy => sortBy === ALPHABETICAL_SORT) !== undefined
|
||||
})
|
||||
|
||||
const firstNewPosition = computed(() => {
|
||||
|
|
Reference in New Issue