From 6f7fba5ac1e9980831f26e43a3328aa5f0e303ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ars=C3=A8ne=20Reymond?= <66876397+p0lycarpio@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:49:12 +0100 Subject: [PATCH] feat: Add user QueryFilter and improve UI on mobile (#6235) Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com> Co-authored-by: Michael Genson --- .../Domain/Cookbook/CookbookEditor.vue | 5 + .../Household/GroupMealPlanRuleForm.vue | 5 + .../components/Domain/QueryFilterBuilder.vue | 601 +++++++++--------- .../Domain/Recipe/RecipeOrganizerSelector.vue | 41 +- .../composables/use-query-filter-builder.ts | 1 + frontend/lib/api/types/non-generated.ts | 4 +- .../g/[groupSlug]/recipes/finder/index.vue | 21 +- 7 files changed, 367 insertions(+), 311 deletions(-) diff --git a/frontend/components/Domain/Cookbook/CookbookEditor.vue b/frontend/components/Domain/Cookbook/CookbookEditor.vue index 5598fcb1d..aa58714e2 100644 --- a/frontend/components/Domain/Cookbook/CookbookEditor.vue +++ b/frontend/components/Domain/Cookbook/CookbookEditor.vue @@ -83,6 +83,11 @@ const fieldDefs: FieldDefinition[] = [ label: i18n.t("household.households"), type: Organizer.Household, }, + { + name: "user_id", + label: i18n.t("user.users"), + type: Organizer.User, + }, { name: "created_at", label: i18n.t("general.date-created"), diff --git a/frontend/components/Domain/Household/GroupMealPlanRuleForm.vue b/frontend/components/Domain/Household/GroupMealPlanRuleForm.vue index 3d8fb76d6..eed1ec2df 100644 --- a/frontend/components/Domain/Household/GroupMealPlanRuleForm.vue +++ b/frontend/components/Domain/Household/GroupMealPlanRuleForm.vue @@ -106,6 +106,11 @@ const fieldDefs: FieldDefinition[] = [ label: i18n.t("household.households"), type: Organizer.Household, }, + { + name: "user_id", + label: i18n.t("user.users"), + type: Organizer.User, + }, { name: "last_made", label: i18n.t("general.last-made"), diff --git a/frontend/components/Domain/QueryFilterBuilder.vue b/frontend/components/Domain/QueryFilterBuilder.vue index aececb2f9..8bdb2b948 100644 --- a/frontend/components/Domain/QueryFilterBuilder.vue +++ b/frontend/components/Domain/QueryFilterBuilder.vue @@ -1,283 +1,297 @@ + + + + + + - - - - - - + + {{ item.raw }} + + + + + + + + - - - - - - + + {{ item.raw.label }} + + + + + + + + - - - - - - + + {{ item.raw.label }} + + + + + + + + + + + + - - - - - - - - - - - - - + - - + - - - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + - + { - const baseColMaxWidth = 55; + const multiple = fields.value.length > 1; + const adv = state.showAdvanced; + return { col: { - class: "d-flex justify-center align-end field-col pa-1", + class: "d-flex justify-center align-end py-0", }, select: { textClass: "d-flex justify-center text-center", }, items: { icon: { - cols: 1, + cols: (_index: number) => 2, + sm: (_index: number) => 1, style: "width: fit-content;", }, leftParens: { - cols: state.showAdvanced ? 1 : 0, - style: `min-width: ${state.showAdvanced ? baseColMaxWidth : 0}px;`, + cols: (index: number) => (adv ? (index === 0 ? 2 : 0) : 0), + sm: (_index: number) => (adv ? 1 : 0), }, logicalOperator: { - cols: 1, - style: `min-width: ${baseColMaxWidth}px;`, + cols: (_index: number) => 0, + sm: (_index: number) => (multiple ? 1 : 0), }, fieldName: { - cols: state.showAdvanced ? 2 : 3, - style: `min-width: ${state.showAdvanced ? baseColMaxWidth * 2 : baseColMaxWidth * 3}px;`, + cols: (index: number) => { + if (adv) return index === 0 ? 8 : 12; + return index === 0 ? 10 : 12; + }, + sm: (_index: number) => (adv ? 2 : 3), }, relationalOperator: { - cols: 2, - style: `min-width: ${baseColMaxWidth * 2}px;`, + cols: (_index: number) => 12, + sm: (_index: number) => 2, }, fieldValue: { - cols: state.showAdvanced ? 3 : 4, - style: `min-width: ${state.showAdvanced ? baseColMaxWidth * 2 : baseColMaxWidth * 3}px;`, + cols: (index: number) => { + const last = index === fields.value.length - 1; + if (adv) return last ? 8 : 10; + return last ? 10 : 12; + }, + sm: (_index: number) => (adv ? 3 : 4), }, rightParens: { - cols: state.showAdvanced ? 1 : 0, - style: `min-width: ${state.showAdvanced ? baseColMaxWidth : 0}px;`, + cols: (index: number) => (adv ? (index === fields.value.length - 1 ? 2 : 0) : 0), + sm: (_index: number) => (adv ? 1 : 0), }, fieldActions: { - cols: 1, - style: `min-width: ${baseColMaxWidth}px;`, + cols: (index: number) => (index === fields.value.length - 1 ? 2 : 0), + sm: (_index: number) => 1, }, }, }; @@ -651,5 +677,14 @@ const config = computed(() => { diff --git a/frontend/components/Domain/Recipe/RecipeOrganizerSelector.vue b/frontend/components/Domain/Recipe/RecipeOrganizerSelector.vue index 8b63e1bc1..f07da4d4c 100644 --- a/frontend/components/Domain/Recipe/RecipeOrganizerSelector.vue +++ b/frontend/components/Domain/Recipe/RecipeOrganizerSelector.vue @@ -8,14 +8,15 @@ :label="label" chips closable-chips - item-title="name" + :item-title="itemTitle" + item-value="name" multiple :variant="variant" :prepend-inner-icon="icon" :append-icon="showAdd ? $globals.icons.create : undefined" return-object auto-select-first - class="pa-0" + class="pa-0 ma-0" @update:model-value="resetSearchInput" @click:append="dialog = true" > @@ -33,7 +34,6 @@ {{ item.value }} -