Skip to content

Commit 7c274de

Browse files
feat: Filter Recipes By Household (and a ton of bug fixes) (#4207)
Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
1 parent 2a6922a commit 7c274de

File tree

65 files changed

+893
-587
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+893
-587
lines changed

frontend/components/Domain/Recipe/RecipeCardSection.vue

Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -69,50 +69,52 @@
6969
@toggle-dense-view="toggleMobileCards()"
7070
/>
7171
</v-app-bar>
72-
<div v-if="recipes" class="mt-2">
73-
<v-row v-if="!useMobileCards">
74-
<v-col v-for="(recipe, index) in recipes" :key="recipe.slug + index" :sm="6" :md="6" :lg="4" :xl="3">
75-
<v-lazy>
76-
<RecipeCard
77-
:name="recipe.name"
78-
:description="recipe.description"
79-
:slug="recipe.slug"
80-
:rating="recipe.rating"
81-
:image="recipe.image"
82-
:tags="recipe.tags"
83-
:recipe-id="recipe.id"
84-
/>
85-
</v-lazy>
86-
</v-col>
87-
</v-row>
88-
<v-row v-else dense>
89-
<v-col
90-
v-for="recipe in recipes"
91-
:key="recipe.name"
92-
cols="12"
93-
:sm="singleColumn ? '12' : '12'"
94-
:md="singleColumn ? '12' : '6'"
95-
:lg="singleColumn ? '12' : '4'"
96-
:xl="singleColumn ? '12' : '3'"
97-
>
98-
<v-lazy>
99-
<RecipeCardMobile
100-
:name="recipe.name"
101-
:description="recipe.description"
102-
:slug="recipe.slug"
103-
:rating="recipe.rating"
104-
:image="recipe.image"
105-
:tags="recipe.tags"
106-
:recipe-id="recipe.id"
107-
/>
108-
</v-lazy>
109-
</v-col>
110-
</v-row>
72+
<div v-if="recipes && ready">
73+
<div class="mt-2">
74+
<v-row v-if="!useMobileCards">
75+
<v-col v-for="(recipe, index) in recipes" :key="recipe.slug + index" :sm="6" :md="6" :lg="4" :xl="3">
76+
<v-lazy>
77+
<RecipeCard
78+
:name="recipe.name"
79+
:description="recipe.description"
80+
:slug="recipe.slug"
81+
:rating="recipe.rating"
82+
:image="recipe.image"
83+
:tags="recipe.tags"
84+
:recipe-id="recipe.id"
85+
/>
86+
</v-lazy>
87+
</v-col>
88+
</v-row>
89+
<v-row v-else dense>
90+
<v-col
91+
v-for="recipe in recipes"
92+
:key="recipe.name"
93+
cols="12"
94+
:sm="singleColumn ? '12' : '12'"
95+
:md="singleColumn ? '12' : '6'"
96+
:lg="singleColumn ? '12' : '4'"
97+
:xl="singleColumn ? '12' : '3'"
98+
>
99+
<v-lazy>
100+
<RecipeCardMobile
101+
:name="recipe.name"
102+
:description="recipe.description"
103+
:slug="recipe.slug"
104+
:rating="recipe.rating"
105+
:image="recipe.image"
106+
:tags="recipe.tags"
107+
:recipe-id="recipe.id"
108+
/>
109+
</v-lazy>
110+
</v-col>
111+
</v-row>
112+
</div>
113+
<v-card v-intersect="infiniteScroll"></v-card>
114+
<v-fade-transition>
115+
<AppLoader v-if="loading" :loading="loading" />
116+
</v-fade-transition>
111117
</div>
112-
<v-card v-intersect="infiniteScroll"></v-card>
113-
<v-fade-transition>
114-
<AppLoader v-if="loading" :loading="loading" />
115-
</v-fade-transition>
116118
</div>
117119
</template>
118120

@@ -223,36 +225,42 @@ export default defineComponent({
223225
224226
const queryFilter = computed(() => {
225227
const orderBy = props.query?.orderBy || preferences.value.orderBy;
226-
return preferences.value.filterNull && orderBy ? `${orderBy} IS NOT NULL` : null;
228+
const orderByFilter = preferences.value.filterNull && orderBy ? `${orderBy} IS NOT NULL` : null;
229+
230+
if (props.query.queryFilter && orderByFilter) {
231+
return `(${props.query.queryFilter}) AND ${orderByFilter}`;
232+
} else if (props.query.queryFilter) {
233+
return props.query.queryFilter;
234+
} else {
235+
return orderByFilter;
236+
}
227237
});
228238
229239
async function fetchRecipes(pageCount = 1) {
230240
return await fetchMore(
231241
page.value,
232-
// we double-up the first call to avoid a bug with large screens that render the entire first page without scrolling, preventing additional loading
233242
perPage * pageCount,
234243
props.query?.orderBy || preferences.value.orderBy,
235244
props.query?.orderDirection || preferences.value.orderDirection,
236245
props.query,
237-
// filter out recipes that have a null value for the property we're sorting by
246+
// we use a computed queryFilter to filter out recipes that have a null value for the property we're sorting by
238247
queryFilter.value
239248
);
240249
}
241250
242251
onMounted(async () => {
243-
if (props.query) {
244-
await initRecipes();
245-
ready.value = true;
246-
}
252+
await initRecipes();
253+
ready.value = true;
247254
});
248255
249-
let lastQuery: string | undefined;
256+
let lastQuery: string | undefined = JSON.stringify(props.query);
250257
watch(
251258
() => props.query,
252259
async (newValue: RecipeSearchQuery | undefined) => {
253260
const newValueString = JSON.stringify(newValue)
254-
if (newValue && (!ready.value || lastQuery !== newValueString)) {
261+
if (lastQuery !== newValueString) {
255262
lastQuery = newValueString;
263+
ready.value = false;
256264
await initRecipes();
257265
ready.value = true;
258266
}
@@ -261,8 +269,12 @@ export default defineComponent({
261269
262270
async function initRecipes() {
263271
page.value = 1;
264-
const newRecipes = await fetchRecipes(2);
265-
if (!newRecipes.length) {
272+
hasMore.value = true;
273+
274+
// we double-up the first call to avoid a bug with large screens that render
275+
// the entire first page without scrolling, preventing additional loading
276+
const newRecipes = await fetchRecipes(page.value + 1);
277+
if (newRecipes.length < perPage) {
266278
hasMore.value = false;
267279
}
268280
@@ -274,17 +286,18 @@ export default defineComponent({
274286
275287
const infiniteScroll = useThrottleFn(() => {
276288
useAsync(async () => {
277-
if (!ready.value || !hasMore.value || loading.value) {
289+
if (!hasMore.value || loading.value) {
278290
return;
279291
}
280292
281293
loading.value = true;
282294
page.value = page.value + 1;
283295
284296
const newRecipes = await fetchRecipes();
285-
if (!newRecipes.length) {
297+
if (newRecipes.length < perPage) {
286298
hasMore.value = false;
287-
} else {
299+
}
300+
if (newRecipes.length) {
288301
context.emit(APPEND_RECIPES_EVENT, newRecipes);
289302
}
290303
@@ -379,6 +392,7 @@ export default defineComponent({
379392
displayTitleIcon,
380393
EVENTS,
381394
infiniteScroll,
395+
ready,
382396
loading,
383397
navigateRandom,
384398
preferences,

frontend/components/Domain/Recipe/RecipeDataTable.vue

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
v-model="selected"
44
item-key="id"
55
show-select
6+
sort-by="dateAdded"
7+
sort-desc
68
:headers="headers"
79
:items="recipes"
810
:items-per-page="15"
@@ -39,6 +41,9 @@
3941
</v-list-item-content>
4042
</v-list-item>
4143
</template>
44+
<template #item.dateAdded="{ item }">
45+
{{ formatDate(item.dateAdded) }}
46+
</template>
4247
</v-data-table>
4348
</template>
4449

@@ -132,6 +137,14 @@ export default defineComponent({
132137
return hdrs;
133138
});
134139
140+
function formatDate(date: string) {
141+
try {
142+
return i18n.d(Date.parse(date), "medium");
143+
} catch {
144+
return "";
145+
}
146+
}
147+
135148
// ============
136149
// Group Members
137150
const api = useUserApi();
@@ -160,6 +173,7 @@ export default defineComponent({
160173
groupSlug,
161174
setValue,
162175
headers,
176+
formatDate,
163177
members,
164178
getMember,
165179
};

0 commit comments

Comments
 (0)