mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-05-24 01:12:54 -04:00
fix: 2148 infinite scroll on search (#2173)
* refactor recipe card section to use unified query construct * rework search page so it uses lazy-loading of RecipeCardSection * remove RecipeQuery again * prettier reformatting * remove recipes/all page * remove max results setting from search * fix typing issues
This commit is contained in:
parent
afbee3a078
commit
541cdc79aa
@ -53,7 +53,7 @@
|
||||
<v-icon left>
|
||||
{{ $globals.icons.chefHat }}
|
||||
</v-icon>
|
||||
<v-list-item-title>{{ $t('general.last-made') }}</v-list-item-title>
|
||||
<v-list-item-title>{{ $t("general.last-made") }}</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
@ -129,6 +129,7 @@ import {
|
||||
useAsync,
|
||||
useContext,
|
||||
useRouter,
|
||||
watch,
|
||||
} from "@nuxtjs/composition-api";
|
||||
import { useThrottleFn } from "@vueuse/core";
|
||||
import RecipeCard from "./RecipeCard.vue";
|
||||
@ -137,6 +138,7 @@ import { useAsyncKey } from "~/composables/use-utils";
|
||||
import { useLazyRecipes } from "~/composables/recipes";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import { useUserSortPreferences } from "~/composables/use-users/preferences";
|
||||
import { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
|
||||
|
||||
const REPLACE_RECIPES_EVENT = "replaceRecipes";
|
||||
const APPEND_RECIPES_EVENT = "appendRecipes";
|
||||
@ -167,26 +169,10 @@ export default defineComponent({
|
||||
type: Array as () => Recipe[],
|
||||
default: () => [],
|
||||
},
|
||||
cookbookSlug: {
|
||||
type: String,
|
||||
query: {
|
||||
type: Object as () => RecipeSearchQuery,
|
||||
default: null,
|
||||
},
|
||||
categorySlug: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
tagSlug: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
toolSlug: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
skipLoad: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, context) {
|
||||
const preferences = useUserSortPreferences();
|
||||
@ -224,45 +210,59 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const page = ref(1);
|
||||
const perPage = ref(32);
|
||||
const perPage = 32;
|
||||
const hasMore = ref(true);
|
||||
const ready = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
const cookbook = ref<string>(props.cookbookSlug);
|
||||
const category = ref<string>(props.categorySlug);
|
||||
const tag = ref<string>(props.tagSlug);
|
||||
const tool = ref<string>(props.toolSlug);
|
||||
|
||||
const { fetchMore } = useLazyRecipes();
|
||||
|
||||
onMounted(async () => {
|
||||
if (props.skipLoad) {
|
||||
return;
|
||||
}
|
||||
const newRecipes = await fetchMore(
|
||||
page.value,
|
||||
|
||||
// we double-up the first call to avoid a bug with large screens that render the entire first page without scrolling, preventing additional loading
|
||||
perPage.value * 2,
|
||||
preferences.value.orderBy,
|
||||
preferences.value.orderDirection,
|
||||
cookbook.value,
|
||||
category.value,
|
||||
tag.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
|
||||
page.value = page.value + 1;
|
||||
|
||||
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
|
||||
ready.value = true;
|
||||
const queryFilter = computed(() => {
|
||||
const orderBy = props.query?.orderBy || preferences.value.orderBy;
|
||||
return preferences.value.filterNull && orderBy ? `${orderBy} <> null` : null;
|
||||
});
|
||||
|
||||
async function fetchRecipes(pageCount = 1) {
|
||||
return await fetchMore(
|
||||
page.value,
|
||||
// we double-up the first call to avoid a bug with large screens that render the entire first page without scrolling, preventing additional loading
|
||||
perPage * pageCount,
|
||||
props.query?.orderBy || preferences.value.orderBy,
|
||||
props.query?.orderDirection || preferences.value.orderDirection,
|
||||
props.query,
|
||||
// filter out recipes that have a null value for the property we're sorting by
|
||||
queryFilter.value
|
||||
);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (props.query) {
|
||||
const newRecipes = await fetchRecipes(2);
|
||||
|
||||
// since we doubled the first call, we also need to advance the page
|
||||
page.value = page.value + 1;
|
||||
|
||||
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
|
||||
ready.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.query,
|
||||
async (newValue: RecipeSearchQuery | undefined) => {
|
||||
if (newValue) {
|
||||
page.value = 1;
|
||||
const newRecipes = await fetchRecipes(2);
|
||||
|
||||
// since we doubled the first call, we also need to advance the page
|
||||
page.value = page.value + 1;
|
||||
|
||||
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
|
||||
ready.value = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const infiniteScroll = useThrottleFn(() => {
|
||||
useAsync(async () => {
|
||||
if (!ready.value || !hasMore.value || loading.value) {
|
||||
@ -272,19 +272,7 @@ export default defineComponent({
|
||||
loading.value = true;
|
||||
page.value = page.value + 1;
|
||||
|
||||
const newRecipes = await fetchMore(
|
||||
page.value,
|
||||
perPage.value,
|
||||
preferences.value.orderBy,
|
||||
preferences.value.orderDirection,
|
||||
cookbook.value,
|
||||
category.value,
|
||||
tag.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
|
||||
);
|
||||
const newRecipes = await fetchRecipes();
|
||||
if (!newRecipes.length) {
|
||||
hasMore.value = false;
|
||||
} else {
|
||||
@ -300,7 +288,13 @@ export default defineComponent({
|
||||
return;
|
||||
}
|
||||
|
||||
function setter(orderBy: string, ascIcon: string, descIcon: string, defaultOrderDirection = "asc", filterNull = false) {
|
||||
function setter(
|
||||
orderBy: string,
|
||||
ascIcon: string,
|
||||
descIcon: string,
|
||||
defaultOrderDirection = "asc",
|
||||
filterNull = false
|
||||
) {
|
||||
if (preferences.value.orderBy !== orderBy) {
|
||||
preferences.value.orderBy = orderBy;
|
||||
preferences.value.orderDirection = defaultOrderDirection;
|
||||
@ -313,19 +307,37 @@ export default defineComponent({
|
||||
|
||||
switch (sortType) {
|
||||
case EVENTS.az:
|
||||
setter("name", $globals.icons.sortAlphabeticalAscending, $globals.icons.sortAlphabeticalDescending, "asc", false);
|
||||
setter(
|
||||
"name",
|
||||
$globals.icons.sortAlphabeticalAscending,
|
||||
$globals.icons.sortAlphabeticalDescending,
|
||||
"asc",
|
||||
false
|
||||
);
|
||||
break;
|
||||
case EVENTS.rating:
|
||||
setter("rating", $globals.icons.sortAscending, $globals.icons.sortDescending, "desc", true);
|
||||
break;
|
||||
case EVENTS.created:
|
||||
setter("created_at", $globals.icons.sortCalendarAscending, $globals.icons.sortCalendarDescending, "desc", false);
|
||||
setter(
|
||||
"created_at",
|
||||
$globals.icons.sortCalendarAscending,
|
||||
$globals.icons.sortCalendarDescending,
|
||||
"desc",
|
||||
false
|
||||
);
|
||||
break;
|
||||
case EVENTS.updated:
|
||||
setter("update_at", $globals.icons.sortClockAscending, $globals.icons.sortClockDescending, "desc", false);
|
||||
break;
|
||||
case EVENTS.lastMade:
|
||||
setter("last_made", $globals.icons.sortCalendarAscending, $globals.icons.sortCalendarDescending, "desc", true);
|
||||
setter(
|
||||
"last_made",
|
||||
$globals.icons.sortCalendarAscending,
|
||||
$globals.icons.sortCalendarDescending,
|
||||
"desc",
|
||||
true
|
||||
);
|
||||
break;
|
||||
default:
|
||||
console.log("Unknown Event", sortType);
|
||||
@ -341,19 +353,7 @@ export default defineComponent({
|
||||
loading.value = true;
|
||||
|
||||
// fetch new recipes
|
||||
const newRecipes = await fetchMore(
|
||||
page.value,
|
||||
perPage.value,
|
||||
preferences.value.orderBy,
|
||||
preferences.value.orderDirection,
|
||||
cookbook.value,
|
||||
category.value,
|
||||
tag.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
|
||||
);
|
||||
const newRecipes = await fetchRecipes();
|
||||
context.emit(REPLACE_RECIPES_EVENT, newRecipes);
|
||||
|
||||
state.sortLoading = false;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Ref, useAsync } from "@nuxtjs/composition-api";
|
||||
import { useAsyncKey } from "../use-utils";
|
||||
import { BaseCRUDAPI } from "~/lib/api/base/base-clients";
|
||||
import { QueryValue } from "~/lib/api/base/route";
|
||||
|
||||
type BoundT = {
|
||||
id?: string | number;
|
||||
@ -25,7 +26,7 @@ export function useStoreActions<T extends BoundT>(
|
||||
allRef: Ref<T[] | null> | null,
|
||||
loading: Ref<boolean>
|
||||
): StoreActions<T> {
|
||||
function getAll(page = 1, perPage = -1, params = {} as any) {
|
||||
function getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
|
||||
params.orderBy ??= "name";
|
||||
params.orderDirection ??= "asc";
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { useAsync, ref } from "@nuxtjs/composition-api";
|
||||
import { useAsyncKey } from "../use-utils";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import {Recipe} from "~/lib/api/types/recipe";
|
||||
import {RecipeSearchQuery} from "~/lib/api/user/recipes/recipe";
|
||||
|
||||
export const allRecipes = ref<Recipe[]>([]);
|
||||
export const recentRecipes = ref<Recipe[]>([]);
|
||||
@ -16,19 +17,22 @@ export const useLazyRecipes = function () {
|
||||
perPage: number,
|
||||
orderBy: string | null = null,
|
||||
orderDirection = "desc",
|
||||
cookbook: string | null = null,
|
||||
category: string | null = null,
|
||||
tag: string | null = null,
|
||||
tool: string | null = null,
|
||||
query: RecipeSearchQuery | null = null,
|
||||
queryFilter: string | null = null,
|
||||
) {
|
||||
const { data } = await api.recipes.getAll(page, perPage, {
|
||||
orderBy,
|
||||
orderDirection,
|
||||
cookbook,
|
||||
categories: category,
|
||||
tags: tag,
|
||||
tools: tool,
|
||||
search: query?.search,
|
||||
cookbook: query?.cookbook,
|
||||
categories: query?.categories,
|
||||
requireAllCategories: query?.requireAllCategories,
|
||||
tags: query?.tags,
|
||||
requireAllTags: query?.requireAllTags,
|
||||
tools: query?.tools,
|
||||
requireAllTools: query?.requireAllTools,
|
||||
foods: query?.foods,
|
||||
requireAllFoods: query?.requireAllFoods,
|
||||
queryFilter,
|
||||
});
|
||||
return data ? data.items : [];
|
||||
|
@ -169,11 +169,6 @@ export default defineComponent({
|
||||
to: "/shopping-lists",
|
||||
restricted: true,
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.viewModule,
|
||||
to: "/recipes/all",
|
||||
title: i18n.t("sidebar.all-recipes"),
|
||||
},
|
||||
{
|
||||
icon: $globals.icons.tags,
|
||||
to: "/recipes/categories",
|
||||
|
@ -50,7 +50,6 @@ export default defineComponent({
|
||||
|
||||
const buttons = [
|
||||
{ icon: $globals.icons.home, to: "/", text: i18n.t("general.home") },
|
||||
{ icon: $globals.icons.primary, to: "/recipes/all", text: i18n.t("page.all-recipes") },
|
||||
];
|
||||
|
||||
return {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Recipe } from "../types/recipe";
|
||||
import { ApiRequestInstance, PaginationData } from "~/lib/api/types/non-generated";
|
||||
import { QueryValue, route } from "~/lib/api/base/route";
|
||||
|
||||
export interface CrudAPIInterface {
|
||||
requests: ApiRequestInstance;
|
||||
@ -21,14 +22,14 @@ export abstract class BaseAPI {
|
||||
|
||||
export abstract class BaseCRUDAPI<CreateType, ReadType, UpdateType = CreateType>
|
||||
extends BaseAPI
|
||||
implements CrudAPIInterface {
|
||||
implements CrudAPIInterface
|
||||
{
|
||||
abstract baseRoute: string;
|
||||
abstract itemRoute(itemId: string | number): string;
|
||||
|
||||
async getAll(page = 1, perPage = -1, params = {} as any) {
|
||||
return await this.requests.get<PaginationData<ReadType>>(this.baseRoute, {
|
||||
params: { page, perPage, ...params },
|
||||
});
|
||||
async getAll(page = 1, perPage = -1, params = {} as Record<string, QueryValue>) {
|
||||
params = Object.fromEntries(Object.entries(params).filter(([_, v]) => v !== null && v !== undefined));
|
||||
return await this.requests.get<PaginationData<ReadType>>(route(this.baseRoute, { page, perPage, ...params }));
|
||||
}
|
||||
|
||||
async createOne(payload: CreateType) {
|
||||
|
@ -21,7 +21,6 @@ export type QueryValue = string | string[] | number | number[] | boolean | null
|
||||
*/
|
||||
export function route(rest: string, params: Record<string, QueryValue> | null = null): string {
|
||||
const url = new URL(parts.prefix + rest, parts.host);
|
||||
|
||||
if (params) {
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (Array.isArray(value)) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
*/
|
||||
/* This file was automatically generated from pydantic models by running pydantic2ts.
|
||||
/* Do not modify it by hand - just update the pydantic models and then re-run the script
|
||||
*/
|
||||
|
||||
export type ExportTypes = "json";
|
||||
export type RegisteredParser = "nlp" | "brute";
|
||||
|
@ -55,9 +55,9 @@ const routes = {
|
||||
recipesSlugTimelineEventId: (slug: string, id: string) => `${prefix}/recipes/${slug}/timeline/events/${id}`,
|
||||
};
|
||||
|
||||
export type RecipeSearchQuery ={
|
||||
export type RecipeSearchQuery = {
|
||||
search: string;
|
||||
orderDirection? : "asc" | "desc";
|
||||
orderDirection?: "asc" | "desc";
|
||||
groupId?: string;
|
||||
|
||||
queryFilter?: string;
|
||||
@ -76,11 +76,10 @@ export type RecipeSearchQuery ={
|
||||
foods?: string[];
|
||||
requireAllFoods?: boolean;
|
||||
|
||||
page: number;
|
||||
perPage: number;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
orderBy?: string;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export class RecipeAPI extends BaseCRUDAPI<CreateRecipe, Recipe, Recipe> {
|
||||
baseRoute: string = routes.recipesBase;
|
||||
@ -96,7 +95,7 @@ export class RecipeAPI extends BaseCRUDAPI<CreateRecipe, Recipe, Recipe> {
|
||||
this.share = new RecipeShareApi(requests);
|
||||
}
|
||||
|
||||
async search(rsq : RecipeSearchQuery) {
|
||||
async search(rsq: RecipeSearchQuery) {
|
||||
return await this.requests.get<PaginationData<Recipe>>(route(routes.recipesBase, rsq));
|
||||
}
|
||||
|
||||
@ -176,7 +175,10 @@ export class RecipeAPI extends BaseCRUDAPI<CreateRecipe, Recipe, Recipe> {
|
||||
}
|
||||
|
||||
async updateTimelineEvent(recipeSlug: string, eventId: string, payload: RecipeTimelineEventUpdate) {
|
||||
return await this.requests.put<RecipeTimelineEventOut, RecipeTimelineEventUpdate>(routes.recipesSlugTimelineEventId(recipeSlug, eventId), payload);
|
||||
return await this.requests.put<RecipeTimelineEventOut, RecipeTimelineEventUpdate>(
|
||||
routes.recipesSlugTimelineEventId(recipeSlug, eventId),
|
||||
payload
|
||||
);
|
||||
}
|
||||
|
||||
async deleteTimelineEvent(recipeSlug: string, eventId: string) {
|
||||
@ -184,8 +186,11 @@ export class RecipeAPI extends BaseCRUDAPI<CreateRecipe, Recipe, Recipe> {
|
||||
}
|
||||
|
||||
async getAllTimelineEvents(recipeSlug: string, page = 1, perPage = -1, params = {} as any) {
|
||||
return await this.requests.get<PaginationData<RecipeTimelineEventOut>>(routes.recipesSlugTimelineEvent(recipeSlug), {
|
||||
params: { page, perPage, ...params },
|
||||
});
|
||||
return await this.requests.get<PaginationData<RecipeTimelineEventOut>>(
|
||||
routes.recipesSlugTimelineEvent(recipeSlug),
|
||||
{
|
||||
params: { page, perPage, ...params },
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
<RecipeCardSection
|
||||
class="mb-5 mx-1"
|
||||
:recipes="recipes"
|
||||
:cookbook-slug="slug"
|
||||
:query="{ cookbook: slug }"
|
||||
@sortRecipes="assignSorted"
|
||||
@replaceRecipes="replaceRecipes"
|
||||
@appendRecipes="appendRecipes"
|
||||
|
@ -98,15 +98,6 @@
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
v-model="state.maxResults"
|
||||
class="mt-0 pt-0"
|
||||
:label="$tc('search.max-results')"
|
||||
type="number"
|
||||
outlined
|
||||
dense
|
||||
hide-details
|
||||
/>
|
||||
<v-switch v-model="state.auto" label="Auto Search" single-line></v-switch>
|
||||
<v-btn block color="primary" @click="reset">
|
||||
{{ $tc("general.reset") }}
|
||||
@ -116,7 +107,7 @@
|
||||
</v-menu>
|
||||
</div>
|
||||
<div v-if="!state.auto" class="search-button-container">
|
||||
<v-btn :loading="state.loading" x-large color="primary" type="submit" block>
|
||||
<v-btn x-large color="primary" type="submit" block>
|
||||
<v-icon left>
|
||||
{{ $globals.icons.search }}
|
||||
</v-icon>
|
||||
@ -131,38 +122,41 @@
|
||||
class="mt-n5"
|
||||
:icon="$globals.icons.search"
|
||||
:title="$tc('search.results')"
|
||||
:recipes="state.results"
|
||||
/>
|
||||
:recipes="recipes"
|
||||
:query="passedQuery"
|
||||
@sortRecipes="assignSorted"
|
||||
@replaceRecipes="replaceRecipes"
|
||||
@appendRecipes="appendRecipes"
|
||||
@delete="removeRecipe"
|
||||
></RecipeCardSection>
|
||||
</v-container>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, defineComponent, useRouter, onMounted, useContext, computed } from "@nuxtjs/composition-api";
|
||||
// eslint-disable-next-line import/namespace
|
||||
import { watchDebounced } from "@vueuse/shared";
|
||||
import SearchFilter from "~/components/Domain/SearchFilter.vue";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { useCategoryStore, useFoodStore, useTagStore, useToolStore } from "~/composables/store";
|
||||
import RecipeCardSection from "~/components/Domain/Recipe/RecipeCardSection.vue";
|
||||
import { IngredientFood, RecipeCategory, RecipeSummary, RecipeTag, RecipeTool } from "~/lib/api/types/recipe";
|
||||
import { IngredientFood, RecipeCategory, RecipeTag, RecipeTool } from "~/lib/api/types/recipe";
|
||||
import { NoUndefinedField } from "~/lib/api/types/non-generated";
|
||||
import { useLazyRecipes } from "~/composables/recipes";
|
||||
import { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
|
||||
|
||||
export default defineComponent({
|
||||
components: { SearchFilter, RecipeCardSection },
|
||||
setup() {
|
||||
const { recipes, appendRecipes, assignSorted, removeRecipe, replaceRecipes } = useLazyRecipes();
|
||||
|
||||
const router = useRouter();
|
||||
const api = useUserApi();
|
||||
const { $globals, i18n } = useContext();
|
||||
|
||||
const state = ref({
|
||||
auto: true,
|
||||
loading: false,
|
||||
search: "",
|
||||
orderBy: "created_at",
|
||||
orderDirection: "desc" as "asc" | "desc",
|
||||
maxResults: 21,
|
||||
results: [] as RecipeSummary[],
|
||||
|
||||
// and/or
|
||||
requireAllCategories: false,
|
||||
@ -183,9 +177,10 @@ export default defineComponent({
|
||||
const tools = useToolStore();
|
||||
const selectedTools = ref<NoUndefinedField<RecipeTool>[]>([]);
|
||||
|
||||
const passedQuery = ref<RecipeSearchQuery | null>(null);
|
||||
|
||||
function reset() {
|
||||
state.value.search = "";
|
||||
state.value.maxResults = 21;
|
||||
state.value.orderBy = "created_at";
|
||||
state.value.orderDirection = "desc";
|
||||
state.value.requireAllCategories = false;
|
||||
@ -213,7 +208,6 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
async function search() {
|
||||
state.value.loading = true;
|
||||
await router.push({
|
||||
query: {
|
||||
categories: toIDArray(selectedCategories.value),
|
||||
@ -225,7 +219,6 @@ export default defineComponent({
|
||||
...{
|
||||
auto: state.value.auto ? undefined : "false",
|
||||
search: state.value.search === "" ? undefined : state.value.search,
|
||||
maxResults: state.value.maxResults === 21 ? undefined : state.value.maxResults.toString(),
|
||||
orderBy: state.value.orderBy === "createdAt" ? undefined : state.value.orderBy,
|
||||
orderDirection: state.value.orderDirection === "desc" ? undefined : state.value.orderDirection,
|
||||
requireAllCategories: state.value.requireAllCategories ? "true" : undefined,
|
||||
@ -236,35 +229,19 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
|
||||
const { data, error } = await api.recipes.search({
|
||||
passedQuery.value = {
|
||||
search: state.value.search,
|
||||
page: 1,
|
||||
orderBy: state.value.orderBy,
|
||||
orderDirection: state.value.orderDirection,
|
||||
perPage: state.value.maxResults,
|
||||
categories: toIDArray(selectedCategories.value),
|
||||
foods: toIDArray(selectedFoods.value),
|
||||
tags: toIDArray(selectedTags.value),
|
||||
tools: toIDArray(selectedTools.value),
|
||||
|
||||
requireAllCategories: state.value.requireAllCategories,
|
||||
requireAllTags: state.value.requireAllTags,
|
||||
requireAllTools: state.value.requireAllTools,
|
||||
requireAllFoods: state.value.requireAllFoods,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
state.value.loading = false;
|
||||
state.value.results = [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
state.value.results = data.items;
|
||||
}
|
||||
|
||||
state.value.loading = false;
|
||||
orderBy: state.value.orderBy,
|
||||
orderDirection: state.value.orderDirection,
|
||||
};
|
||||
}
|
||||
|
||||
function waitUntilAndExecute(
|
||||
@ -345,10 +322,6 @@ export default defineComponent({
|
||||
state.value.search = query.search as string;
|
||||
}
|
||||
|
||||
if (query.maxResults) {
|
||||
state.value.maxResults = parseInt(query.maxResults as string);
|
||||
}
|
||||
|
||||
if (query.orderBy) {
|
||||
state.value.orderBy = query.orderBy as string;
|
||||
}
|
||||
@ -429,7 +402,6 @@ export default defineComponent({
|
||||
() => state.value.requireAllFoods,
|
||||
() => state.value.orderBy,
|
||||
() => state.value.orderDirection,
|
||||
() => state.value.maxResults,
|
||||
selectedCategories,
|
||||
selectedFoods,
|
||||
selectedTags,
|
||||
@ -462,6 +434,12 @@ export default defineComponent({
|
||||
selectedFoods,
|
||||
selectedTags,
|
||||
selectedTools,
|
||||
appendRecipes,
|
||||
assignSorted,
|
||||
recipes,
|
||||
removeRecipe,
|
||||
replaceRecipes,
|
||||
passedQuery,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@ -1,32 +0,0 @@
|
||||
<template>
|
||||
<v-container>
|
||||
<RecipeCardSection
|
||||
:icon="$globals.icons.primary"
|
||||
:title="$t('page.all-recipes')"
|
||||
:recipes="recipes"
|
||||
@sortRecipes="assignSorted"
|
||||
@replaceRecipes="replaceRecipes"
|
||||
@appendRecipes="appendRecipes"
|
||||
@delete="removeRecipe"
|
||||
></RecipeCardSection>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api";
|
||||
import RecipeCardSection from "~/components/Domain/Recipe/RecipeCardSection.vue";
|
||||
import { useLazyRecipes } from "~/composables/recipes";
|
||||
|
||||
export default defineComponent({
|
||||
components: { RecipeCardSection },
|
||||
setup() {
|
||||
const { recipes, appendRecipes, assignSorted, removeRecipe, replaceRecipes } = useLazyRecipes();
|
||||
return { appendRecipes, assignSorted, recipes, removeRecipe, replaceRecipes };
|
||||
},
|
||||
head() {
|
||||
return {
|
||||
title: this.$t("page.all-recipes") as string,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
@ -5,7 +5,7 @@
|
||||
:icon="$globals.icons.tags"
|
||||
:title="category.name"
|
||||
:recipes="recipes"
|
||||
:category-slug="category.slug"
|
||||
:query="{ categories: [category.slug] }"
|
||||
@sortRecipes="assignSorted"
|
||||
@replaceRecipes="replaceRecipes"
|
||||
@appendRecipes="appendRecipes"
|
||||
|
@ -5,7 +5,7 @@
|
||||
:icon="$globals.icons.tags"
|
||||
:title="tag.name"
|
||||
:recipes="recipes"
|
||||
:tag-slug="tag.slug"
|
||||
:query="{ tags: [tag.slug] }"
|
||||
@sortRecipes="assignSorted"
|
||||
@replaceRecipes="replaceRecipes"
|
||||
@appendRecipes="appendRecipes"
|
||||
|
@ -5,7 +5,7 @@
|
||||
:icon="$globals.icons.potSteam"
|
||||
:title="tool.name"
|
||||
:recipes="recipes"
|
||||
:tool-slug="tool.slug"
|
||||
:query="{ tools: [tool.slug] }"
|
||||
@sortRecipes="assignSorted"
|
||||
@replaceRecipes="replaceRecipes"
|
||||
@appendRecipes="appendRecipes"
|
||||
|
@ -301,7 +301,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
const statsTo: { [key: string]: string } = {
|
||||
totalRecipes: "/recipes/all",
|
||||
totalRecipes: "/",
|
||||
totalUsers: "/group/members",
|
||||
totalCategories: "/recipes/categories",
|
||||
totalTags: "/recipes/tags",
|
||||
|
Loading…
x
Reference in New Issue
Block a user