diff --git a/backend/app/api/docs/docs.go b/backend/app/api/docs/docs.go index 346ee39..1e00622 100644 --- a/backend/app/api/docs/docs.go +++ b/backend/app/api/docs/docs.go @@ -70,6 +70,34 @@ const docTemplate = `{ "Items" ], "summary": "Get All Items", + "parameters": [ + { + "type": "string", + "description": "search string", + "name": "q", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "label Ids", + "name": "labels", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "location Ids", + "name": "locations", + "in": "query" + } + ], "responses": { "200": { "description": "OK", diff --git a/backend/app/api/docs/swagger.json b/backend/app/api/docs/swagger.json index 4408d9c..84d37a9 100644 --- a/backend/app/api/docs/swagger.json +++ b/backend/app/api/docs/swagger.json @@ -62,6 +62,34 @@ "Items" ], "summary": "Get All Items", + "parameters": [ + { + "type": "string", + "description": "search string", + "name": "q", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "label Ids", + "name": "labels", + "in": "query" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "location Ids", + "name": "locations", + "in": "query" + } + ], "responses": { "200": { "description": "OK", diff --git a/backend/app/api/docs/swagger.yaml b/backend/app/api/docs/swagger.yaml index 876519e..e6d2af0 100644 --- a/backend/app/api/docs/swagger.yaml +++ b/backend/app/api/docs/swagger.yaml @@ -426,6 +426,25 @@ paths: - User /v1/items: get: + parameters: + - description: search string + in: query + name: q + type: string + - collectionFormat: multi + description: label Ids + in: query + items: + type: string + name: labels + type: array + - collectionFormat: multi + description: location Ids + in: query + items: + type: string + name: locations + type: array produces: - application/json responses: diff --git a/backend/app/api/v1/v1_ctrl_items.go b/backend/app/api/v1/v1_ctrl_items.go index 328ab21..ab231e4 100644 --- a/backend/app/api/v1/v1_ctrl_items.go +++ b/backend/app/api/v1/v1_ctrl_items.go @@ -3,24 +3,51 @@ package v1 import ( "encoding/csv" "net/http" + "net/url" + "github.com/google/uuid" "github.com/hay-kot/homebox/backend/internal/repo" "github.com/hay-kot/homebox/backend/internal/services" "github.com/hay-kot/homebox/backend/pkgs/server" "github.com/rs/zerolog/log" ) +func uuidList(params url.Values, key string) []uuid.UUID { + var ids []uuid.UUID + for _, id := range params[key] { + uid, err := uuid.Parse(id) + if err != nil { + continue + } + ids = append(ids, uid) + } + return ids +} + +func extractQuery(r *http.Request) repo.ItemQuery { + params := r.URL.Query() + + return repo.ItemQuery{ + Search: params.Get("q"), + LocationIDs: uuidList(params, "locations"), + LabelIDs: uuidList(params, "labels"), + } +} + // HandleItemsGetAll godoc // @Summary Get All Items // @Tags Items // @Produce json -// @Success 200 {object} server.Results{items=[]repo.ItemSummary} +// @Param q query string false "search string" +// @Param labels query []string false "label Ids" collectionFormat(multi) +// @Param locations query []string false "location Ids" collectionFormat(multi) +// @Success 200 {object} server.Results{items=[]repo.ItemSummary} // @Router /v1/items [GET] // @Security Bearer func (ctrl *V1Controller) HandleItemsGetAll() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - user := services.UseUserCtx(r.Context()) - items, err := ctrl.svc.Items.GetAll(r.Context(), user.GroupID) + ctx := services.NewContext(r.Context()) + items, err := ctrl.svc.Items.Query(ctx, extractQuery(r)) if err != nil { log.Err(err).Msg("failed to get items") server.RespondServerError(w) diff --git a/backend/internal/repo/repo_items.go b/backend/internal/repo/repo_items.go index e6375f7..57a4dbb 100644 --- a/backend/internal/repo/repo_items.go +++ b/backend/internal/repo/repo_items.go @@ -8,6 +8,8 @@ import ( "github.com/hay-kot/homebox/backend/ent" "github.com/hay-kot/homebox/backend/ent/group" "github.com/hay-kot/homebox/backend/ent/item" + "github.com/hay-kot/homebox/backend/ent/label" + "github.com/hay-kot/homebox/backend/ent/location" "github.com/hay-kot/homebox/backend/ent/predicate" ) @@ -16,6 +18,12 @@ type ItemsRepository struct { } type ( + ItemQuery struct { + Search string `json:"search"` + LocationIDs []uuid.UUID `json:"locationIds"` + LabelIDs []uuid.UUID `json:"labelIds"` + } + ItemCreate struct { ImportRef string `json:"-"` Name string `json:"name"` @@ -206,6 +214,40 @@ func (e *ItemsRepository) GetOneByGroup(ctx context.Context, gid, id uuid.UUID) return e.getOne(ctx, item.ID(id), item.HasGroupWith(group.ID(gid))) } +// QueryByGroup returns a list of items that belong to a specific group based on the provided query. +func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q ItemQuery) ([]ItemSummary, error) { + qb := e.db.Item.Query().Where(item.HasGroupWith(group.ID(gid))) + + if len(q.LabelIDs) > 0 { + labels := make([]predicate.Item, 0, len(q.LabelIDs)) + for _, l := range q.LabelIDs { + labels = append(labels, item.HasLabelWith(label.ID(l))) + } + qb = qb.Where(item.Or(labels...)) + } + + if len(q.LocationIDs) > 0 { + locations := make([]predicate.Item, 0, len(q.LocationIDs)) + for _, l := range q.LocationIDs { + locations = append(locations, item.HasLocationWith(location.ID(l))) + } + qb = qb.Where(item.Or(locations...)) + } + + if q.Search != "" { + qb.Where( + item.Or( + item.NameContainsFold(q.Search), + item.DescriptionContainsFold(q.Search), + ), + ) + } + + return mapItemsSummaryErr(qb.WithLabel(). + WithLocation(). + All(ctx)) +} + // GetAll returns all the items in the database with the Labels and Locations eager loaded. func (e *ItemsRepository) GetAll(ctx context.Context, gid uuid.UUID) ([]ItemSummary, error) { return mapItemsSummaryErr(e.db.Item.Query(). diff --git a/backend/internal/services/service_items.go b/backend/internal/services/service_items.go index bc1a994..0a8e2df 100644 --- a/backend/internal/services/service_items.go +++ b/backend/internal/services/service_items.go @@ -28,6 +28,10 @@ func (svc *ItemService) GetOne(ctx context.Context, gid uuid.UUID, id uuid.UUID) return svc.repo.Items.GetOneByGroup(ctx, gid, id) } +func (svc *ItemService) Query(ctx Context, q repo.ItemQuery) ([]repo.ItemSummary, error) { + return svc.repo.Items.QueryByGroup(ctx, ctx.GID, q) +} + func (svc *ItemService) GetAll(ctx context.Context, gid uuid.UUID) ([]repo.ItemSummary, error) { return svc.repo.Items.GetAll(ctx, gid) } diff --git a/frontend/components/App/Header.vue b/frontend/components/App/Header.vue index 9c6b552..1561c87 100644 --- a/frontend/components/App/Header.vue +++ b/frontend/components/App/Header.vue @@ -18,6 +18,10 @@ name: "Home", href: "/home", }, + { + name: "Items", + href: "/items", + }, { name: "Logout", action: logout, diff --git a/frontend/components/Form/Multiselect.vue b/frontend/components/Form/Multiselect.vue index bf0e0cc..52cf2c5 100644 --- a/frontend/components/Form/Multiselect.vue +++ b/frontend/components/Form/Multiselect.vue @@ -11,7 +11,8 @@