From 4773ce3dd79be0ba516e17e3abecb698406f3163 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sun, 1 Sep 2024 16:43:17 -0300 Subject: [PATCH 01/19] Update npm dependencies --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 6 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10d15d8..fe2ef49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@playwright/test": "^1.46.1", "@stylistic/eslint-plugin": "^2.7.2", "@supabase/supabase-js": "^2.45.3", - "@types/node": "^22.5.1", + "@types/node": "^22.5.2", "@vitejs/plugin-vue": "^5.1.3", "@vitest/coverage-v8": "^2.0.5", "@vue/test-utils": "^2.4.6", @@ -26,7 +26,7 @@ "eslint-plugin-tailwindcss": "^3.17.4", "google-protobuf": "^3.21.4", "gsap": "^3.12.5", - "happy-dom": "^15.7.0", + "happy-dom": "^15.7.3", "nuxt": "^3.13.0", "protobufjs": "^7.4.0", "protobufjs-cli": "^1.1.3", @@ -34,7 +34,7 @@ "typescript": "^5.5.4", "typescript-eslint": "^8.3.0", "vitest": "^2.0.5", - "vue-tsc": "^2.1.2" + "vue-tsc": "^2.1.4" } }, "node_modules/@alloc/quick-lru": { @@ -3720,9 +3720,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.1.tgz", - "integrity": "sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==", + "version": "22.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.2.tgz", + "integrity": "sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==", "dev": true, "dependencies": { "undici-types": "~6.19.2" @@ -4556,9 +4556,9 @@ } }, "node_modules/@vue/language-core": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.2.tgz", - "integrity": "sha512-tt2J7C+l0J/T5PaLhJ0jvCCi0JNwu3e8azWTYxW3jmAW5B/dac0g5UxmI7l59CQgCGFotqUqI3tXjfZgoWNtog==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.1.4.tgz", + "integrity": "sha512-i8pfAgNjTNjabBX1xRsuV6aRw2E8bdQXwd5H8m3cUkTVJju3QN5nfdoXET0uK+yXsuloNJPzo6PXFujRRPNmMA==", "dev": true, "dependencies": { "@volar/language-core": "~2.4.1", @@ -8050,9 +8050,9 @@ } }, "node_modules/happy-dom": { - "version": "15.7.0", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.7.0.tgz", - "integrity": "sha512-tH48xarc37FLrtpNWuA89xw4IS1dxNrfzI8ehiafkj3kM3JSCMknuigKv2unDPRzd2C9Hv7ZDPkqPiBCvIz2Dg==", + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-15.7.3.tgz", + "integrity": "sha512-w3RUaYNXFJX5LiNVhOJLK4GqCB1bFj1FvELtpon3HrN8gUpS09V0Vvm4/BBRRj7mLUE1+ch8PKv1JxEp/0IHjA==", "dev": true, "dependencies": { "entities": "^4.5.0", @@ -15115,13 +15115,13 @@ } }, "node_modules/vue-tsc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.2.tgz", - "integrity": "sha512-PH1BDxWT3eaPhl73elyZj6DV0nR3K4IFoUM1sGzMXXQneovVUwHQytdSyAHiED5MtEINGSHpL/Hs9ch+c/tDTw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.4.tgz", + "integrity": "sha512-XTzMXQcsixAvNbpou/9qngEsZawaiJRZH3Ja+lfgRfv2A1TJv9vnZ/Kyv7XxPqv/TaZVFSnjGpM87VbWIg6yQg==", "dev": true, "dependencies": { "@volar/typescript": "~2.4.1", - "@vue/language-core": "2.1.2", + "@vue/language-core": "2.1.4", "semver": "^7.5.4" }, "bin": { diff --git a/package.json b/package.json index 05c2cfa..12ab247 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@playwright/test": "^1.46.1", "@stylistic/eslint-plugin": "^2.7.2", "@supabase/supabase-js": "^2.45.3", - "@types/node": "^22.5.1", + "@types/node": "^22.5.2", "@vitejs/plugin-vue": "^5.1.3", "@vitest/coverage-v8": "^2.0.5", "@vue/test-utils": "^2.4.6", @@ -37,7 +37,7 @@ "eslint-plugin-tailwindcss": "^3.17.4", "google-protobuf": "^3.21.4", "gsap": "^3.12.5", - "happy-dom": "^15.7.0", + "happy-dom": "^15.7.3", "nuxt": "^3.13.0", "protobufjs": "^7.4.0", "protobufjs-cli": "^1.1.3", @@ -45,7 +45,7 @@ "typescript": "^5.5.4", "typescript-eslint": "^8.3.0", "vitest": "^2.0.5", - "vue-tsc": "^2.1.2" + "vue-tsc": "^2.1.4" }, "overrides": { "uri-js": "npm:uri-js-replace", From 87443139af77f29eeac798e7c946cd318b9df54e Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sun, 1 Sep 2024 21:43:59 -0300 Subject: [PATCH 02/19] Introduce the auth, list, and storage composables for the worklog tracker --- .../worklog-tracker/useWorklogAuth.ts | 41 ++++++++++++ .../worklog-tracker/useWorklogList.ts | 39 +++++++++++ .../worklog-tracker/useWorklogStorage.ts | 66 +++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 components/applications/worklog-tracker/useWorklogAuth.ts create mode 100644 components/applications/worklog-tracker/useWorklogList.ts create mode 100644 components/applications/worklog-tracker/useWorklogStorage.ts diff --git a/components/applications/worklog-tracker/useWorklogAuth.ts b/components/applications/worklog-tracker/useWorklogAuth.ts new file mode 100644 index 0000000..135e087 --- /dev/null +++ b/components/applications/worklog-tracker/useWorklogAuth.ts @@ -0,0 +1,41 @@ +import { ref } from 'vue' + +import { useCookie } from '#app' + +export function useWorklogAuth() { + const authCookie = useCookie('authenticated') + const authenticated = ref(Boolean(authCookie.value)) + + async function login(email: string, password: string) { + const response = await fetch('/api/worklog-tracker/worklogs/auth', { + method: 'POST', + body: JSON.stringify({ + email: email, + password: password + }), + headers: { + 'Content-Type': 'application/json' + } + }) + + if (response.status !== 204) { + throw new Error('Failed to authenticate') + } + + authenticated.value = true + } + + async function logout() { + await fetch('/api/worklog-tracker/worklogs/auth', { + method: 'DELETE' + }) + + authenticated.value = false + } + + return { + authenticated, + login, + logout + } +} diff --git a/components/applications/worklog-tracker/useWorklogList.ts b/components/applications/worklog-tracker/useWorklogList.ts new file mode 100644 index 0000000..e6a2f67 --- /dev/null +++ b/components/applications/worklog-tracker/useWorklogList.ts @@ -0,0 +1,39 @@ +import { shallowReactive } from 'vue' + +import type { WorklogItem } from '~/composables/server/worklog-tracker/types/worklogItem' + +export function useWorklogList() { + const value = shallowReactive([]) + + function add(item: WorklogItem) { + value.push(item) + } + + function update(item: WorklogItem) { + const index = value.findIndex(x => x.id === item.id) + + if (index > -1) { + value[index] = item + } + } + + function remove(item: WorklogItem) { + const index = value.indexOf(item) + + if (index > -1) { + value.splice(index, 1) + } + } + + function load(items: WorklogItem[]) { + value.splice(0, value.length, ...items) + } + + return { + value, + add, + update, + load, + remove + } +} diff --git a/components/applications/worklog-tracker/useWorklogStorage.ts b/components/applications/worklog-tracker/useWorklogStorage.ts new file mode 100644 index 0000000..2abae16 --- /dev/null +++ b/components/applications/worklog-tracker/useWorklogStorage.ts @@ -0,0 +1,66 @@ +import { WorklogItem } from '~/composables/server/worklog-tracker/types/worklogItem' + +const url = '/api/worklog-tracker/worklogs' + +function transformWorklogItem(worklogItem: WorklogItem): WorklogItem { + // TODO: Refactor this. It shouldn't be necessary to transform the date. + return new WorklogItem( + worklogItem.ticket, + worklogItem.content, + new Date(worklogItem.startTime), + new Date(worklogItem.endTime), + worklogItem.id, + worklogItem.issueId + ) +} + +export function useWorklogStorage() { + async function load(): Promise { + const response = await fetch(url) + const items = await response.json() as WorklogItem[] + + return items.map(transformWorklogItem) + } + + async function save(worklogItem: WorklogItem): Promise { + const response = await fetch(url, { + method: 'POST', + body: JSON.stringify(worklogItem), + headers: { + 'Content-Type': 'application/json' + } + }) + + const result = await response.json() + + return transformWorklogItem(result) + } + + async function remove(worklogItem: WorklogItem): Promise { + const query = new URLSearchParams({ + issueId: worklogItem.issueId, + worklogId: worklogItem.id + }) + + await fetch(`${url}?${query.toString()}`, { + method: 'DELETE' + }) + } + + async function update(worklogItem: WorklogItem): Promise { + await fetch(url, { + method: 'PUT', + body: JSON.stringify(worklogItem), + headers: { + 'Content-Type': 'application/json' + } + }) + } + + return { + load, + save, + remove, + update + } +} From ccf3da7bc287747ee527ae242d8b16c31d426255 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sun, 1 Sep 2024 21:44:10 -0300 Subject: [PATCH 03/19] Introduce the `WlWorklogAuthForm` component --- .../worklog-tracker/WlWorklogAuthForm.vue | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 components/applications/worklog-tracker/WlWorklogAuthForm.vue diff --git a/components/applications/worklog-tracker/WlWorklogAuthForm.vue b/components/applications/worklog-tracker/WlWorklogAuthForm.vue new file mode 100644 index 0000000..4dbf6c8 --- /dev/null +++ b/components/applications/worklog-tracker/WlWorklogAuthForm.vue @@ -0,0 +1,34 @@ + + + From 4f41bf4540942620a6e707b2f67307c9aba45201 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sun, 1 Sep 2024 21:44:23 -0300 Subject: [PATCH 04/19] Introduce the `WlWorklogDetailsForm` component --- .../worklog-tracker/WlWorklogDetailsForm.vue | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 components/applications/worklog-tracker/WlWorklogDetailsForm.vue diff --git a/components/applications/worklog-tracker/WlWorklogDetailsForm.vue b/components/applications/worklog-tracker/WlWorklogDetailsForm.vue new file mode 100644 index 0000000..36cdc4b --- /dev/null +++ b/components/applications/worklog-tracker/WlWorklogDetailsForm.vue @@ -0,0 +1,119 @@ +