From 9a8da76c00fdaa6c5380b2f39a2a88aae21f11c3 Mon Sep 17 00:00:00 2001 From: Pier Dolique Date: Sun, 21 Jul 2024 03:48:51 +0300 Subject: [PATCH] feat: User inventory page * Fixes #11 * Fixes #12 * Fixes #14 * Fixes #18 * Fixes #19 * Fixes #20 * Fixes #25 --- app/app.vue | 15 +- app/assets/styles/_base.scss | 51 +- app/components/CircleSpinner.vue | 30 + .../{NavigationBar.vue => PageHeader.vue} | 24 +- app/components/PerdButton.vue | 44 +- app/components/PerdInput.vue | 15 +- app/components/PerdLink.vue | 9 +- app/components/PerdSearch/DefaultOption.vue | 56 + app/components/PerdSearch/EmptyOption.vue | 15 + app/components/PerdSearch/PerdSearch.vue | 275 + app/components/PerdSearch/SearchOption.vue | 11 + app/components/equipment/EmptyContent.vue | 23 + app/components/equipment/EquipmentRow.vue | 48 - app/components/equipment/EquipmentTable.vue | 172 +- app/components/inventory/SearchOption.vue | 50 + app/composables/use-user-equipment-state.ts | 10 + app/composables/use-user-equipment.ts | 23 + app/composables/use-user-state.ts | 40 +- app/layouts/default.vue | 6 +- app/middleware/0.user.global.ts | 9 + app/middleware/1.auth.global.ts | 23 + app/middleware/admin.ts | 43 +- app/pages/index.vue | 55 +- app/pages/inventory.vue | 116 + app/pages/login.vue | 61 + app/pages/manager/equipment/add.vue | 3 + app/pages/manager/equipment/index.vue | 14 +- app/utils/async.ts | 3 + app/utils/string.ts | 29 + app/utils/types.ts | 3 + constants.ts | 5 + nuxt.config.ts | 12 +- package-lock.json | 13392 +++++++--------- package.json | 4 + server/api/admin/create-equipment.post.ts | 21 +- server/api/auth/check-admin.ts | 11 - server/api/auth/create-session.post.ts | 2 + server/api/auth/logout.post.ts | 2 - server/api/equipment/index.get.ts | 52 +- server/api/equipment/index.post.ts | 42 + server/api/user/equipment/[id].delete.ts | 35 + server/api/user/equipment/index.get.ts | 35 + server/database/schema.ts | 9 +- server/middleware/api-session-check.ts | 25 + 44 files changed, 6640 insertions(+), 8283 deletions(-) create mode 100644 app/components/CircleSpinner.vue rename app/components/{NavigationBar.vue => PageHeader.vue} (71%) create mode 100644 app/components/PerdSearch/DefaultOption.vue create mode 100644 app/components/PerdSearch/EmptyOption.vue create mode 100644 app/components/PerdSearch/PerdSearch.vue create mode 100644 app/components/PerdSearch/SearchOption.vue create mode 100644 app/components/equipment/EmptyContent.vue delete mode 100644 app/components/equipment/EquipmentRow.vue create mode 100644 app/components/inventory/SearchOption.vue create mode 100644 app/composables/use-user-equipment-state.ts create mode 100644 app/composables/use-user-equipment.ts create mode 100644 app/middleware/0.user.global.ts create mode 100644 app/middleware/1.auth.global.ts create mode 100644 app/pages/inventory.vue create mode 100644 app/pages/login.vue create mode 100644 app/utils/async.ts create mode 100644 app/utils/string.ts create mode 100644 app/utils/types.ts delete mode 100644 server/api/auth/check-admin.ts create mode 100644 server/api/equipment/index.post.ts create mode 100644 server/api/user/equipment/[id].delete.ts create mode 100644 server/api/user/equipment/index.get.ts create mode 100644 server/middleware/api-session-check.ts diff --git a/app/app.vue b/app/app.vue index 4872895..2ab8fbf 100644 --- a/app/app.vue +++ b/app/app.vue @@ -5,25 +5,12 @@ - - diff --git a/app/assets/styles/_base.scss b/app/assets/styles/_base.scss index d63be65..c854e91 100644 --- a/app/assets/styles/_base.scss +++ b/app/assets/styles/_base.scss @@ -16,14 +16,14 @@ --screen-desktop-l: #{$screen-desktop-l}; // Spacings - --spacing-2: 2px; - --spacing-4: 4px; - --spacing-8: 8px; - --spacing-12: 12px; - --spacing-16: 16px; - --spacing-24: 24px; - --spacing-32: 32px; - --spacing-48: 48px; + --spacing-2: 0.125rem; + --spacing-4: 0.25rem; + --spacing-8: 0.5rem; + --spacing-12: 0.75rem; + --spacing-16: 1rem; + --spacing-24: 1.5rem; + --spacing-32: 2rem; + --spacing-48: 3rem; // Colors --color-blue-50: #f0fafb; @@ -38,16 +38,37 @@ --color-blue-900: #244555; --color-blue-950: #132c39; --color-white: #fff; + --color-primary: #244555; + --color-secondary: #666; + + // Font sizes + --font-size-12: 0.75rem; + --font-size-16: 1rem; + + // Font weights + --font-weight-medium: 500; // Borders - --border-radius-1: 8px; - --border-radius-2: 16px; + --border-radius-1: 0.5rem; + --border-radius-2: 1rem; // Inputs - --input-height: 48px; - --input-spacing-horizontal: 16px; + --input-height: 3rem; + --input-spacing-horizontal: 1rem; + --input-color-hover-bg: rgb(0 0 0 / 10%); --input-color-main: #287892; - --input-color-focus: #266278; - --input-color-text: #244555; - --input-color-placeholder: rgb(140, 140, 140); + --input-color-focus: #a26830; + --input-color-active: #7a4f24; + --input-color-text: #f0fafb; + --input-color-placeholder: #8c8c8c; + + // Secondary inputs + --input-secondary-color-main: #f0fafb; + --input-secondary-color-text: #244555; + --input-secondary-color-focus: #d8f2f5; + --input-secondary-color-active: #b5e3ec; + --input-secondary-color-disabled: #2d95ad; + + // Small inputs + --input-small-spacing: 1.5rem; } diff --git a/app/components/CircleSpinner.vue b/app/components/CircleSpinner.vue new file mode 100644 index 0000000..dbc0604 --- /dev/null +++ b/app/components/CircleSpinner.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/app/components/NavigationBar.vue b/app/components/PageHeader.vue similarity index 71% rename from app/components/NavigationBar.vue rename to app/components/PageHeader.vue index e49f2d3..9bdeb6b 100644 --- a/app/components/NavigationBar.vue +++ b/app/components/PageHeader.vue @@ -9,7 +9,14 @@
+ Inventory + + + Equipment manager @@ -18,7 +25,7 @@
Log out @@ -28,16 +35,21 @@ diff --git a/app/components/PerdButton.vue b/app/components/PerdButton.vue index 8da6648..6100107 100644 --- a/app/components/PerdButton.vue +++ b/app/components/PerdButton.vue @@ -1,9 +1,24 @@ + + diff --git a/app/components/PerdInput.vue b/app/components/PerdInput.vue index fb67528..b76874e 100644 --- a/app/components/PerdInput.vue +++ b/app/components/PerdInput.vue @@ -55,13 +55,9 @@ background-color: var(--color-white); border: 1px solid var(--input-color-main); border-radius: var(--border-radius-2); - outline: 1px solid transparent; - transition: - outline-color 0.15s ease-out, - border-color 0.15s ease-out; + transition: border-color 0.15s ease-out; &:has(.input:focus-visible) { - outline-color: var(--input-color-focus); border-color: var(--input-color-focus); } } @@ -75,11 +71,12 @@ display: flex; align-items: center; pointer-events: none; - color: var(--input-color-main); + color: var(--input-secondary-color-text); left: var(--input-spacing-horizontal); transform-origin: top left; user-select: none; transition: + color 0.15s linear, scale 0.15s linear, translate 0.15s linear; @@ -88,6 +85,10 @@ scale: 0.70; translate: 0 -5px; } + + .input:focus-visible + & { + color: var(--input-color-focus); + } } .input { @@ -101,7 +102,7 @@ border: none; background: none; outline: none; - color: var(--input-color-text); + color: var(--input-secondary-color-text); } .input::placeholder { diff --git a/app/components/PerdLink.vue b/app/components/PerdLink.vue index bac7668..7b605d6 100644 --- a/app/components/PerdLink.vue +++ b/app/components/PerdLink.vue @@ -18,8 +18,13 @@ color: var(--color-blue-700); transition: color 0.15s ease-out; - &:hover { - color: var(--color-blue-950); + &:hover, + &:focus-visible { + color: var(--input-color-focus); + } + + &:active { + color: var(--input-color-active); } } diff --git a/app/components/PerdSearch/DefaultOption.vue b/app/components/PerdSearch/DefaultOption.vue new file mode 100644 index 0000000..5f40fd7 --- /dev/null +++ b/app/components/PerdSearch/DefaultOption.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/app/components/PerdSearch/EmptyOption.vue b/app/components/PerdSearch/EmptyOption.vue new file mode 100644 index 0000000..f7847cb --- /dev/null +++ b/app/components/PerdSearch/EmptyOption.vue @@ -0,0 +1,15 @@ + + + diff --git a/app/components/PerdSearch/PerdSearch.vue b/app/components/PerdSearch/PerdSearch.vue new file mode 100644 index 0000000..dff0930 --- /dev/null +++ b/app/components/PerdSearch/PerdSearch.vue @@ -0,0 +1,275 @@ + + + + + diff --git a/app/components/PerdSearch/SearchOption.vue b/app/components/PerdSearch/SearchOption.vue new file mode 100644 index 0000000..8b2a11e --- /dev/null +++ b/app/components/PerdSearch/SearchOption.vue @@ -0,0 +1,11 @@ + + + diff --git a/app/components/equipment/EmptyContent.vue b/app/components/equipment/EmptyContent.vue new file mode 100644 index 0000000..289a648 --- /dev/null +++ b/app/components/equipment/EmptyContent.vue @@ -0,0 +1,23 @@ + + + diff --git a/app/components/equipment/EquipmentRow.vue b/app/components/equipment/EquipmentRow.vue deleted file mode 100644 index ed00454..0000000 --- a/app/components/equipment/EquipmentRow.vue +++ /dev/null @@ -1,48 +0,0 @@ - - - - - diff --git a/app/components/equipment/EquipmentTable.vue b/app/components/equipment/EquipmentTable.vue index a3f0428..9a419d1 100644 --- a/app/components/equipment/EquipmentTable.vue +++ b/app/components/equipment/EquipmentTable.vue @@ -1,72 +1,146 @@ - diff --git a/app/components/inventory/SearchOption.vue b/app/components/inventory/SearchOption.vue new file mode 100644 index 0000000..4d8e5a0 --- /dev/null +++ b/app/components/inventory/SearchOption.vue @@ -0,0 +1,50 @@ + + + diff --git a/app/composables/use-user-equipment-state.ts b/app/composables/use-user-equipment-state.ts new file mode 100644 index 0000000..342c527 --- /dev/null +++ b/app/composables/use-user-equipment-state.ts @@ -0,0 +1,10 @@ +interface EquipmentItem { + readonly id: string; + readonly name: string; + readonly weight: number; + readonly createdAt: string; +} + +export function useUserEquipmentState() { + return useState('userEquipment', () => []) +} diff --git a/app/composables/use-user-equipment.ts b/app/composables/use-user-equipment.ts new file mode 100644 index 0000000..117b26d --- /dev/null +++ b/app/composables/use-user-equipment.ts @@ -0,0 +1,23 @@ +export async function useUserEquipment() { + const equipment = useUserEquipmentState() + const { data } = await useFetch('/api/user/equipment') + + async function updateEquipment() { + try { + const response = await $fetch('/api/user/equipment') + + equipment.value = response + } catch { + console.error('Failed to update equipment') + } + } + + if (data.value !== undefined) { + equipment.value = data.value + } + + return { + equipment, + updateEquipment + } +} diff --git a/app/composables/use-user-state.ts b/app/composables/use-user-state.ts index c30b09a..6941e2a 100644 --- a/app/composables/use-user-state.ts +++ b/app/composables/use-user-state.ts @@ -1,24 +1,38 @@ -interface UserState { - userId: string | null - isAdmin: boolean +interface User { + userId: string | null; + isAdmin: boolean; + hasData: boolean; } -export function useUserState() { - const userState = useState('userState', () => ({ +export function useUserStore() { + const user = useState('user', () => ({ userId: null, - isAdmin: false + isAdmin: false, + hasData: false })) - const isAuthorized = computed(() => userState.value.userId !== null) + const isAuthenticated = computed(() => user.value.userId !== null) - function resetUserState() { - userState.value.userId = null - userState.value.isAdmin = false + async function getUser() { + const { data } = await useFetch('/api/user') + + if (data.value?.userId !== undefined) { + user.value.userId = data.value.userId + user.value.isAdmin = data.value.isAdmin + } + + user.value.hasData = true + } + + function resetAuthentication() { + user.value.userId = null + user.value.isAdmin = false } return { - isAuthorized, - resetUserState, - userState + getUser, + isAuthenticated, + resetAuthentication, + user } } diff --git a/app/layouts/default.vue b/app/layouts/default.vue index 1a18fd0..9bc8db9 100644 --- a/app/layouts/default.vue +++ b/app/layouts/default.vue @@ -1,6 +1,6 @@ + + diff --git a/app/pages/login.vue b/app/pages/login.vue new file mode 100644 index 0000000..211884b --- /dev/null +++ b/app/pages/login.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/app/pages/manager/equipment/add.vue b/app/pages/manager/equipment/add.vue index 67d6051..e164c2b 100644 --- a/app/pages/manager/equipment/add.vue +++ b/app/pages/manager/equipment/add.vue @@ -30,6 +30,9 @@