From bd06fdafaf6cf5d5de263440c9ff5cb44001518a Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Sun, 5 Feb 2023 12:12:54 -0900 Subject: [PATCH] feat: enhanced search functions (#260) * make login case insensitive * expand query to support by Field and By AID search * type generation * new API callers * rework search to support field queries * improve unnecessary data fetches * clear stores on logout * change verbage * add labels --- backend/app/api/handlers/v1/v1_ctrl_auth.go | 3 +- backend/app/api/handlers/v1/v1_ctrl_items.go | 75 ++++- backend/app/api/routes.go | 5 +- backend/app/api/static/docs/docs.go | 54 ++++ backend/app/api/static/docs/swagger.json | 54 ++++ backend/app/api/static/docs/swagger.yaml | 32 ++ backend/internal/data/repo/asset_id_type.go | 26 +- backend/internal/data/repo/repo_items.go | 90 +++++- backend/internal/data/repo/repo_items_test.go | 46 +++ backend/internal/data/repo/repo_users.go | 2 +- frontend/components/App/ImportDialog.vue | 2 +- frontend/composables/use-api.ts | 2 +- frontend/composables/use-events.ts | 4 +- frontend/layouts/default.vue | 20 +- frontend/lib/api/classes/items.ts | 13 + frontend/lib/api/types/data-contracts.ts | 44 +-- frontend/lib/requests/requests.ts | 9 +- frontend/pages/items.vue | 289 ++++++++++++------ 18 files changed, 637 insertions(+), 133 deletions(-) diff --git a/backend/app/api/handlers/v1/v1_ctrl_auth.go b/backend/app/api/handlers/v1/v1_ctrl_auth.go index 24b1654..95a5cb2 100644 --- a/backend/app/api/handlers/v1/v1_ctrl_auth.go +++ b/backend/app/api/handlers/v1/v1_ctrl_auth.go @@ -3,6 +3,7 @@ package v1 import ( "errors" "net/http" + "strings" "time" "github.com/hay-kot/homebox/backend/internal/core/services" @@ -70,7 +71,7 @@ func (ctrl *V1Controller) HandleAuthLogin() server.HandlerFunc { ) } - newToken, err := ctrl.svc.User.Login(r.Context(), loginForm.Username, loginForm.Password) + newToken, err := ctrl.svc.User.Login(r.Context(), strings.ToLower(loginForm.Username), loginForm.Password) if err != nil { return validate.NewRequestError(errors.New("authentication failed"), http.StatusInternalServerError) diff --git a/backend/app/api/handlers/v1/v1_ctrl_items.go b/backend/app/api/handlers/v1/v1_ctrl_items.go index d7be19d..ec8f580 100644 --- a/backend/app/api/handlers/v1/v1_ctrl_items.go +++ b/backend/app/api/handlers/v1/v1_ctrl_items.go @@ -4,6 +4,7 @@ import ( "database/sql" "errors" "net/http" + "strings" "github.com/hay-kot/homebox/backend/internal/core/services" "github.com/hay-kot/homebox/backend/internal/data/repo" @@ -29,18 +30,48 @@ func (ctrl *V1Controller) HandleItemsGetAll() server.HandlerFunc { extractQuery := func(r *http.Request) repo.ItemQuery { params := r.URL.Query() - return repo.ItemQuery{ + filterFieldItems := func(raw []string) []repo.FieldQuery { + var items []repo.FieldQuery + + for _, v := range raw { + parts := strings.SplitN(v, "=", 2) + if len(parts) == 2 { + items = append(items, repo.FieldQuery{ + Name: parts[0], + Value: parts[1], + }) + } + } + + return items + } + + v := repo.ItemQuery{ Page: queryIntOrNegativeOne(params.Get("page")), PageSize: queryIntOrNegativeOne(params.Get("pageSize")), Search: params.Get("q"), LocationIDs: queryUUIDList(params, "locations"), LabelIDs: queryUUIDList(params, "labels"), IncludeArchived: queryBool(params.Get("includeArchived")), + Fields: filterFieldItems(params["fields"]), } + + if strings.HasPrefix(v.Search, "#") { + aidStr := strings.TrimPrefix(v.Search, "#") + + aid, ok := repo.ParseAssetID(aidStr) + if ok { + v.Search = "" + v.AssetID = aid + } + } + + return v } return func(w http.ResponseWriter, r *http.Request) error { ctx := services.NewContext(r.Context()) + items, err := ctrl.repo.Items.QueryByGroup(ctx, ctx.GID, extractQuery(r)) if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -161,6 +192,48 @@ func (ctrl *V1Controller) handleItemsGeneral() server.HandlerFunc { } } +// HandleGetAllCustomFieldNames godocs +// @Summary imports items into the database +// @Tags Items +// @Produce json +// @Success 200 +// @Router /v1/items/fields [GET] +// @Success 200 {object} []string +// @Security Bearer +func (ctrl *V1Controller) HandleGetAllCustomFieldNames() server.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) error { + ctx := services.NewContext(r.Context()) + + v, err := ctrl.repo.Items.GetAllCustomFieldNames(r.Context(), ctx.GID) + if err != nil { + return err + } + + return server.Respond(w, http.StatusOK, v) + } +} + +// HandleGetAllCustomFieldValues godocs +// @Summary imports items into the database +// @Tags Items +// @Produce json +// @Success 200 +// @Router /v1/items/fields/values [GET] +// @Success 200 {object} []string +// @Security Bearer +func (ctrl *V1Controller) HandleGetAllCustomFieldValues() server.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) error { + ctx := services.NewContext(r.Context()) + + v, err := ctrl.repo.Items.GetAllCustomFieldValues(r.Context(), ctx.GID, r.URL.Query().Get("field")) + if err != nil { + return err + } + + return server.Respond(w, http.StatusOK, v) + } +} + // HandleItemsImport godocs // @Summary imports items into the database // @Tags Items diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go index 3475e7a..04e4066 100644 --- a/backend/app/api/routes.go +++ b/backend/app/api/routes.go @@ -103,8 +103,11 @@ func (a *app) mountRoutes(repos *repo.AllRepos) { a.server.Delete(v1Base("/labels/{id}"), v1Ctrl.HandleLabelDelete(), userMW...) a.server.Get(v1Base("/items"), v1Ctrl.HandleItemsGetAll(), userMW...) - a.server.Post(v1Base("/items/import"), v1Ctrl.HandleItemsImport(), userMW...) a.server.Post(v1Base("/items"), v1Ctrl.HandleItemsCreate(), userMW...) + a.server.Post(v1Base("/items/import"), v1Ctrl.HandleItemsImport(), userMW...) + a.server.Get(v1Base("/items/fields"), v1Ctrl.HandleGetAllCustomFieldNames(), userMW...) + a.server.Get(v1Base("/items/fields/values"), v1Ctrl.HandleGetAllCustomFieldValues(), userMW...) + a.server.Get(v1Base("/items/{id}"), v1Ctrl.HandleItemGet(), userMW...) a.server.Put(v1Base("/items/{id}"), v1Ctrl.HandleItemUpdate(), userMW...) a.server.Delete(v1Base("/items/{id}"), v1Ctrl.HandleItemDelete(), userMW...) diff --git a/backend/app/api/static/docs/docs.go b/backend/app/api/static/docs/docs.go index e8c89a1..0fcf591 100644 --- a/backend/app/api/static/docs/docs.go +++ b/backend/app/api/static/docs/docs.go @@ -383,6 +383,60 @@ const docTemplate = `{ } } }, + "/v1/items/fields": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Items" + ], + "summary": "imports items into the database", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "/v1/items/fields/values": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Items" + ], + "summary": "imports items into the database", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, "/v1/items/import": { "post": { "security": [ diff --git a/backend/app/api/static/docs/swagger.json b/backend/app/api/static/docs/swagger.json index 499f83f..74bfeaa 100644 --- a/backend/app/api/static/docs/swagger.json +++ b/backend/app/api/static/docs/swagger.json @@ -375,6 +375,60 @@ } } }, + "/v1/items/fields": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Items" + ], + "summary": "imports items into the database", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "/v1/items/fields/values": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Items" + ], + "summary": "imports items into the database", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, "/v1/items/import": { "post": { "security": [ diff --git a/backend/app/api/static/docs/swagger.yaml b/backend/app/api/static/docs/swagger.yaml index 1781f32..fc3b9c1 100644 --- a/backend/app/api/static/docs/swagger.yaml +++ b/backend/app/api/static/docs/swagger.yaml @@ -1094,6 +1094,38 @@ paths: summary: Update Maintenance Entry tags: - Maintenance + /v1/items/fields: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + security: + - Bearer: [] + summary: imports items into the database + tags: + - Items + /v1/items/fields/values: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + security: + - Bearer: [] + summary: imports items into the database + tags: + - Items /v1/items/import: post: parameters: diff --git a/backend/internal/data/repo/asset_id_type.go b/backend/internal/data/repo/asset_id_type.go index 6aecf9c..06d610e 100644 --- a/backend/internal/data/repo/asset_id_type.go +++ b/backend/internal/data/repo/asset_id_type.go @@ -8,11 +8,34 @@ import ( type AssetID int +func (aid AssetID) Nil() bool { + return aid.Int() <= 0 +} + +func (aid AssetID) Int() int { + return int(aid) +} + +func ParseAssetIDBytes(d []byte) (AID AssetID, ok bool) { + d = bytes.Replace(d, []byte(`"`), []byte(``), -1) + d = bytes.Replace(d, []byte(`-`), []byte(``), -1) + + aidInt, err := strconv.Atoi(string(d)) + if err != nil { + return AssetID(-1), false + } + + return AssetID(aidInt), true +} + +func ParseAssetID(s string) (AID AssetID, ok bool) { + return ParseAssetIDBytes([]byte(s)) +} + func (aid AssetID) MarshalJSON() ([]byte, error) { aidStr := fmt.Sprintf("%06d", aid) aidStr = fmt.Sprintf("%s-%s", aidStr[:3], aidStr[3:]) return []byte(fmt.Sprintf(`"%s"`, aidStr)), nil - } func (aid *AssetID) UnmarshalJSON(d []byte) error { @@ -26,5 +49,4 @@ func (aid *AssetID) UnmarshalJSON(d []byte) error { *aid = AssetID(aidInt) return nil - } diff --git a/backend/internal/data/repo/repo_items.go b/backend/internal/data/repo/repo_items.go index d6faccd..01f090f 100644 --- a/backend/internal/data/repo/repo_items.go +++ b/backend/internal/data/repo/repo_items.go @@ -2,6 +2,7 @@ package repo import ( "context" + "fmt" "time" "github.com/google/uuid" @@ -19,14 +20,21 @@ type ItemsRepository struct { } type ( + FieldQuery struct { + Name string + Value string + } + ItemQuery struct { Page int PageSize int Search string `json:"search"` + AssetID AssetID `json:"assetId"` LocationIDs []uuid.UUID `json:"locationIds"` LabelIDs []uuid.UUID `json:"labelIds"` SortBy string `json:"sortBy"` IncludeArchived bool `json:"includeArchived"` + Fields []FieldQuery } ItemField struct { @@ -326,7 +334,26 @@ func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q Ite ) } + if !q.AssetID.Nil() { + qb = qb.Where(item.AssetID(q.AssetID.Int())) + } + + if len(q.Fields) > 0 { + predicates := make([]predicate.Item, 0, len(q.Fields)) + for _, f := range q.Fields { + predicates = append(predicates, item.HasFieldsWith( + itemfield.And( + itemfield.Name(f.Name), + itemfield.TextValue(f.Value), + ), + )) + } + + qb = qb.Where(item.Or(predicates...)) + } + count, err := qb.Count(ctx) + if err != nil { return PaginationResult[ItemSummary]{}, err } @@ -473,8 +500,8 @@ func (e *ItemsRepository) DeleteByGroup(ctx context.Context, gid, id uuid.UUID) return err } -func (e *ItemsRepository) UpdateByGroup(ctx context.Context, gid uuid.UUID, data ItemUpdate) (ItemOut, error) { - q := e.db.Item.Update().Where(item.ID(data.ID), item.HasGroupWith(group.ID(gid))). +func (e *ItemsRepository) UpdateByGroup(ctx context.Context, GID uuid.UUID, data ItemUpdate) (ItemOut, error) { + q := e.db.Item.Update().Where(item.ID(data.ID), item.HasGroupWith(group.ID(GID))). SetName(data.Name). SetDescription(data.Description). SetLocationID(data.LocationID). @@ -587,3 +614,62 @@ func (e *ItemsRepository) UpdateByGroup(ctx context.Context, gid uuid.UUID, data return e.GetOne(ctx, data.ID) } + +func (e *ItemsRepository) GetAllCustomFieldValues(ctx context.Context, GID uuid.UUID, name string) ([]string, error) { + type st struct { + Value string `json:"text_value"` + } + + var values []st + + err := e.db.Item.Query(). + Where( + item.HasGroupWith(group.ID(GID)), + ). + QueryFields(). + Where( + itemfield.Name(name), + ). + Unique(true). + Select(itemfield.FieldTextValue). + Scan(ctx, &values) + + if err != nil { + return nil, fmt.Errorf("failed to get field values: %w", err) + } + + valueStrings := make([]string, len(values)) + for i, f := range values { + valueStrings[i] = f.Value + } + + return valueStrings, nil +} + +func (e *ItemsRepository) GetAllCustomFieldNames(ctx context.Context, GID uuid.UUID) ([]string, error) { + type st struct { + Name string `json:"name"` + } + + var fields []st + + err := e.db.Debug().Item.Query(). + Where( + item.HasGroupWith(group.ID(GID)), + ). + QueryFields(). + Unique(true). + Select(itemfield.FieldName). + Scan(ctx, &fields) + + if err != nil { + return nil, fmt.Errorf("failed to get custom fields: %w", err) + } + + fieldNames := make([]string, len(fields)) + for i, f := range fields { + fieldNames[i] = f.Name + } + + return fieldNames, nil +} diff --git a/backend/internal/data/repo/repo_items_test.go b/backend/internal/data/repo/repo_items_test.go index 6d361ba..1f5ea0e 100644 --- a/backend/internal/data/repo/repo_items_test.go +++ b/backend/internal/data/repo/repo_items_test.go @@ -7,6 +7,7 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func itemFactory() ItemCreate { @@ -273,3 +274,48 @@ func TestItemsRepository_Update(t *testing.T) { assert.Equal(t, updateData.WarrantyDetails, got.WarrantyDetails) assert.Equal(t, updateData.LifetimeWarranty, got.LifetimeWarranty) } + +func TestItemRepository_GetAllCustomFields(t *testing.T) { + const FIELDS_COUNT = 5 + + entity := useItems(t, 1)[0] + + fields := make([]ItemField, FIELDS_COUNT) + names := make([]string, FIELDS_COUNT) + values := make([]string, FIELDS_COUNT) + + for i := 0; i < FIELDS_COUNT; i++ { + name := fk.Str(10) + fields[i] = ItemField{ + Name: name, + Type: "text", + TextValue: fk.Str(10), + } + names[i] = name + values[i] = fields[i].TextValue + } + + _, err := tRepos.Items.UpdateByGroup(context.Background(), tGroup.ID, ItemUpdate{ + ID: entity.ID, + Name: entity.Name, + LocationID: entity.Location.ID, + Fields: fields, + }) + + require.NoError(t, err) + + // Test getting all fields + { + results, err := tRepos.Items.GetAllCustomFieldNames(context.Background(), tGroup.ID) + assert.NoError(t, err) + assert.ElementsMatch(t, names, results) + } + + // Test getting all values from field + { + results, err := tRepos.Items.GetAllCustomFieldValues(context.Background(), tUser.GroupID, names[0]) + + assert.NoError(t, err) + assert.ElementsMatch(t, values[:1], results) + } +} diff --git a/backend/internal/data/repo/repo_users.go b/backend/internal/data/repo/repo_users.go index 0eaa127..28407f1 100644 --- a/backend/internal/data/repo/repo_users.go +++ b/backend/internal/data/repo/repo_users.go @@ -69,7 +69,7 @@ func (e *UserRepository) GetOneId(ctx context.Context, id uuid.UUID) (UserOut, e func (e *UserRepository) GetOneEmail(ctx context.Context, email string) (UserOut, error) { return mapUserOutErr(e.db.User.Query(). - Where(user.Email(email)). + Where(user.EmailEqualFold(email)). WithGroup(). Only(ctx), ) diff --git a/frontend/components/App/ImportDialog.vue b/frontend/components/App/ImportDialog.vue index 6fe8997..fec49f2 100644 --- a/frontend/components/App/ImportDialog.vue +++ b/frontend/components/App/ImportDialog.vue @@ -85,6 +85,6 @@ importRef.value.value = ""; } - eventBus.emit(EventTypes.ClearStores); + eventBus.emit(EventTypes.InvalidStores); } diff --git a/frontend/composables/use-api.ts b/frontend/composables/use-api.ts index cd06588..04c3bdc 100644 --- a/frontend/composables/use-api.ts +++ b/frontend/composables/use-api.ts @@ -4,7 +4,7 @@ import { Requests } from "~~/lib/requests"; import { useAuthStore } from "~~/stores/auth"; export type Observer = { - handler: (r: Response) => void; + handler: (r: Response, req?: RequestInit) => void; }; export type RemoveObserver = () => void; diff --git a/frontend/composables/use-events.ts b/frontend/composables/use-events.ts index 3c1af07..92c8cdd 100644 --- a/frontend/composables/use-events.ts +++ b/frontend/composables/use-events.ts @@ -2,7 +2,7 @@ export enum EventTypes { // ClearStores event is used to inform the stores that _all_ the data they are using // is now out of date and they should refresh - This is used when the user makes large // changes to the data such as bulk actions or importing a CSV file - ClearStores, + InvalidStores, } export type EventFn = () => void; @@ -15,7 +15,7 @@ export interface IEventBus { class EventBus implements IEventBus { private listeners: Record> = { - [EventTypes.ClearStores]: {}, + [EventTypes.InvalidStores]: {}, }; on(event: EventTypes, fn: EventFn, key: string): void { diff --git a/frontend/layouts/default.vue b/frontend/layouts/default.vue index 5006336..6a17bf2 100644 --- a/frontend/layouts/default.vue +++ b/frontend/layouts/default.vue @@ -181,11 +181,18 @@ }, ]; + function isMutation(method: string | undefined) { + return method === "POST" || method === "PUT" || method === "DELETE"; + } + function isSuccess(status: number) { + return status >= 200 && status < 300; + } + const labelStore = useLabelStore(); const reLabel = /\/api\/v1\/labels\/.*/gm; const rmLabelStoreObserver = defineObserver("labelStore", { - handler: r => { - if (r.status === 201 || r.url.match(reLabel)) { + handler: (resp, req) => { + if (isMutation(req?.method) && isSuccess(resp.status) && resp.url.match(reLabel)) { labelStore.refresh(); } console.debug("labelStore handler called by observer"); @@ -195,18 +202,19 @@ const locationStore = useLocationStore(); const reLocation = /\/api\/v1\/locations\/.*/gm; const rmLocationStoreObserver = defineObserver("locationStore", { - handler: r => { - if (r.status === 201 || r.url.match(reLocation)) { + handler: (resp, req) => { + if (isMutation(req?.method) && isSuccess(resp.status) && resp.url.match(reLocation)) { locationStore.refreshChildren(); locationStore.refreshParents(); } + console.debug("locationStore handler called by observer"); }, }); const eventBus = useEventBus(); eventBus.on( - EventTypes.ClearStores, + EventTypes.InvalidStores, () => { labelStore.refresh(); locationStore.refreshChildren(); @@ -218,7 +226,7 @@ onUnmounted(() => { rmLabelStoreObserver(); rmLocationStoreObserver(); - eventBus.off(EventTypes.ClearStores, "stores"); + eventBus.off(EventTypes.InvalidStores, "stores"); }); const authStore = useAuthStore(); diff --git a/frontend/lib/api/classes/items.ts b/frontend/lib/api/classes/items.ts index 8522852..0a45e62 100644 --- a/frontend/lib/api/classes/items.ts +++ b/frontend/lib/api/classes/items.ts @@ -21,6 +21,7 @@ export type ItemsQuery = { locations?: string[]; labels?: string[]; q?: string; + fields?: string[]; }; export class AttachmentsAPI extends BaseAPI { @@ -48,6 +49,16 @@ export class AttachmentsAPI extends BaseAPI { } } +export class FieldsAPI extends BaseAPI { + getAll() { + return this.http.get({ url: route("/items/fields") }); + } + + getAllValues(field: string) { + return this.http.get({ url: route(`/items/fields/values`, { field }) }); + } +} + export class MaintenanceAPI extends BaseAPI { getLog(itemId: string) { return this.http.get({ url: route(`/items/${itemId}/maintenance`) }); @@ -75,9 +86,11 @@ export class MaintenanceAPI extends BaseAPI { export class ItemsApi extends BaseAPI { attachments: AttachmentsAPI; maintenance: MaintenanceAPI; + fields: FieldsAPI; constructor(http: Requests, token: string) { super(http, token); + this.fields = new FieldsAPI(http); this.attachments = new AttachmentsAPI(http); this.maintenance = new MaintenanceAPI(http); } diff --git a/frontend/lib/api/types/data-contracts.ts b/frontend/lib/api/types/data-contracts.ts index b4f688c..e461e89 100644 --- a/frontend/lib/api/types/data-contracts.ts +++ b/frontend/lib/api/types/data-contracts.ts @@ -17,11 +17,11 @@ export interface DocumentOut { } export interface Group { - createdAt: Date; + createdAt: string; currency: string; id: string; name: string; - updatedAt: Date; + updatedAt: string; } export interface GroupStatistics { @@ -39,11 +39,11 @@ export interface GroupUpdate { } export interface ItemAttachment { - createdAt: Date; + createdAt: string; document: DocumentOut; id: string; type: string; - updatedAt: Date; + updatedAt: string; } export interface ItemAttachmentUpdate { @@ -76,7 +76,7 @@ export interface ItemOut { assetId: string; attachments: ItemAttachment[]; children: ItemSummary[]; - createdAt: Date; + createdAt: string; description: string; fields: ItemField[]; id: string; @@ -103,16 +103,16 @@ export interface ItemOut { /** @example "0" */ soldPrice: string; /** Sold */ - soldTime: Date; + soldTime: string; soldTo: string; - updatedAt: Date; + updatedAt: string; warrantyDetails: string; - warrantyExpires: Date; + warrantyExpires: string; } export interface ItemSummary { archived: boolean; - createdAt: Date; + createdAt: string; description: string; id: string; insured: boolean; @@ -123,7 +123,7 @@ export interface ItemSummary { /** @example "0" */ purchasePrice: string; quantity: number; - updatedAt: Date; + updatedAt: string; } export interface ItemUpdate { @@ -156,10 +156,10 @@ export interface ItemUpdate { /** @example "0" */ soldPrice: string; /** Sold */ - soldTime: Date; + soldTime: string; soldTo: string; warrantyDetails: string; - warrantyExpires: Date; + warrantyExpires: string; } export interface LabelCreate { @@ -169,20 +169,20 @@ export interface LabelCreate { } export interface LabelOut { - createdAt: Date; + createdAt: string; description: string; id: string; items: ItemSummary[]; name: string; - updatedAt: Date; + updatedAt: string; } export interface LabelSummary { - createdAt: Date; + createdAt: string; description: string; id: string; name: string; - updatedAt: Date; + updatedAt: string; } export interface LocationCreate { @@ -193,30 +193,30 @@ export interface LocationCreate { export interface LocationOut { children: LocationSummary[]; - createdAt: Date; + createdAt: string; description: string; id: string; items: ItemSummary[]; name: string; parent: LocationSummary; - updatedAt: Date; + updatedAt: string; } export interface LocationOutCount { - createdAt: Date; + createdAt: string; description: string; id: string; itemCount: number; name: string; - updatedAt: Date; + updatedAt: string; } export interface LocationSummary { - createdAt: Date; + createdAt: string; description: string; id: string; name: string; - updatedAt: Date; + updatedAt: string; } export interface LocationUpdate { diff --git a/frontend/lib/requests/requests.ts b/frontend/lib/requests/requests.ts index 9d62c36..f8ed355 100644 --- a/frontend/lib/requests/requests.ts +++ b/frontend/lib/requests/requests.ts @@ -5,8 +5,7 @@ export enum Method { DELETE = "DELETE", } -export type RequestInterceptor = (r: Response) => void; -export type ResponseInterceptor = (r: Response) => void; +export type ResponseInterceptor = (r: Response, rq?: RequestInit) => void; export interface TResponse { status: number; @@ -32,8 +31,8 @@ export class Requests { this.responseInterceptors.push(interceptor); } - private callResponseInterceptors(response: Response) { - this.responseInterceptors.forEach(i => i(response)); + private callResponseInterceptors(response: Response, request?: RequestInit) { + this.responseInterceptors.forEach(i => i(response, request)); } private url(rest: string): string { @@ -90,7 +89,7 @@ export class Requests { } const response = await fetch(this.url(rargs.url), payload); - this.callResponseInterceptors(response); + this.callResponseInterceptors(response, payload); const data: T = await (async () => { if (response.status === 204) { diff --git a/frontend/pages/items.vue b/frontend/pages/items.vue index b326363..0cad8d4 100644 --- a/frontend/pages/items.vue +++ b/frontend/pages/items.vue @@ -1,5 +1,4 @@