diff --git a/.gitignore b/.gitignore index f2596031..51a835a4 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,7 @@ dist .pnpm-store backend/app/api/app -backend/app/api/__debug_bin +backend/app/api/__debug_bin* dist/ # Nuxt Publish Dir diff --git a/.vscode/launch.json b/.vscode/launch.json index d3753958..8c6ce98a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -25,6 +25,7 @@ "HBOX_STORAGE_DATA": "${workspaceRoot}/backend/.data", "HBOX_STORAGE_SQLITE_URL": "${workspaceRoot}/backend/.data/homebox.db?_fk=1" }, + "console": "integratedTerminal", }, { "name": "Launch Frontend", @@ -38,10 +39,11 @@ "cwd": "${workspaceFolder}/frontend", "serverReadyAction": { "action": "debugWithChrome", - "pattern": "Local: http://localhost:([0-9]+)", + "pattern": "Local: +http://localhost:([0-9]+)", "uriFormat": "http://localhost:%s", "webRoot": "${workspaceFolder}/frontend" - } + }, + "console": "integratedTerminal", } ] } \ No newline at end of file diff --git a/backend/app/api/handlers/v1/v1_ctrl_items.go b/backend/app/api/handlers/v1/v1_ctrl_items.go index f53377db..a978d031 100644 --- a/backend/app/api/handlers/v1/v1_ctrl_items.go +++ b/backend/app/api/handlers/v1/v1_ctrl_items.go @@ -58,6 +58,7 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc { Search: params.Get("q"), LocationIDs: queryUUIDList(params, "locations"), LabelIDs: queryUUIDList(params, "labels"), + NegateLabels: queryBool(params.Get("negateLabels")), ParentItemIDs: queryUUIDList(params, "parentIds"), IncludeArchived: queryBool(params.Get("includeArchived")), Fields: filterFieldItems(params["fields"]), diff --git a/backend/internal/data/repo/repo_items.go b/backend/internal/data/repo/repo_items.go index e88a18ff..c714e229 100644 --- a/backend/internal/data/repo/repo_items.go +++ b/backend/internal/data/repo/repo_items.go @@ -36,6 +36,7 @@ type ( AssetID AssetID `json:"assetId"` LocationIDs []uuid.UUID `json:"locationIds"` LabelIDs []uuid.UUID `json:"labelIds"` + NegateLabels bool `json:"negateLabels"` ParentItemIDs []uuid.UUID `json:"parentIds"` SortBy string `json:"sortBy"` IncludeArchived bool `json:"includeArchived"` @@ -365,10 +366,17 @@ func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q Ite if len(q.LabelIDs) > 0 { labelPredicates := make([]predicate.Item, 0, len(q.LabelIDs)) for _, l := range q.LabelIDs { - labelPredicates = append(labelPredicates, item.HasLabelWith(label.ID(l))) + if !q.NegateLabels { + labelPredicates = append(labelPredicates, item.HasLabelWith(label.ID(l))) + } else { + labelPredicates = append(labelPredicates, item.Not(item.HasLabelWith(label.ID(l)))) + } + } + if !q.NegateLabels { + andPredicates = append(andPredicates, item.Or(labelPredicates...)) + } else { + andPredicates = append(andPredicates, item.And(labelPredicates...)) } - - andPredicates = append(andPredicates, item.Or(labelPredicates...)) } if len(q.LocationIDs) > 0 { diff --git a/frontend/lib/api/classes/items.ts b/frontend/lib/api/classes/items.ts index a5d3f2e9..22fbda2d 100644 --- a/frontend/lib/api/classes/items.ts +++ b/frontend/lib/api/classes/items.ts @@ -23,6 +23,7 @@ export type ItemsQuery = { pageSize?: number; locations?: string[]; labels?: string[]; + negateLabels?: boolean; parentIds?: string[]; q?: string; fields?: string[]; diff --git a/frontend/pages/items.vue b/frontend/pages/items.vue index cb207b15..2213975b 100644 --- a/frontend/pages/items.vue +++ b/frontend/pages/items.vue @@ -39,6 +39,7 @@ const advanced = useRouteQuery("advanced", false); const includeArchived = useRouteQuery("archived", false); const fieldSelector = useRouteQuery("fieldSelector", false); + const negateLabels = useRouteQuery("negateLabels", false); const totalPages = computed(() => Math.ceil(total.value / pageSize.value)); const hasNext = computed(() => page.value * pageSize.value < total.value); @@ -162,6 +163,12 @@ } }); + watch(negateLabels, (newV, oldV) => { + if (newV !== oldV) { + search(); + } + }); + async function fetchValues(field: string): Promise { if (fieldValuesCache.value[field]) { return fieldValuesCache.value[field]; @@ -193,6 +200,7 @@ page: page.value, pageSize: pageSize.value, includeArchived: includeArchived.value ? "true" : "false", + negateLabels: negateLabels.value ? "true" : "false", }, }); } @@ -219,6 +227,7 @@ q: query.value || "", locations: locIDs.value, labels: labIDs.value, + negateLabels: negateLabels.value, includeArchived: includeArchived.value, page: page.value, pageSize: pageSize.value, @@ -268,6 +277,7 @@ advanced: "true", archived: includeArchived.value ? "true" : "false", fieldSelector: fieldSelector.value ? "true" : "false", + negateLabels: negateLabels.value ? "true" : "false", pageSize: pageSize.value, page: page.value, q: query.value, @@ -359,6 +369,10 @@ Field Selector +
Reset Search