feat: additional recipe sort behavior (#1858)

* changed default sort direction for certain attrs

* added workaround for filtering out null datetimes

* filtered out null-valued results for certain sorts

* removed unecessary parse

* used minyear instead of 1900
This commit is contained in:
Michael Genson 2022-11-30 23:59:30 -06:00 committed by GitHub
parent 33dffccaa5
commit 1b9ff454fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 17 deletions

View File

@ -243,7 +243,10 @@ export default defineComponent({
cookbook.value, cookbook.value,
category.value, category.value,
tag.value, tag.value,
tool.value tool.value,
// filter out recipes that have a null value for the property we're sorting by
preferences.value.filterNull && preferences.value.orderBy ? `${preferences.value.orderBy} <> null` : null
); );
// since we doubled the first call, we also need to advance the page // since we doubled the first call, we also need to advance the page
@ -270,7 +273,10 @@ export default defineComponent({
cookbook.value, cookbook.value,
category.value, category.value,
tag.value, tag.value,
tool.value tool.value,
// filter out recipes that have a null value for the property we're sorting by
preferences.value.filterNull && preferences.value.orderBy ? `${preferences.value.orderBy} <> null` : null
); );
if (!newRecipes.length) { if (!newRecipes.length) {
hasMore.value = false; hasMore.value = false;
@ -287,10 +293,11 @@ export default defineComponent({
return; return;
} }
function setter(orderBy: string, ascIcon: string, descIcon: string) { function setter(orderBy: string, ascIcon: string, descIcon: string, defaultOrderDirection = "asc", filterNull = false) {
if (preferences.value.orderBy !== orderBy) { if (preferences.value.orderBy !== orderBy) {
preferences.value.orderBy = orderBy; preferences.value.orderBy = orderBy;
preferences.value.orderDirection = "asc"; preferences.value.orderDirection = defaultOrderDirection;
preferences.value.filterNull = filterNull;
} else { } else {
preferences.value.orderDirection = preferences.value.orderDirection === "asc" ? "desc" : "asc"; preferences.value.orderDirection = preferences.value.orderDirection === "asc" ? "desc" : "asc";
} }
@ -299,19 +306,19 @@ export default defineComponent({
switch (sortType) { switch (sortType) {
case EVENTS.az: case EVENTS.az:
setter("name", $globals.icons.sortAlphabeticalAscending, $globals.icons.sortAlphabeticalDescending); setter("name", $globals.icons.sortAlphabeticalAscending, $globals.icons.sortAlphabeticalDescending, "asc", false);
break; break;
case EVENTS.rating: case EVENTS.rating:
setter("rating", $globals.icons.sortAscending, $globals.icons.sortDescending); setter("rating", $globals.icons.sortAscending, $globals.icons.sortDescending, "desc", true);
break; break;
case EVENTS.created: case EVENTS.created:
setter("created_at", $globals.icons.sortCalendarAscending, $globals.icons.sortCalendarDescending); setter("created_at", $globals.icons.sortCalendarAscending, $globals.icons.sortCalendarDescending, "desc", false);
break; break;
case EVENTS.updated: case EVENTS.updated:
setter("update_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending); setter("update_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending, "desc", false);
break; break;
case EVENTS.lastMade: case EVENTS.lastMade:
setter("last_made", $globals.icons.sortCalendarAscending, $globals.icons.sortCalendarDescending); setter("last_made", $globals.icons.sortCalendarAscending, $globals.icons.sortCalendarDescending, "desc", true);
break; break;
default: default:
console.log("Unknown Event", sortType); console.log("Unknown Event", sortType);
@ -335,7 +342,10 @@ export default defineComponent({
cookbook.value, cookbook.value,
category.value, category.value,
tag.value, tag.value,
tool.value tool.value,
// filter out recipes that have a null value for the property we're sorting by
preferences.value.filterNull && preferences.value.orderBy ? `${preferences.value.orderBy} <> null` : null
); );
context.emit(REPLACE_RECIPES_EVENT, newRecipes); context.emit(REPLACE_RECIPES_EVENT, newRecipes);

View File

@ -19,7 +19,8 @@ export const useLazyRecipes = function () {
cookbook: string | null = null, cookbook: string | null = null,
category: string | null = null, category: string | null = null,
tag: string | null = null, tag: string | null = null,
tool: string | null = null tool: string | null = null,
queryFilter: string | null = null,
) { ) {
const { data } = await api.recipes.getAll(page, perPage, { const { data } = await api.recipes.getAll(page, perPage, {
orderBy, orderBy,
@ -28,6 +29,7 @@ export const useLazyRecipes = function () {
categories: category, categories: category,
tags: tag, tags: tag,
tools: tool, tools: tool,
queryFilter,
}); });
return data ? data.items : []; return data ? data.items : [];
} }

View File

@ -4,6 +4,7 @@ import { useLocalStorage } from "@vueuse/core";
export interface UserRecipePreferences { export interface UserRecipePreferences {
orderBy: string; orderBy: string;
orderDirection: string; orderDirection: string;
filterNull: boolean;
sortIcon: string; sortIcon: string;
useMobileCards: boolean; useMobileCards: boolean;
} }
@ -16,6 +17,7 @@ export function useUserSortPreferences(): Ref<UserRecipePreferences> {
{ {
orderBy: "name", orderBy: "name",
orderDirection: "asc", orderDirection: "asc",
filterNull: false,
sortIcon: $globals.icons.sortAlphabeticalAscending, sortIcon: $globals.icons.sortAlphabeticalAscending,
useMobileCards: false, useMobileCards: false,
}, },

View File

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import datetime
import re import re
from enum import Enum from enum import Enum
from typing import Any, TypeVar, cast from typing import Any, TypeVar, cast
@ -96,13 +97,20 @@ class QueryFilter:
value: Any = component.value value: Any = component.value
if isinstance(attr.type, (sqltypes.Date, sqltypes.DateTime)): if isinstance(attr.type, (sqltypes.Date, sqltypes.DateTime)):
try: # TODO: add support for IS NULL and IS NOT NULL
value = date_parser.parse(component.value) # in the meantime, this will work for the specific usecase of non-null dates/datetimes
if value in ["none", "null"] and component.relational_operator == RelationalOperator.NOTEQ:
component.relational_operator = RelationalOperator.GTE
value = datetime.datetime(datetime.MINYEAR, 1, 1)
except ParserError as e: else:
raise ValueError( try:
f"invalid query string: unknown date or datetime format '{component.value}'" value = date_parser.parse(component.value)
) from e
except ParserError as e:
raise ValueError(
f"invalid query string: unknown date or datetime format '{component.value}'"
) from e
if isinstance(attr.type, sqltypes.Boolean): if isinstance(attr.type, sqltypes.Boolean):
try: try: