From 26ecb5a9d4fccf1e4052830c66241856623afdeb Mon Sep 17 00:00:00 2001
From: Hayden <64056131+hay-kot@users.noreply.github.com>
Date: Thu, 1 Sep 2022 14:32:03 -0800
Subject: [PATCH] move to nuxt
---
backend/app/api/docs/docs.go | 144 +
backend/app/api/docs/swagger.json | 144 +
backend/app/api/docs/swagger.yaml | 92 +-
backend/app/api/routes.go | 3 +
backend/app/api/v1/v1_ctrl_locations.go | 107 +-
backend/internal/repo/repo_locations.go | 14 +-
backend/internal/services/mappers/helpers.go | 9 +
backend/internal/services/mappers/items.go | 17 +
.../internal/services/mappers/locations.go | 32 +
.../internal/services/service_locations.go | 69 +-
backend/internal/types/location_types.go | 16 +-
frontend/.gitignore | 14 +-
frontend/LICENSE | 21 -
frontend/README.md | 137 +-
frontend/app.vue | 6 +
frontend/auto-imports.d.ts | 252 -
frontend/components.d.ts | 19 -
frontend/components/App/Header.vue | 139 +
frontend/components/App/Toast.vue | 58 +
frontend/components/Base/ActionsDivider.vue | 21 +
frontend/components/Base/Button.vue | 24 +
frontend/components/Base/Container.vue | 14 +
frontend/components/Base/Details.vue | 37 +
frontend/components/Base/Modal.vue | 45 +
frontend/components/Base/SectionHeader.vue | 10 +
frontend/components/Form/TextField.vue | 42 +
frontend/components/Icon.vue | 31 +
frontend/components/ModalConfirm.vue | 15 +
frontend/composables/use-api.ts | 23 +
frontend/composables/use-confirm.ts | 40 +
frontend/{src => }/composables/use-ids.ts | 0
.../{src => }/composables/use-notifier.ts | 0
frontend/composables/use-preferences.ts | 23 +
frontend/index.html | 20 -
frontend/layouts/404.vue | 5 +
frontend/layouts/default.vue | 10 +
frontend/layouts/empty.vue | 7 +
frontend/layouts/home.vue | 9 +
frontend/{src => lib}/api/base/base-api.ts | 2 +-
frontend/{src => lib}/api/base/base-types.ts | 0
frontend/{src => lib}/api/base/index.test.ts | 0
frontend/{src => lib}/api/base/index.ts | 0
frontend/{src => lib}/api/base/urls.ts | 0
.../{src => lib}/api/classes/locations.ts | 13 +
frontend/lib/api/public.ts | 39 +
frontend/{src => lib}/api/user.ts | 10 +-
frontend/{src => }/lib/requests/index.ts | 0
frontend/{src => }/lib/requests/requests.ts | 5 +-
frontend/locales/en.json | 22 -
frontend/locales/id.json | 22 -
frontend/nuxt.config.ts | 14 +
frontend/package.json | 98 +-
frontend/pages/[...all].vue | 15 +
frontend/pages/home.vue | 39 +
frontend/pages/index.vue | 190 +
frontend/pages/location/[id].vue | 136 +
frontend/pnpm-lock.yaml | 6648 +++++++++--------
frontend/public/apple-touch-icon.png | Bin 7911 -> 0 bytes
frontend/public/favicon-16x16.png | Bin 647 -> 0 bytes
frontend/public/favicon-32x32.png | Bin 1417 -> 0 bytes
frontend/public/favicon.ico | Bin 15406 -> 0 bytes
frontend/public/pwa-192x192.png | Bin 8867 -> 0 bytes
frontend/public/pwa-512x512.png | Bin 22375 -> 0 bytes
frontend/public/robots.txt | 2 -
frontend/public/site.webmanifest | 1 -
frontend/src/App.vue | 8 -
frontend/src/__test__/basic.spec.ts | 7 -
frontend/src/api/public.ts | 39 -
frontend/src/assets/logo.png | Bin 36025 -> 0 bytes
frontend/src/components/App/Toast.vue | 71 -
frontend/src/components/AppHeader.vue | 80 -
frontend/src/components/Form/TextField.vue | 31 -
frontend/src/composables/use-api.ts | 23 -
frontend/src/env.d.ts | 8 -
frontend/src/layouts/404.vue | 5 -
frontend/src/layouts/default.vue | 15 -
frontend/src/main.ts | 19 -
frontend/src/modules/i18n.ts | 29 -
frontend/src/modules/pinia.ts | 14 -
frontend/src/modules/pwa.ts | 10 -
frontend/src/pages/[...all].vue | 19 -
frontend/src/pages/home.vue | 137 -
frontend/src/pages/index.vue | 188 -
frontend/src/router.ts | 17 -
frontend/src/store/auth.ts | 36 -
frontend/src/store/index.ts | 7 -
frontend/src/styles/index.css | 3 -
frontend/src/types/ViteSetupModule.ts | 3 -
frontend/stores/auth.ts | 37 +
frontend/tailwind.config.js | 28 +-
frontend/tsconfig.json | 34 +-
frontend/typed-router.d.ts | 96 -
frontend/vite.config.ts | 133 -
93 files changed, 5273 insertions(+), 4749 deletions(-)
create mode 100644 backend/internal/services/mappers/helpers.go
create mode 100644 backend/internal/services/mappers/items.go
create mode 100644 backend/internal/services/mappers/locations.go
delete mode 100644 frontend/LICENSE
create mode 100644 frontend/app.vue
delete mode 100644 frontend/auto-imports.d.ts
delete mode 100644 frontend/components.d.ts
create mode 100644 frontend/components/App/Header.vue
create mode 100644 frontend/components/App/Toast.vue
create mode 100644 frontend/components/Base/ActionsDivider.vue
create mode 100644 frontend/components/Base/Button.vue
create mode 100644 frontend/components/Base/Container.vue
create mode 100644 frontend/components/Base/Details.vue
create mode 100644 frontend/components/Base/Modal.vue
create mode 100644 frontend/components/Base/SectionHeader.vue
create mode 100644 frontend/components/Form/TextField.vue
create mode 100644 frontend/components/Icon.vue
create mode 100644 frontend/components/ModalConfirm.vue
create mode 100644 frontend/composables/use-api.ts
create mode 100644 frontend/composables/use-confirm.ts
rename frontend/{src => }/composables/use-ids.ts (100%)
rename frontend/{src => }/composables/use-notifier.ts (100%)
create mode 100644 frontend/composables/use-preferences.ts
delete mode 100644 frontend/index.html
create mode 100644 frontend/layouts/404.vue
create mode 100644 frontend/layouts/default.vue
create mode 100644 frontend/layouts/empty.vue
create mode 100644 frontend/layouts/home.vue
rename frontend/{src => lib}/api/base/base-api.ts (84%)
rename frontend/{src => lib}/api/base/base-types.ts (100%)
rename frontend/{src => lib}/api/base/index.test.ts (100%)
rename frontend/{src => lib}/api/base/index.ts (100%)
rename frontend/{src => lib}/api/base/urls.ts (100%)
rename frontend/{src => lib}/api/classes/locations.ts (58%)
create mode 100644 frontend/lib/api/public.ts
rename frontend/{src => lib}/api/user.ts (58%)
rename frontend/{src => }/lib/requests/index.ts (100%)
rename frontend/{src => }/lib/requests/requests.ts (96%)
delete mode 100644 frontend/locales/en.json
delete mode 100644 frontend/locales/id.json
create mode 100644 frontend/nuxt.config.ts
create mode 100644 frontend/pages/[...all].vue
create mode 100644 frontend/pages/home.vue
create mode 100644 frontend/pages/index.vue
create mode 100644 frontend/pages/location/[id].vue
delete mode 100644 frontend/public/apple-touch-icon.png
delete mode 100644 frontend/public/favicon-16x16.png
delete mode 100644 frontend/public/favicon-32x32.png
delete mode 100644 frontend/public/favicon.ico
delete mode 100644 frontend/public/pwa-192x192.png
delete mode 100644 frontend/public/pwa-512x512.png
delete mode 100644 frontend/public/robots.txt
delete mode 100644 frontend/public/site.webmanifest
delete mode 100644 frontend/src/App.vue
delete mode 100644 frontend/src/__test__/basic.spec.ts
delete mode 100644 frontend/src/api/public.ts
delete mode 100644 frontend/src/assets/logo.png
delete mode 100644 frontend/src/components/App/Toast.vue
delete mode 100644 frontend/src/components/AppHeader.vue
delete mode 100644 frontend/src/components/Form/TextField.vue
delete mode 100644 frontend/src/composables/use-api.ts
delete mode 100644 frontend/src/env.d.ts
delete mode 100644 frontend/src/layouts/404.vue
delete mode 100644 frontend/src/layouts/default.vue
delete mode 100644 frontend/src/main.ts
delete mode 100644 frontend/src/modules/i18n.ts
delete mode 100644 frontend/src/modules/pinia.ts
delete mode 100644 frontend/src/modules/pwa.ts
delete mode 100644 frontend/src/pages/[...all].vue
delete mode 100644 frontend/src/pages/home.vue
delete mode 100644 frontend/src/pages/index.vue
delete mode 100644 frontend/src/router.ts
delete mode 100644 frontend/src/store/auth.ts
delete mode 100644 frontend/src/store/index.ts
delete mode 100644 frontend/src/styles/index.css
delete mode 100644 frontend/src/types/ViteSetupModule.ts
create mode 100644 frontend/stores/auth.ts
delete mode 100644 frontend/typed-router.d.ts
delete mode 100644 frontend/vite.config.ts
diff --git a/backend/app/api/docs/docs.go b/backend/app/api/docs/docs.go
index e62b065..211e15f 100644
--- a/backend/app/api/docs/docs.go
+++ b/backend/app/api/docs/docs.go
@@ -323,6 +323,39 @@ const docTemplate = `{
}
}
],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/types.LocationSummary"
+ }
+ }
+ }
+ }
+ },
+ "/v1/locations/{id}": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Locations"
+ ],
+ "summary": "Gets a location and fields",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Location ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
"responses": {
"200": {
"description": "OK",
@@ -331,6 +364,65 @@ const docTemplate = `{
}
}
}
+ },
+ "put": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Locations"
+ ],
+ "summary": "updates a location",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Location ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/types.LocationOut"
+ }
+ }
+ }
+ },
+ "delete": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Locations"
+ ],
+ "summary": "deletes a location",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Location ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": ""
+ }
+ }
}
},
"/v1/users/login": {
@@ -993,6 +1085,29 @@ const docTemplate = `{
}
}
},
+ "types.ItemSummary": {
+ "type": "object",
+ "properties": {
+ "createdAt": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "locationId": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "updatedAt": {
+ "type": "string"
+ }
+ }
+ },
"types.LocationCreate": {
"type": "object",
"properties": {
@@ -1005,6 +1120,35 @@ const docTemplate = `{
}
},
"types.LocationOut": {
+ "type": "object",
+ "properties": {
+ "createdAt": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "groupId": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/types.ItemSummary"
+ }
+ },
+ "name": {
+ "type": "string"
+ },
+ "updatedAt": {
+ "type": "string"
+ }
+ }
+ },
+ "types.LocationSummary": {
"type": "object",
"properties": {
"createdAt": {
diff --git a/backend/app/api/docs/swagger.json b/backend/app/api/docs/swagger.json
index 4d5a114..b688cb7 100644
--- a/backend/app/api/docs/swagger.json
+++ b/backend/app/api/docs/swagger.json
@@ -315,6 +315,39 @@
}
}
],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/types.LocationSummary"
+ }
+ }
+ }
+ }
+ },
+ "/v1/locations/{id}": {
+ "get": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Locations"
+ ],
+ "summary": "Gets a location and fields",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Location ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
"responses": {
"200": {
"description": "OK",
@@ -323,6 +356,65 @@
}
}
}
+ },
+ "put": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Locations"
+ ],
+ "summary": "updates a location",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Location ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "schema": {
+ "$ref": "#/definitions/types.LocationOut"
+ }
+ }
+ }
+ },
+ "delete": {
+ "security": [
+ {
+ "Bearer": []
+ }
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "tags": [
+ "Locations"
+ ],
+ "summary": "deletes a location",
+ "parameters": [
+ {
+ "type": "string",
+ "description": "Location ID",
+ "name": "id",
+ "in": "path",
+ "required": true
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": ""
+ }
+ }
}
},
"/v1/users/login": {
@@ -985,6 +1077,29 @@
}
}
},
+ "types.ItemSummary": {
+ "type": "object",
+ "properties": {
+ "createdAt": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "locationId": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "updatedAt": {
+ "type": "string"
+ }
+ }
+ },
"types.LocationCreate": {
"type": "object",
"properties": {
@@ -997,6 +1112,35 @@
}
},
"types.LocationOut": {
+ "type": "object",
+ "properties": {
+ "createdAt": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "groupId": {
+ "type": "string"
+ },
+ "id": {
+ "type": "string"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/types.ItemSummary"
+ }
+ },
+ "name": {
+ "type": "string"
+ },
+ "updatedAt": {
+ "type": "string"
+ }
+ }
+ },
+ "types.LocationSummary": {
"type": "object",
"properties": {
"createdAt": {
diff --git a/backend/app/api/docs/swagger.yaml b/backend/app/api/docs/swagger.yaml
index fa6d3e2..62cf296 100644
--- a/backend/app/api/docs/swagger.yaml
+++ b/backend/app/api/docs/swagger.yaml
@@ -338,6 +338,21 @@ definitions:
type: string
type: array
type: object
+ types.ItemSummary:
+ properties:
+ createdAt:
+ type: string
+ description:
+ type: string
+ id:
+ type: string
+ locationId:
+ type: string
+ name:
+ type: string
+ updatedAt:
+ type: string
+ type: object
types.LocationCreate:
properties:
description:
@@ -346,6 +361,25 @@ definitions:
type: string
type: object
types.LocationOut:
+ properties:
+ createdAt:
+ type: string
+ description:
+ type: string
+ groupId:
+ type: string
+ id:
+ type: string
+ items:
+ items:
+ $ref: '#/definitions/types.ItemSummary'
+ type: array
+ name:
+ type: string
+ updatedAt:
+ type: string
+ type: object
+ types.LocationSummary:
properties:
createdAt:
type: string
@@ -584,12 +618,68 @@ paths:
"200":
description: OK
schema:
- $ref: '#/definitions/types.LocationOut'
+ $ref: '#/definitions/types.LocationSummary'
security:
- Bearer: []
summary: Create a new location
tags:
- Locations
+ /v1/locations/{id}:
+ delete:
+ parameters:
+ - description: Location ID
+ in: path
+ name: id
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "204":
+ description: ""
+ security:
+ - Bearer: []
+ summary: deletes a location
+ tags:
+ - Locations
+ get:
+ parameters:
+ - description: Location ID
+ in: path
+ name: id
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/types.LocationOut'
+ security:
+ - Bearer: []
+ summary: Gets a location and fields
+ tags:
+ - Locations
+ put:
+ parameters:
+ - description: Location ID
+ in: path
+ name: id
+ required: true
+ type: string
+ produces:
+ - application/json
+ responses:
+ "200":
+ description: OK
+ schema:
+ $ref: '#/definitions/types.LocationOut'
+ security:
+ - Bearer: []
+ summary: updates a location
+ tags:
+ - Locations
/v1/users/login:
post:
consumes:
diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go
index 0041cde..81efe4d 100644
--- a/backend/app/api/routes.go
+++ b/backend/app/api/routes.go
@@ -52,6 +52,9 @@ func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux {
r.Get(v1Base("/locations"), v1Handlers.HandleLocationGetAll())
r.Post(v1Base("/locations"), v1Handlers.HandleLocationCreate())
+ r.Get(v1Base("/locations/{id}"), v1Handlers.HandleLocationGet())
+ r.Put(v1Base("/locations/{id}"), v1Handlers.HandleLocationUpdate())
+ r.Delete(v1Base("/locations/{id}"), v1Handlers.HandleLocationDelete())
})
r.Group(func(r chi.Router) {
diff --git a/backend/app/api/v1/v1_ctrl_locations.go b/backend/app/api/v1/v1_ctrl_locations.go
index 0674e7a..f0b8ef1 100644
--- a/backend/app/api/v1/v1_ctrl_locations.go
+++ b/backend/app/api/v1/v1_ctrl_locations.go
@@ -3,12 +3,15 @@ package v1
import (
"net/http"
+ "github.com/go-chi/chi/v5"
+ "github.com/google/uuid"
"github.com/hay-kot/content/backend/internal/services"
"github.com/hay-kot/content/backend/internal/types"
+ "github.com/hay-kot/content/backend/pkgs/logger"
"github.com/hay-kot/content/backend/pkgs/server"
)
-// HandleUserSelf godoc
+// HandleLocationGetAll godoc
// @Summary Get All Locations
// @Tags Locations
// @Produce json
@@ -29,12 +32,12 @@ func (ctrl *V1Controller) HandleLocationGetAll() http.HandlerFunc {
}
}
-// HandleUserSelf godoc
+// HandleLocationCreate godoc
// @Summary Create a new location
// @Tags Locations
// @Produce json
// @Param payload body types.LocationCreate true "Location Data"
-// @Success 200 {object} types.LocationOut
+// @Success 200 {object} types.LocationSummary
// @Router /v1/locations [POST]
// @Security Bearer
func (ctrl *V1Controller) HandleLocationCreate() http.HandlerFunc {
@@ -57,3 +60,101 @@ func (ctrl *V1Controller) HandleLocationCreate() http.HandlerFunc {
server.Respond(w, http.StatusCreated, location)
}
}
+
+func (ctrl *V1Controller) partialParseIdAndUser(w http.ResponseWriter, r *http.Request) (uuid.UUID, *types.UserOut, error) {
+ uid, err := uuid.Parse(chi.URLParam(r, "id"))
+ if err != nil {
+ ctrl.log.Debug(err.Error(), logger.Props{
+ "details": "failed to convert id to valid UUID",
+ })
+ server.RespondError(w, http.StatusBadRequest, err)
+ return uuid.Nil, nil, err
+ }
+
+ user := services.UseUserCtx(r.Context())
+ return uid, user, nil
+}
+
+// HandleLocationDelete godocs
+// @Summary deletes a location
+// @Tags Locations
+// @Produce json
+// @Param id path string true "Location ID"
+// @Success 204
+// @Router /v1/locations/{id} [DELETE]
+// @Security Bearer
+func (ctrl *V1Controller) HandleLocationDelete() http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ uid, user, err := ctrl.partialParseIdAndUser(w, r)
+ if err != nil {
+ return
+ }
+
+ err = ctrl.svc.Location.Delete(r.Context(), user.GroupID, uid)
+ if err != nil {
+ ctrl.log.Error(err, nil)
+ server.RespondServerError(w)
+ return
+ }
+ server.Respond(w, http.StatusNoContent, nil)
+ }
+}
+
+// HandleLocationGet godocs
+// @Summary Gets a location and fields
+// @Tags Locations
+// @Produce json
+// @Param id path string true "Location ID"
+// @Success 200 {object} types.LocationOut
+// @Router /v1/locations/{id} [GET]
+// @Security Bearer
+func (ctrl *V1Controller) HandleLocationGet() http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ uid, user, err := ctrl.partialParseIdAndUser(w, r)
+ if err != nil {
+ return
+ }
+
+ location, err := ctrl.svc.Location.GetOne(r.Context(), user.GroupID, uid)
+ if err != nil {
+ ctrl.log.Error(err, nil)
+ server.RespondServerError(w)
+ return
+ }
+ server.Respond(w, http.StatusOK, location)
+ }
+}
+
+// HandleLocationUpdate godocs
+// @Summary updates a location
+// @Tags Locations
+// @Produce json
+// @Param id path string true "Location ID"
+// @Success 200 {object} types.LocationOut
+// @Router /v1/locations/{id} [PUT]
+// @Security Bearer
+func (ctrl *V1Controller) HandleLocationUpdate() http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ body := types.LocationUpdate{}
+ if err := server.Decode(r, &body); err != nil {
+ ctrl.log.Error(err, nil)
+ server.RespondError(w, http.StatusInternalServerError, err)
+ return
+ }
+
+ uid, user, err := ctrl.partialParseIdAndUser(w, r)
+ if err != nil {
+ return
+ }
+
+ body.ID = uid
+
+ result, err := ctrl.svc.Location.Update(r.Context(), user.GroupID, body)
+ if err != nil {
+ ctrl.log.Error(err, nil)
+ server.RespondServerError(w)
+ return
+ }
+ server.Respond(w, http.StatusOK, result)
+ }
+}
diff --git a/backend/internal/repo/repo_locations.go b/backend/internal/repo/repo_locations.go
index babc185..3844b08 100644
--- a/backend/internal/repo/repo_locations.go
+++ b/backend/internal/repo/repo_locations.go
@@ -15,7 +15,11 @@ type EntLocationRepository struct {
}
func (r *EntLocationRepository) Get(ctx context.Context, ID uuid.UUID) (*ent.Location, error) {
- return r.db.Location.Get(ctx, ID)
+ return r.db.Location.Query().
+ Where(location.ID(ID)).
+ WithGroup().
+ WithItems().
+ Only(ctx)
}
func (r *EntLocationRepository) GetAll(ctx context.Context, groupId uuid.UUID) ([]*ent.Location, error) {
@@ -37,10 +41,16 @@ func (r *EntLocationRepository) Create(ctx context.Context, groupdId uuid.UUID,
}
func (r *EntLocationRepository) Update(ctx context.Context, data types.LocationUpdate) (*ent.Location, error) {
- return r.db.Location.UpdateOneID(data.ID).
+ _, err := r.db.Location.UpdateOneID(data.ID).
SetName(data.Name).
SetDescription(data.Description).
Save(ctx)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return r.Get(ctx, data.ID)
}
func (r *EntLocationRepository) Delete(ctx context.Context, id uuid.UUID) error {
diff --git a/backend/internal/services/mappers/helpers.go b/backend/internal/services/mappers/helpers.go
new file mode 100644
index 0000000..1ffbf7a
--- /dev/null
+++ b/backend/internal/services/mappers/helpers.go
@@ -0,0 +1,9 @@
+package mappers
+
+func MapEach[T any, U any](items []T, fn func(T) U) []U {
+ result := make([]U, len(items))
+ for i, item := range items {
+ result[i] = fn(item)
+ }
+ return result
+}
diff --git a/backend/internal/services/mappers/items.go b/backend/internal/services/mappers/items.go
new file mode 100644
index 0000000..475bb30
--- /dev/null
+++ b/backend/internal/services/mappers/items.go
@@ -0,0 +1,17 @@
+package mappers
+
+import (
+ "github.com/hay-kot/content/backend/ent"
+ "github.com/hay-kot/content/backend/internal/types"
+)
+
+func ToItemSummary(item *ent.Item) *types.ItemSummary {
+ return &types.ItemSummary{
+ ID: item.ID,
+ LocationID: item.Edges.Location.ID,
+ Name: item.Name,
+ Description: item.Description,
+ CreatedAt: item.CreatedAt,
+ UpdatedAt: item.UpdatedAt,
+ }
+}
diff --git a/backend/internal/services/mappers/locations.go b/backend/internal/services/mappers/locations.go
new file mode 100644
index 0000000..f8cb2a2
--- /dev/null
+++ b/backend/internal/services/mappers/locations.go
@@ -0,0 +1,32 @@
+package mappers
+
+import (
+ "github.com/hay-kot/content/backend/ent"
+ "github.com/hay-kot/content/backend/internal/types"
+)
+
+func ToLocationSummary(location *ent.Location) *types.LocationSummary {
+ return &types.LocationSummary{
+ ID: location.ID,
+ GroupID: location.Edges.Group.ID,
+ Name: location.Name,
+ Description: location.Description,
+ CreatedAt: location.CreatedAt,
+ UpdatedAt: location.UpdatedAt,
+ }
+}
+
+func ToLocationSummaryErr(location *ent.Location, err error) (*types.LocationSummary, error) {
+ return ToLocationSummary(location), err
+}
+
+func ToLocationOut(location *ent.Location) *types.LocationOut {
+ return &types.LocationOut{
+ LocationSummary: *ToLocationSummary(location),
+ Items: MapEach(location.Edges.Items, ToItemSummary),
+ }
+}
+
+func ToLocationOutErr(location *ent.Location, err error) (*types.LocationOut, error) {
+ return ToLocationOut(location), err
+}
diff --git a/backend/internal/services/service_locations.go b/backend/internal/services/service_locations.go
index b809c28..6394e94 100644
--- a/backend/internal/services/service_locations.go
+++ b/backend/internal/services/service_locations.go
@@ -2,44 +2,75 @@ package services
import (
"context"
+ "errors"
"github.com/google/uuid"
- "github.com/hay-kot/content/backend/ent"
"github.com/hay-kot/content/backend/internal/repo"
+ "github.com/hay-kot/content/backend/internal/services/mappers"
"github.com/hay-kot/content/backend/internal/types"
)
+var (
+ ErrNotOwner = errors.New("not owner")
+)
+
type LocationService struct {
repos *repo.AllRepos
}
-func ToLocationOut(location *ent.Location, err error) (*types.LocationOut, error) {
- return &types.LocationOut{
- ID: location.ID,
- GroupID: location.Edges.Group.ID,
- Name: location.Name,
- Description: location.Description,
- CreatedAt: location.CreatedAt,
- UpdatedAt: location.UpdatedAt,
- }, err
+func (svc *LocationService) GetOne(ctx context.Context, groupId uuid.UUID, id uuid.UUID) (*types.LocationOut, error) {
+ location, err := svc.repos.Locations.Get(ctx, id)
+
+ if err != nil {
+ return nil, err
+ }
+
+ if location.Edges.Group.ID != groupId {
+ return nil, ErrNotOwner
+ }
+
+ return mappers.ToLocationOut(location), nil
}
-func (svc *LocationService) Create(ctx context.Context, groupId uuid.UUID, data types.LocationCreate) (*types.LocationOut, error) {
- location, err := svc.repos.Locations.Create(ctx, groupId, data)
- return ToLocationOut(location, err)
-}
-
-func (svc *LocationService) GetAll(ctx context.Context, groupId uuid.UUID) ([]*types.LocationOut, error) {
+func (svc *LocationService) GetAll(ctx context.Context, groupId uuid.UUID) ([]*types.LocationSummary, error) {
locations, err := svc.repos.Locations.GetAll(ctx, groupId)
if err != nil {
return nil, err
}
- locationsOut := make([]*types.LocationOut, len(locations))
+ locationsOut := make([]*types.LocationSummary, len(locations))
for i, location := range locations {
- locationOut, _ := ToLocationOut(location, nil)
- locationsOut[i] = locationOut
+ locationsOut[i] = mappers.ToLocationSummary(location)
}
return locationsOut, nil
}
+
+func (svc *LocationService) Create(ctx context.Context, groupId uuid.UUID, data types.LocationCreate) (*types.LocationSummary, error) {
+ location, err := svc.repos.Locations.Create(ctx, groupId, data)
+ return mappers.ToLocationSummaryErr(location, err)
+}
+
+func (svc *LocationService) Delete(ctx context.Context, groupId uuid.UUID, id uuid.UUID) error {
+ location, err := svc.repos.Locations.Get(ctx, id)
+ if err != nil {
+ return err
+ }
+ if location.Edges.Group.ID != groupId {
+ return ErrNotOwner
+ }
+
+ return svc.repos.Locations.Delete(ctx, id)
+}
+
+func (svc *LocationService) Update(ctx context.Context, groupId uuid.UUID, data types.LocationUpdate) (*types.LocationOut, error) {
+ location, err := svc.repos.Locations.Get(ctx, data.ID)
+ if err != nil {
+ return nil, err
+ }
+ if location.Edges.Group.ID != groupId {
+ return nil, ErrNotOwner
+ }
+
+ return mappers.ToLocationOutErr(svc.repos.Locations.Update(ctx, data))
+}
diff --git a/backend/internal/types/location_types.go b/backend/internal/types/location_types.go
index 9e9ee19..f60adf4 100644
--- a/backend/internal/types/location_types.go
+++ b/backend/internal/types/location_types.go
@@ -17,7 +17,7 @@ type LocationUpdate struct {
Description string `json:"description"`
}
-type LocationOut struct {
+type LocationSummary struct {
ID uuid.UUID `json:"id"`
GroupID uuid.UUID `json:"groupId"`
Name string `json:"name"`
@@ -25,3 +25,17 @@ type LocationOut struct {
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
+
+type ItemSummary struct {
+ ID uuid.UUID `json:"id"`
+ LocationID uuid.UUID `json:"locationId"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ CreatedAt time.Time `json:"createdAt"`
+ UpdatedAt time.Time `json:"updatedAt"`
+}
+
+type LocationOut struct {
+ LocationSummary
+ Items []*ItemSummary `json:"items"`
+}
diff --git a/frontend/.gitignore b/frontend/.gitignore
index a0782ae..438cb08 100644
--- a/frontend/.gitignore
+++ b/frontend/.gitignore
@@ -1,10 +1,8 @@
node_modules
-.DS_Store
+*.log*
+.nuxt
+.nitro
+.cache
+.output
+.env
dist
-dist-ssr
-*.local
-.*-debug.log
-*.log
-.vercel
-.vite-ssg-temp
-.idea
diff --git a/frontend/LICENSE b/frontend/LICENSE
deleted file mode 100644
index a441282..0000000
--- a/frontend/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2021 Christopher Reeve
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/frontend/README.md b/frontend/README.md
index c9c506a..d90610e 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,137 +1,42 @@
-
-
-
+# Nuxt 3 Minimal Starter
-Opinionated Vite starter template with [TailwindCSS](https://tailwindcss.com/)
+Look at the [nuxt 3 documentation](https://v3.nuxtjs.org) to learn more.
-Inspired by [Vitesse](https://github.com/antfu/vitesse) ❤
+## Setup
-## Features
-
-- ⚡️ [Vue 3](https://github.com/vuejs/vue-next), [Vite 2](https://github.com/vitejs/vite), [pnpm](https://pnpm.js.org/), [ESBuild](https://github.com/evanw/esbuild) - born with fastness
-
-- 🗂 [File based routing](./src/pages)
-
-- 📦 [Components auto importing](./src/components)
-
-- 🍍 [State Management via Pinia](https://pinia.esm.dev/)
-
-- 📑 [Layout system](./src/layouts)
-
-- 📲 [PWA](https://github.com/antfu/vite-plugin-pwa)
-
-- 🌍 [I18n ready](./locales)
-
-- 🎨 [Tailwind CSS](https://tailwindcss.com/) - Rapidly build modern websites without ever leaving your HTML.
-
-- 😃 [Use icons from any icon sets, with no compromise](https://github.com/antfu/unplugin-icons)
-
-- 🔥 Use the [new `
+
+
+
+
+ Create Location
+
+
+
+ Homebox
+
+
+
+ {{ link.name }}
+
+
+ /
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/frontend/components/App/Toast.vue b/frontend/components/App/Toast.vue
new file mode 100644
index 0000000..9804066
--- /dev/null
+++ b/frontend/components/App/Toast.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ notify.message }}
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/Base/ActionsDivider.vue b/frontend/components/Base/ActionsDivider.vue
new file mode 100644
index 0000000..0affe50
--- /dev/null
+++ b/frontend/components/Base/ActionsDivider.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/Base/Button.vue b/frontend/components/Base/Button.vue
new file mode 100644
index 0000000..126ba30
--- /dev/null
+++ b/frontend/components/Base/Button.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/frontend/components/Base/Container.vue b/frontend/components/Base/Container.vue
new file mode 100644
index 0000000..29f515b
--- /dev/null
+++ b/frontend/components/Base/Container.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/frontend/components/Base/Details.vue b/frontend/components/Base/Details.vue
new file mode 100644
index 0000000..679e3e5
--- /dev/null
+++ b/frontend/components/Base/Details.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
-
+ {{ dKey }}
+
+ -
+ {{ dValue }}
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/Base/Modal.vue b/frontend/components/Base/Modal.vue
new file mode 100644
index 0000000..fa2b715
--- /dev/null
+++ b/frontend/components/Base/Modal.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
diff --git a/frontend/components/Base/SectionHeader.vue b/frontend/components/Base/SectionHeader.vue
new file mode 100644
index 0000000..3e1e4e6
--- /dev/null
+++ b/frontend/components/Base/SectionHeader.vue
@@ -0,0 +1,10 @@
+
+
+
diff --git a/frontend/components/Form/TextField.vue b/frontend/components/Form/TextField.vue
new file mode 100644
index 0000000..0351e1c
--- /dev/null
+++ b/frontend/components/Form/TextField.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
diff --git a/frontend/components/Icon.vue b/frontend/components/Icon.vue
new file mode 100644
index 0000000..ecdae33
--- /dev/null
+++ b/frontend/components/Icon.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+ {{ name }}
+
diff --git a/frontend/components/ModalConfirm.vue b/frontend/components/ModalConfirm.vue
new file mode 100644
index 0000000..b77c0bc
--- /dev/null
+++ b/frontend/components/ModalConfirm.vue
@@ -0,0 +1,15 @@
+
+
+ Confirm
+
+
+ Confirm
+
+
+
+
+
diff --git a/frontend/composables/use-api.ts b/frontend/composables/use-api.ts
new file mode 100644
index 0000000..3e63488
--- /dev/null
+++ b/frontend/composables/use-api.ts
@@ -0,0 +1,23 @@
+import { PublicApi } from "~~/lib/api/public";
+import { UserApi } from "~~/lib/api/user";
+import { Requests } from "~~/lib/requests";
+import { useAuthStore } from "~~/stores/auth";
+
+async function ApiDebugger(r: Response) {
+ console.table({
+ "Request Url": r.url,
+ "Response Status": r.status,
+ "Response Status Text": r.statusText,
+ });
+}
+
+export function usePublicApi(): PublicApi {
+ const requests = new Requests("", "", {}, ApiDebugger);
+ return new PublicApi(requests);
+}
+
+export function useUserApi(): UserApi {
+ const authStore = useAuthStore();
+ const requests = new Requests("", () => authStore.token, {}, ApiDebugger);
+ return new UserApi(requests);
+}
diff --git a/frontend/composables/use-confirm.ts b/frontend/composables/use-confirm.ts
new file mode 100644
index 0000000..b8e2c2c
--- /dev/null
+++ b/frontend/composables/use-confirm.ts
@@ -0,0 +1,40 @@
+import { UseConfirmDialogReturn } from '@vueuse/core';
+import { Ref } from 'vue';
+
+type Store = UseConfirmDialogReturn & {
+ text: Ref;
+ setup: boolean;
+};
+
+const store: Partial = {
+ text: ref('Are you sure you want to delete this item? '),
+ setup: false,
+};
+
+/**
+ * This function is used to wrap the ModalConfirmation which is a "Singleton" component
+ * that is used to confirm actions. It's mounded once on the root of the page and reused
+ * for every confirmation action that is required.
+ *
+ * This is in an experimental phase of development and may have unknown or unexpected side effects.
+ */
+export function useConfirm(): Store {
+ if (!store.setup) {
+ store.setup = true;
+ const { isRevealed, reveal, confirm, cancel } = useConfirmDialog();
+ store.isRevealed = isRevealed;
+ store.reveal = reveal;
+ store.confirm = confirm;
+ store.cancel = cancel;
+ }
+
+ async function openDialog(msg: string) {
+ store.text.value = msg;
+ return await store.reveal();
+ }
+
+ return {
+ ...(store as Store),
+ reveal: openDialog,
+ };
+}
diff --git a/frontend/src/composables/use-ids.ts b/frontend/composables/use-ids.ts
similarity index 100%
rename from frontend/src/composables/use-ids.ts
rename to frontend/composables/use-ids.ts
diff --git a/frontend/src/composables/use-notifier.ts b/frontend/composables/use-notifier.ts
similarity index 100%
rename from frontend/src/composables/use-notifier.ts
rename to frontend/composables/use-notifier.ts
diff --git a/frontend/composables/use-preferences.ts b/frontend/composables/use-preferences.ts
new file mode 100644
index 0000000..a933505
--- /dev/null
+++ b/frontend/composables/use-preferences.ts
@@ -0,0 +1,23 @@
+import { Ref } from 'vue';
+
+export type LocationViewPreferences = {
+ showDetails: boolean;
+};
+
+/**
+ * useLocationViewPreferences loads the view preferences from local storage and hydrates
+ * them. These are reactive and will update the local storage when changed.
+ */
+export function useLocationViewPreferences(): Ref {
+ const results = useLocalStorage(
+ 'homebox/preferences/location',
+ {
+ showDetails: true,
+ },
+ { mergeDefaults: true }
+ );
+
+ // casting is required because the type returned is removable, however since we
+ // use `mergeDefaults` the result _should_ always be present.
+ return results as unknown as Ref;
+}
diff --git a/frontend/index.html b/frontend/index.html
deleted file mode 100644
index 63af3c8..0000000
--- a/frontend/index.html
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
- Vitailse | Opinionated vite starter template with TailwindCSS
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/layouts/404.vue b/frontend/layouts/404.vue
new file mode 100644
index 0000000..ee22669
--- /dev/null
+++ b/frontend/layouts/404.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/frontend/layouts/default.vue b/frontend/layouts/default.vue
new file mode 100644
index 0000000..a65f7e3
--- /dev/null
+++ b/frontend/layouts/default.vue
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/frontend/layouts/empty.vue b/frontend/layouts/empty.vue
new file mode 100644
index 0000000..a263ec6
--- /dev/null
+++ b/frontend/layouts/empty.vue
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/frontend/layouts/home.vue b/frontend/layouts/home.vue
new file mode 100644
index 0000000..634897b
--- /dev/null
+++ b/frontend/layouts/home.vue
@@ -0,0 +1,9 @@
+
+
+
diff --git a/frontend/src/api/base/base-api.ts b/frontend/lib/api/base/base-api.ts
similarity index 84%
rename from frontend/src/api/base/base-api.ts
rename to frontend/lib/api/base/base-api.ts
index e05a94d..0f386bf 100644
--- a/frontend/src/api/base/base-api.ts
+++ b/frontend/lib/api/base/base-api.ts
@@ -1,4 +1,4 @@
-import { Requests } from '../../lib/requests';
+import { Requests } from '../../requests';
// <
// TGetResult,
// TPostData,
diff --git a/frontend/src/api/base/base-types.ts b/frontend/lib/api/base/base-types.ts
similarity index 100%
rename from frontend/src/api/base/base-types.ts
rename to frontend/lib/api/base/base-types.ts
diff --git a/frontend/src/api/base/index.test.ts b/frontend/lib/api/base/index.test.ts
similarity index 100%
rename from frontend/src/api/base/index.test.ts
rename to frontend/lib/api/base/index.test.ts
diff --git a/frontend/src/api/base/index.ts b/frontend/lib/api/base/index.ts
similarity index 100%
rename from frontend/src/api/base/index.ts
rename to frontend/lib/api/base/index.ts
diff --git a/frontend/src/api/base/urls.ts b/frontend/lib/api/base/urls.ts
similarity index 100%
rename from frontend/src/api/base/urls.ts
rename to frontend/lib/api/base/urls.ts
diff --git a/frontend/src/api/classes/locations.ts b/frontend/lib/api/classes/locations.ts
similarity index 58%
rename from frontend/src/api/classes/locations.ts
rename to frontend/lib/api/classes/locations.ts
index 17ad9de..749a0f2 100644
--- a/frontend/src/api/classes/locations.ts
+++ b/frontend/lib/api/classes/locations.ts
@@ -13,6 +13,8 @@ export type Location = LocationCreate & {
updatedAt: string;
};
+export type LocationUpdate = LocationCreate;
+
export class LocationsApi extends BaseAPI {
async getAll() {
return this.http.get>(UrlBuilder('/locations'));
@@ -21,4 +23,15 @@ export class LocationsApi extends BaseAPI {
async create(location: LocationCreate) {
return this.http.post(UrlBuilder('/locations'), location);
}
+
+ async get(id: string) {
+ return this.http.get(UrlBuilder(`/locations/${id}`));
+ }
+ async delete(id: string) {
+ return this.http.delete(UrlBuilder(`/locations/${id}`));
+ }
+
+ async update(id: string, location: LocationUpdate) {
+ return this.http.put(UrlBuilder(`/locations/${id}`), location);
+ }
}
diff --git a/frontend/lib/api/public.ts b/frontend/lib/api/public.ts
new file mode 100644
index 0000000..25f5af6
--- /dev/null
+++ b/frontend/lib/api/public.ts
@@ -0,0 +1,39 @@
+import { BaseAPI, UrlBuilder } from "./base";
+
+export type LoginResult = {
+ token: string;
+ expiresAt: string;
+};
+
+export type LoginPayload = {
+ username: string;
+ password: string;
+};
+
+export type RegisterPayload = {
+ user: {
+ email: string;
+ password: string;
+ name: string;
+ };
+ groupName: string;
+};
+
+export class PublicApi extends BaseAPI {
+ public login(username: string, password: string) {
+ return this.http.post(
+ UrlBuilder("/users/login"),
+ {
+ username,
+ password,
+ }
+ );
+ }
+
+ public register(payload: RegisterPayload) {
+ return this.http.post(
+ UrlBuilder("/users/register"),
+ payload
+ );
+ }
+}
diff --git a/frontend/src/api/user.ts b/frontend/lib/api/user.ts
similarity index 58%
rename from frontend/src/api/user.ts
rename to frontend/lib/api/user.ts
index 1bd0f2a..c5299f5 100644
--- a/frontend/src/api/user.ts
+++ b/frontend/lib/api/user.ts
@@ -1,6 +1,6 @@
-import { Requests } from '@/lib/requests';
-import { BaseAPI, UrlBuilder } from './base';
-import { LocationsApi } from './classes/locations';
+import { Requests } from "~~/lib/requests";
+import { BaseAPI, UrlBuilder } from "./base";
+import { LocationsApi } from "./classes/locations";
export type Result = {
item: T;
@@ -25,10 +25,10 @@ export class UserApi extends BaseAPI {
}
public self() {
- return this.http.get>(UrlBuilder('/users/self'));
+ return this.http.get>(UrlBuilder("/users/self"));
}
public logout() {
- return this.http.post