WIP: feat: filters script setup #2474

Closed
dpschen wants to merge 1 commits from dpschen/frontend:feature/feat-filters-script-setup into main
11 changed files with 595 additions and 489 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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

View File

@ -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
}
/**

View File

@ -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

38
src/types/IParams.ts Normal file
View File

@ -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
}

View File

@ -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(() => {