From c1e9784b4a07a52dc5e233a7897701c49431031c Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 3 Oct 2024 13:43:13 +0200 Subject: [PATCH 01/15] protocol base views --- package-lock.json | 156 +++++++++++++++++- package.json | 1 + .../admin/club/protocol/ProtocolListItem.vue | 27 +++ src/globalProperties.config.ts | 4 - src/router/index.ts | 38 ++++- src/router/protocolGuard.ts | 16 ++ src/stores/admin/protocol.ts | 80 +++++++++ src/types/permissionTypes.ts | 6 +- src/viewmodels/admin/protocol.models.ts | 16 ++ src/views/Login.vue | 10 +- src/views/admin/protocol/Protocol.vue | 151 +++++++++++++++++ src/views/admin/protocol/ProtocolEdit.vue | 111 +++++++++++++ src/views/admin/protocol/ProtocolOverview.vue | 34 ++++ src/views/admin/protocol/ProtocolRouting.vue | 82 +++++++++ 14 files changed, 708 insertions(+), 24 deletions(-) create mode 100644 src/components/admin/club/protocol/ProtocolListItem.vue create mode 100644 src/router/protocolGuard.ts create mode 100644 src/stores/admin/protocol.ts create mode 100644 src/viewmodels/admin/protocol.models.ts create mode 100644 src/views/admin/protocol/Protocol.vue create mode 100644 src/views/admin/protocol/ProtocolEdit.vue create mode 100644 src/views/admin/protocol/ProtocolOverview.vue create mode 100644 src/views/admin/protocol/ProtocolRouting.vue diff --git a/package-lock.json b/package-lock.json index 1d9dc45..3a50f6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@headlessui/vue": "^1.7.13", "@heroicons/vue": "^2.1.5", - "@types/lodash.isequal": "^4.5.8", + "@vueup/vue-quill": "^1.2.0", "axios": "^0.26.1", "jwt-decode": "^4.0.0", "lodash.clonedeep": "^4.5.0", @@ -31,6 +31,7 @@ "@tsconfig/node20": "^20.1.4", "@types/eslint": "~9.6.0", "@types/lodash.clonedeep": "^4.5.9", + "@types/lodash.isequal": "^4.5.8", "@types/node": "^20.14.5", "@types/nprogress": "^0.2.0", "@types/qrcode": "^1.5.5", @@ -3070,6 +3071,7 @@ "version": "4.17.7", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "dev": true, "license": "MIT" }, "node_modules/@types/lodash.clonedeep": { @@ -3086,6 +3088,7 @@ "version": "4.5.8", "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.8.tgz", "integrity": "sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==", + "dev": true, "license": "MIT", "dependencies": { "@types/lodash": "*" @@ -3680,6 +3683,19 @@ "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==", "dev": true }, + "node_modules/@vueup/vue-quill": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vueup/vue-quill/-/vue-quill-1.2.0.tgz", + "integrity": "sha512-kd5QPSHMDpycklojPXno2Kw2JSiKMYduKYQckTm1RJoVDA557MnyUXgcuuDpry4HY/Rny9nGNcK+m3AHk94wag==", + "license": "MIT", + "dependencies": { + "quill": "^1.3.7", + "quill-delta": "^4.2.2" + }, + "peerDependencies": { + "vue": "^3.2.41" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -4311,6 +4327,15 @@ "wrap-ansi": "^6.2.0" } }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -4589,6 +4614,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "license": "MIT", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -4673,7 +4718,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -5237,6 +5281,12 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==", + "license": "MIT" + }, "node_modules/execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", @@ -5269,6 +5319,12 @@ "node": ">=6" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5550,7 +5606,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5815,7 +5870,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -5976,6 +6030,22 @@ "node": ">= 0.4" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -6099,7 +6169,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -6236,7 +6305,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -6996,11 +7064,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -7126,6 +7209,12 @@ "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", "dev": true }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", + "license": "BSD-3-Clause" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7669,6 +7758,57 @@ "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", "dev": true }, + "node_modules/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "license": "BSD-3-Clause", + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/quill-delta": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-4.2.2.tgz", + "integrity": "sha512-qjbn82b/yJzOjstBgkhtBjN2TNK+ZHP/BgUQO+j6bRhWQQdmj2lH6hXG7+nwwLF41Xgn//7/83lxs9n2BkTtTg==", + "license": "MIT", + "dependencies": { + "fast-diff": "1.2.0", + "lodash.clonedeep": "^4.5.0", + "lodash.isequal": "^4.5.0" + } + }, + "node_modules/quill-delta/node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "license": "Apache-2.0" + }, + "node_modules/quill/node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", + "license": "Apache-2.0" + }, + "node_modules/quill/node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "license": "MIT", + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -7787,7 +7927,6 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, "dependencies": { "call-bind": "^1.0.6", "define-properties": "^1.2.1", @@ -8090,7 +8229,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", diff --git a/package.json b/package.json index 275162d..ea69780 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "dependencies": { "@headlessui/vue": "^1.7.13", "@heroicons/vue": "^2.1.5", + "@vueup/vue-quill": "^1.2.0", "axios": "^0.26.1", "jwt-decode": "^4.0.0", "lodash.clonedeep": "^4.5.0", diff --git a/src/components/admin/club/protocol/ProtocolListItem.vue b/src/components/admin/club/protocol/ProtocolListItem.vue new file mode 100644 index 0000000..6cf34e3 --- /dev/null +++ b/src/components/admin/club/protocol/ProtocolListItem.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/src/globalProperties.config.ts b/src/globalProperties.config.ts index 73264a1..b9d36eb 100644 --- a/src/globalProperties.config.ts +++ b/src/globalProperties.config.ts @@ -1,12 +1,8 @@ -import type { AxiosInstance } from "axios"; -import type { NProgress } from "nprogress"; import type { RouteLocationNormalizedLoaded, Router } from "vue-router"; declare module "@vue/runtime-core" { interface ComponentCustomProperties { $dev: boolean; - $http: AxiosInstance; - $progress: NProgress; $router: Router; $route: RouteLocationNormalizedLoaded; } diff --git a/src/router/index.ts b/src/router/index.ts index 25dbd9d..ba48a6d 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -7,6 +7,7 @@ import { isSetup } from "./setupGuard"; import { abilityAndNavUpdate } from "./adminGuard"; import type { PermissionType, PermissionSection, PermissionModule } from "@/types/permissionTypes"; import { resetMemberStores, setMemberId } from "./memberGuard"; +import { resetProtocolStores, setProtocolId } from "./protocolGuard"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), @@ -148,10 +149,41 @@ const router = createRouter({ }, { path: "protocol", - name: "admin-club-protocol", - component: () => import("@/views/admin/members/Overview.vue"), - meta: { type: "read", section: "club", module: "protocoll" }, + name: "admin-club-protocol-route", + component: () => import("@/views/RouterView.vue"), + meta: { type: "read", section: "club", module: "protocol" }, beforeEnter: [abilityAndNavUpdate], + children: [ + { + path: "", + name: "admin-club-protocol", + component: () => import("@/views/admin/protocol/Protocol.vue"), + beforeEnter: [resetProtocolStores], + }, + { + path: ":protocolId", + name: "admin-club-protocol-routing", + component: () => import("@/views/admin/protocol/ProtocolRouting.vue"), + beforeEnter: [setProtocolId], + props: true, + children: [ + { + path: "overview", + name: "admin-club-protocol-overview", + component: () => import("@/views/admin/protocol/ProtocolOverview.vue"), + props: true, + }, + { + path: "edit", + name: "admin-club-protocol-edit", + component: () => import("@/views/admin/protocol/ProtocolEdit.vue"), + meta: { type: "update", section: "club", module: "member" }, + beforeEnter: [abilityAndNavUpdate], + props: true, + }, + ], + }, + ], }, ], }, diff --git a/src/router/protocolGuard.ts b/src/router/protocolGuard.ts new file mode 100644 index 0000000..6d80fe1 --- /dev/null +++ b/src/router/protocolGuard.ts @@ -0,0 +1,16 @@ +import { useProtocolStore } from "@/stores/admin/protocol"; + +export async function setProtocolId(to: any, from: any, next: any) { + const protocol = useProtocolStore(); + protocol.activeProtocol = to.params?.protocolId ?? null; + + next(); +} + +export async function resetProtocolStores(to: any, from: any, next: any) { + const protocol = useProtocolStore(); + protocol.activeProtocol = null; + protocol.activeProtocolObj = null; + + next(); +} diff --git a/src/stores/admin/protocol.ts b/src/stores/admin/protocol.ts new file mode 100644 index 0000000..1d19bf3 --- /dev/null +++ b/src/stores/admin/protocol.ts @@ -0,0 +1,80 @@ +import { defineStore } from "pinia"; +import type { CreateProtocolViewModel, UpdateProtocolViewModel } from "@/viewmodels/admin/protocol.models"; +import { http } from "@/serverCom"; +import type { AxiosResponse } from "axios"; +import type { ProtocolViewModel } from "@/viewmodels/admin/protocol.models"; + +export const useProtocolStore = defineStore("protocol", { + state: () => { + return { + protocols: [] as Array, + totalCount: 0 as number, + loading: "loading" as "loading" | "fetched" | "failed", + activeProtocol: null as number | null, + activeProtocolObj: null as ProtocolViewModel | null, + loadingActive: "loading" as "loading" | "fetched" | "failed", + }; + }, + actions: { + fetchProtocols(offset = 0, count = 25, clear = false) { + if (clear) this.protocols = []; + this.loading = "loading"; + http + .get(`/admin/protocol?offset=${offset}&count=${count}`) + .then((result) => { + this.totalCount = result.data.total; + result.data.protocols + .filter((elem: ProtocolViewModel) => this.protocols.findIndex((m) => m.id == elem.id) == -1) + .map((elem: ProtocolViewModel, index: number): ProtocolViewModel & { tab_pos: number } => { + return { + ...elem, + tab_pos: index + offset, + }; + }) + .forEach((elem: ProtocolViewModel & { tab_pos: number }) => { + this.protocols.push(elem); + }); + this.loading = "fetched"; + }) + .catch((err) => { + this.loading = "failed"; + }); + }, + fetchProtocolByActiveId() { + this.loadingActive = "loading"; + http + .get(`/admin/protocol/${this.activeProtocol}`) + .then((res) => { + this.activeProtocolObj = res.data; + this.loadingActive = "fetched"; + }) + .catch((err) => { + this.loadingActive = "failed"; + }); + }, + fetchProtocolById(id: number) { + return http.get(`/admin/protocol/${id}`); + }, + async createProtocol(protocol: CreateProtocolViewModel): Promise> { + const result = await http.post(`/admin/protocol`, { + title: protocol.title, + date: protocol.date, + }); + this.fetchProtocols(); + return result; + }, + async updateActiveProtocol(protocol: UpdateProtocolViewModel): Promise> { + const result = await http.patch(`/admin/protocol/${protocol.id}`, { + title: protocol.title, + date: protocol.date, + }); + this.fetchProtocols(); + return result; + }, + async deleteProtocol(protocol: number): Promise> { + const result = await http.delete(`/admin/protocol/${protocol}`); + this.fetchProtocols(); + return result; + }, + }, +}); diff --git a/src/types/permissionTypes.ts b/src/types/permissionTypes.ts index 3bd54c8..cb8a989 100644 --- a/src/types/permissionTypes.ts +++ b/src/types/permissionTypes.ts @@ -4,7 +4,7 @@ export type PermissionModule = | "member" | "calendar" | "newsletter" - | "protocoll" + | "protocol" | "qualification" | "award" | "executive_position" @@ -39,7 +39,7 @@ export const permissionModules: Array = [ "member", "calendar", "newsletter", - "protocoll", + "protocol", "qualification", "award", "executive_position", @@ -50,7 +50,7 @@ export const permissionModules: Array = [ ]; export const permissionTypes: Array = ["read", "create", "update", "delete"]; export const sectionsAndModules: SectionsAndModulesObject = { - club: ["member", "calendar", "newsletter", "protocoll"], + club: ["member", "calendar", "newsletter", "protocol"], settings: ["qualification", "award", "executive_position", "communication", "membership_status"], user: ["user", "role"], }; diff --git a/src/viewmodels/admin/protocol.models.ts b/src/viewmodels/admin/protocol.models.ts new file mode 100644 index 0000000..95b6272 --- /dev/null +++ b/src/viewmodels/admin/protocol.models.ts @@ -0,0 +1,16 @@ +export interface ProtocolViewModel { + id: number; + title: string; + date: Date; +} + +export interface CreateProtocolViewModel { + title: string; + date: Date; +} + +export interface UpdateProtocolViewModel { + id: number; + title: string; + date: Date; +} diff --git a/src/views/Login.vue b/src/views/Login.vue index b1751b2..bb72d7e 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -69,11 +69,11 @@ export default defineComponent({ }) .then((result) => { this.loginStatus = "success"; - localStorage.setItem("accessToken", result.data.accessToken), - localStorage.setItem("refreshToken", result.data.refreshToken), - setTimeout(() => { - this.$router.push(`/admin`); - }, 1000); + localStorage.setItem("accessToken", result.data.accessToken); + localStorage.setItem("refreshToken", result.data.refreshToken); + setTimeout(() => { + this.$router.push(`/admin`); + }, 1000); }) .catch((err) => { this.loginStatus = "failed"; diff --git a/src/views/admin/protocol/Protocol.vue b/src/views/admin/protocol/Protocol.vue new file mode 100644 index 0000000..c21d9a2 --- /dev/null +++ b/src/views/admin/protocol/Protocol.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/src/views/admin/protocol/ProtocolEdit.vue b/src/views/admin/protocol/ProtocolEdit.vue new file mode 100644 index 0000000..f78c4cd --- /dev/null +++ b/src/views/admin/protocol/ProtocolEdit.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/src/views/admin/protocol/ProtocolOverview.vue b/src/views/admin/protocol/ProtocolOverview.vue new file mode 100644 index 0000000..f0fda79 --- /dev/null +++ b/src/views/admin/protocol/ProtocolOverview.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/src/views/admin/protocol/ProtocolRouting.vue b/src/views/admin/protocol/ProtocolRouting.vue new file mode 100644 index 0000000..46d971b --- /dev/null +++ b/src/views/admin/protocol/ProtocolRouting.vue @@ -0,0 +1,82 @@ + + + + + From e70e6644a667f5a5787dec2099707391340b38af Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 4 Oct 2024 12:47:04 +0200 Subject: [PATCH 02/15] basic routing and info display --- src/helpers/quillConfig.ts | 8 ++ src/router/authGuards.ts | 2 +- src/router/index.ts | 26 +++- src/stores/admin/protocol.ts | 1 + src/viewmodels/admin/protocol.models.ts | 9 ++ .../admin/protocol/ProtocolDecisions.vue | 29 +++++ src/views/admin/protocol/ProtocolEdit.vue | 111 ------------------ src/views/admin/protocol/ProtocolOverview.vue | 38 +++++- src/views/admin/protocol/ProtocolPrecense.vue | 29 +++++ src/views/admin/protocol/ProtocolProtocol.vue | 29 +++++ src/views/admin/protocol/ProtocolRouting.vue | 25 +++- src/views/admin/protocol/ProtocolVoting.vue | 29 +++++ 12 files changed, 211 insertions(+), 125 deletions(-) create mode 100644 src/helpers/quillConfig.ts create mode 100644 src/views/admin/protocol/ProtocolDecisions.vue delete mode 100644 src/views/admin/protocol/ProtocolEdit.vue create mode 100644 src/views/admin/protocol/ProtocolPrecense.vue create mode 100644 src/views/admin/protocol/ProtocolProtocol.vue create mode 100644 src/views/admin/protocol/ProtocolVoting.vue diff --git a/src/helpers/quillConfig.ts b/src/helpers/quillConfig.ts new file mode 100644 index 0000000..4761b98 --- /dev/null +++ b/src/helpers/quillConfig.ts @@ -0,0 +1,8 @@ +export const toolbarOptions = [ + [{ header: [1, 2, false] }, { font: [] }], + [{ header: 1 }, { header: 2 }], + ["bold", "italic", "underline", "strike"], + ["blockquote", "code-block", "link"], + [{ list: "ordered" }, { list: "bullet" }, { list: "check" }], + ["clean"], +]; diff --git a/src/router/authGuards.ts b/src/router/authGuards.ts index 5e7ece7..cbedad9 100644 --- a/src/router/authGuards.ts +++ b/src/router/authGuards.ts @@ -68,7 +68,7 @@ export async function isAuthenticatedPromise(): Promise { var { firstname, lastname, mail, username, permissions } = decoded; - if (Object.keys(permissions).length === 0) { + if (Object.keys(permissions ?? {}).length === 0) { auth.setFailed(); reject("nopermissions"); } diff --git a/src/router/index.ts b/src/router/index.ts index ba48a6d..f281ee0 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -174,11 +174,27 @@ const router = createRouter({ props: true, }, { - path: "edit", - name: "admin-club-protocol-edit", - component: () => import("@/views/admin/protocol/ProtocolEdit.vue"), - meta: { type: "update", section: "club", module: "member" }, - beforeEnter: [abilityAndNavUpdate], + path: "precense", + name: "admin-club-protocol-precense", + component: () => import("@/views/admin/protocol/ProtocolPrecense.vue"), + props: true, + }, + { + path: "voting", + name: "admin-club-protocol-voting", + component: () => import("@/views/admin/protocol/ProtocolVoting.vue"), + props: true, + }, + { + path: "decisions", + name: "admin-club-protocol-decisions", + component: () => import("@/views/admin/protocol/ProtocolDecisions.vue"), + props: true, + }, + { + path: "protocol", + name: "admin-club-protocol-protocol", + component: () => import("@/views/admin/protocol/ProtocolProtocol.vue"), props: true, }, ], diff --git a/src/stores/admin/protocol.ts b/src/stores/admin/protocol.ts index 1d19bf3..3dd3b13 100644 --- a/src/stores/admin/protocol.ts +++ b/src/stores/admin/protocol.ts @@ -13,6 +13,7 @@ export const useProtocolStore = defineStore("protocol", { activeProtocol: null as number | null, activeProtocolObj: null as ProtocolViewModel | null, loadingActive: "loading" as "loading" | "fetched" | "failed", + syncing: "synced" as "synced" | "syncing" | "detectedChanges" | "failed", }; }, actions: { diff --git a/src/viewmodels/admin/protocol.models.ts b/src/viewmodels/admin/protocol.models.ts index 95b6272..a30fb3c 100644 --- a/src/viewmodels/admin/protocol.models.ts +++ b/src/viewmodels/admin/protocol.models.ts @@ -2,15 +2,24 @@ export interface ProtocolViewModel { id: number; title: string; date: Date; + starttime: Date; + endtime: Date; + summary: string; } export interface CreateProtocolViewModel { title: string; date: Date; + starttime: Date; + endtime: Date; + summary: string; } export interface UpdateProtocolViewModel { id: number; title: string; date: Date; + starttime: Date; + endtime: Date; + summary: string; } diff --git a/src/views/admin/protocol/ProtocolDecisions.vue b/src/views/admin/protocol/ProtocolDecisions.vue new file mode 100644 index 0000000..8bd9e6d --- /dev/null +++ b/src/views/admin/protocol/ProtocolDecisions.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/src/views/admin/protocol/ProtocolEdit.vue b/src/views/admin/protocol/ProtocolEdit.vue deleted file mode 100644 index f78c4cd..0000000 --- a/src/views/admin/protocol/ProtocolEdit.vue +++ /dev/null @@ -1,111 +0,0 @@ - - - - - diff --git a/src/views/admin/protocol/ProtocolOverview.vue b/src/views/admin/protocol/ProtocolOverview.vue index f0fda79..24f8d01 100644 --- a/src/views/admin/protocol/ProtocolOverview.vue +++ b/src/views/admin/protocol/ProtocolOverview.vue @@ -1,6 +1,37 @@ @@ -48,7 +49,13 @@ import { mapActions, mapState } from "pinia"; import MainTemplate from "@/templates/Main.vue"; import { RouterLink, RouterView } from "vue-router"; import { useProtocolStore } from "@/stores/admin/protocol"; -import { PencilIcon, TrashIcon } from "@heroicons/vue/24/outline"; +import { + ArrowPathIcon, + CloudArrowUpIcon, + CloudIcon, + ExclamationTriangleIcon, + TrashIcon, +} from "@heroicons/vue/24/outline"; import { useModalStore } from "@/stores/modal"; @@ -59,11 +66,17 @@ export default defineComponent({ }, data() { return { - tabs: [{ route: "admin-club-protocol-overview", title: "Übersicht" }], + tabs: [ + { route: "admin-club-protocol-overview", title: "Übersicht" }, + { route: "admin-club-protocol-precense", title: "Anwesenheit" }, + { route: "admin-club-protocol-voting", title: "Abstimmungen" }, + { route: "admin-club-protocol-decisions", title: "Entscheidungen" }, + { route: "admin-club-protocol-protocol", title: "Protokoll" }, + ], }; }, computed: { - ...mapState(useProtocolStore, ["activeProtocolObj"]), + ...mapState(useProtocolStore, ["activeProtocolObj", "syncing"]), }, mounted() { this.fetchProtocolByActiveId(); diff --git a/src/views/admin/protocol/ProtocolVoting.vue b/src/views/admin/protocol/ProtocolVoting.vue new file mode 100644 index 0000000..8bd9e6d --- /dev/null +++ b/src/views/admin/protocol/ProtocolVoting.vue @@ -0,0 +1,29 @@ + + + + + From 562f6ab1f27a9329295c5ee3f4b86049a5ecaf42 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sun, 6 Oct 2024 11:38:07 +0200 Subject: [PATCH 03/15] precense --- src/main.css | 4 +- src/views/admin/protocol/ProtocolPrecense.vue | 129 +++++++++++++++++- 2 files changed, 126 insertions(+), 7 deletions(-) diff --git a/src/main.css b/src/main.css index 3d54f9b..7526df5 100644 --- a/src/main.css +++ b/src/main.css @@ -56,8 +56,8 @@ body { @apply w-full h-full overflow-hidden flex flex-col; } -button:not([headlessui]), -a[button]:not([headlessui]) { +button:not([headlessui]):not([id*="headlessui"]):not([class*="headlessui"]), +a[button]:not([headlessui]):not([id*="headlessui"]):not([class*="headlessui"]) { @apply relative box-border h-10 w-full flex justify-center py-2 px-4 text-sm font-medium rounded-md focus:outline-none focus:ring-0; } diff --git a/src/views/admin/protocol/ProtocolPrecense.vue b/src/views/admin/protocol/ProtocolPrecense.vue index 8bd9e6d..092fc93 100644 --- a/src/views/admin/protocol/ProtocolPrecense.vue +++ b/src/views/admin/protocol/ProtocolPrecense.vue @@ -2,6 +2,79 @@

↺ laden fehlgeschlagen

+
+ +
+ Anwesende suchen +
+ + + +
+ + + +
  • + Keine Auswahl +
  • +
    + + +
  • + + {{ member.firstname }} {{ member.lastname }} {{ member.nameaffix }} + + + +
  • +
    +
    +
    +
    +
    +
    +
    +

    Ausgewählte Anwesende

    +
    +
    +

    {{ member.lastname }}, {{ member.firstname }} {{ member.nameaffix ? `- ${member.nameaffix}` : "" }}

    + +
    +
    @@ -9,10 +82,20 @@ import { defineComponent } from "vue"; import { mapActions, mapState } from "pinia"; import Spinner from "@/components/Spinner.vue"; +import { + Combobox, + ComboboxLabel, + ComboboxInput, + ComboboxButton, + ComboboxOptions, + ComboboxOption, + TransitionRoot, +} from "@headlessui/vue"; +import { CheckIcon, ChevronUpDownIcon } from "@heroicons/vue/20/solid"; +import { TrashIcon } from "@heroicons/vue/24/outline"; import { useProtocolStore } from "@/stores/admin/protocol"; -import { QuillEditor } from "@vueup/vue-quill"; -import "@vueup/vue-quill/dist/vue-quill.snow.css"; -import { toolbarOptions } from "@/helpers/quillConfig"; +import { useMemberStore } from "@/stores/admin/member"; +import type { MemberViewModel } from "@/viewmodels/admin/member.models"; From 0d559c9365887b2e9769be8e90010a296f4e4609 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 8 Oct 2024 10:43:16 +0200 Subject: [PATCH 04/15] decisions --- package-lock.json | 7 +++++ package.json | 1 + src/globalProperties.config.ts | 2 ++ src/main.css | 31 +++++++++++++++++-- src/router/index.ts | 14 ++++----- src/views/Login.vue | 10 +++++- .../admin/{ => club}/protocol/Protocol.vue | 0 .../{ => club}/protocol/ProtocolDecisions.vue | 0 .../{ => club}/protocol/ProtocolOverview.vue | 0 .../{ => club}/protocol/ProtocolPrecense.vue | 0 .../{ => club}/protocol/ProtocolProtocol.vue | 22 +++++++++++++ .../{ => club}/protocol/ProtocolRouting.vue | 2 +- .../{ => club}/protocol/ProtocolVoting.vue | 0 13 files changed, 78 insertions(+), 11 deletions(-) rename src/views/admin/{ => club}/protocol/Protocol.vue (100%) rename src/views/admin/{ => club}/protocol/ProtocolDecisions.vue (100%) rename src/views/admin/{ => club}/protocol/ProtocolOverview.vue (100%) rename src/views/admin/{ => club}/protocol/ProtocolPrecense.vue (100%) rename src/views/admin/{ => club}/protocol/ProtocolProtocol.vue (50%) rename src/views/admin/{ => club}/protocol/ProtocolRouting.vue (97%) rename src/views/admin/{ => club}/protocol/ProtocolVoting.vue (100%) diff --git a/package-lock.json b/package-lock.json index 3a50f6e..654c907 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "axios": "^0.26.1", "jwt-decode": "^4.0.0", "lodash.clonedeep": "^4.5.0", + "lodash.difference": "^4.5.0", "lodash.isequal": "^4.5.0", "nprogress": "^0.2.0", "pdf-dist": "^1.0.0", @@ -6702,6 +6703,12 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "license": "MIT" + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", diff --git a/package.json b/package.json index ea69780..6b17df4 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "axios": "^0.26.1", "jwt-decode": "^4.0.0", "lodash.clonedeep": "^4.5.0", + "lodash.difference": "^4.5.0", "lodash.isequal": "^4.5.0", "nprogress": "^0.2.0", "pdf-dist": "^1.0.0", diff --git a/src/globalProperties.config.ts b/src/globalProperties.config.ts index b9d36eb..89d63ce 100644 --- a/src/globalProperties.config.ts +++ b/src/globalProperties.config.ts @@ -1,8 +1,10 @@ +import type { AxiosInstance } from "axios"; import type { RouteLocationNormalizedLoaded, Router } from "vue-router"; declare module "@vue/runtime-core" { interface ComponentCustomProperties { $dev: boolean; + $http: AxiosInstance; $router: Router; $route: RouteLocationNormalizedLoaded; } diff --git a/src/main.css b/src/main.css index 7526df5..b4c501c 100644 --- a/src/main.css +++ b/src/main.css @@ -56,8 +56,8 @@ body { @apply w-full h-full overflow-hidden flex flex-col; } -button:not([headlessui]):not([id*="headlessui"]):not([class*="headlessui"]), -a[button]:not([headlessui]):not([id*="headlessui"]):not([class*="headlessui"]) { +button:not([headlessui]):not([id*="headlessui"]):not([class*="headlessui"]):not([class*="ql"] *), +a[button] { @apply relative box-border h-10 w-full flex justify-center py-2 px-4 text-sm font-medium rounded-md focus:outline-none focus:ring-0; } @@ -91,3 +91,30 @@ input[disabled], textarea[disabled] { @apply opacity-75 pointer-events-none; } + +details { + user-select: none; + & summary svg { + transform: rotate(90deg); + } +} +details[open] { + & summary svg { + transform: rotate(-90deg); + } +} + +details[open] summary ~ * { + animation: ease-opacity-t-b 0.5s ease; +} + +summary { + cursor: pointer; +} +summary > svg { + transition: all 0.3s; +} + +summary::-webkit-details-marker { + display: none; +} diff --git a/src/router/index.ts b/src/router/index.ts index f281ee0..d956e4f 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -157,44 +157,44 @@ const router = createRouter({ { path: "", name: "admin-club-protocol", - component: () => import("@/views/admin/protocol/Protocol.vue"), + component: () => import("@/views/admin/club/protocol/Protocol.vue"), beforeEnter: [resetProtocolStores], }, { path: ":protocolId", name: "admin-club-protocol-routing", - component: () => import("@/views/admin/protocol/ProtocolRouting.vue"), + component: () => import("@/views/admin/club/protocol/ProtocolRouting.vue"), beforeEnter: [setProtocolId], props: true, children: [ { path: "overview", name: "admin-club-protocol-overview", - component: () => import("@/views/admin/protocol/ProtocolOverview.vue"), + component: () => import("@/views/admin/club/protocol/ProtocolOverview.vue"), props: true, }, { path: "precense", name: "admin-club-protocol-precense", - component: () => import("@/views/admin/protocol/ProtocolPrecense.vue"), + component: () => import("@/views/admin/club/protocol/ProtocolPrecense.vue"), props: true, }, { path: "voting", name: "admin-club-protocol-voting", - component: () => import("@/views/admin/protocol/ProtocolVoting.vue"), + component: () => import("@/views/admin/club/protocol/ProtocolVoting.vue"), props: true, }, { path: "decisions", name: "admin-club-protocol-decisions", - component: () => import("@/views/admin/protocol/ProtocolDecisions.vue"), + component: () => import("@/views/admin/club/protocol/ProtocolDecisions.vue"), props: true, }, { path: "protocol", name: "admin-club-protocol-protocol", - component: () => import("@/views/admin/protocol/ProtocolProtocol.vue"), + component: () => import("@/views/admin/club/protocol/ProtocolProtocol.vue"), props: true, }, ], diff --git a/src/views/Login.vue b/src/views/Login.vue index bb72d7e..475b76d 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -12,7 +12,15 @@
    - +
    diff --git a/src/views/admin/protocol/Protocol.vue b/src/views/admin/club/protocol/Protocol.vue similarity index 100% rename from src/views/admin/protocol/Protocol.vue rename to src/views/admin/club/protocol/Protocol.vue diff --git a/src/views/admin/protocol/ProtocolDecisions.vue b/src/views/admin/club/protocol/ProtocolDecisions.vue similarity index 100% rename from src/views/admin/protocol/ProtocolDecisions.vue rename to src/views/admin/club/protocol/ProtocolDecisions.vue diff --git a/src/views/admin/protocol/ProtocolOverview.vue b/src/views/admin/club/protocol/ProtocolOverview.vue similarity index 100% rename from src/views/admin/protocol/ProtocolOverview.vue rename to src/views/admin/club/protocol/ProtocolOverview.vue diff --git a/src/views/admin/protocol/ProtocolPrecense.vue b/src/views/admin/club/protocol/ProtocolPrecense.vue similarity index 100% rename from src/views/admin/protocol/ProtocolPrecense.vue rename to src/views/admin/club/protocol/ProtocolPrecense.vue diff --git a/src/views/admin/protocol/ProtocolProtocol.vue b/src/views/admin/club/protocol/ProtocolProtocol.vue similarity index 50% rename from src/views/admin/protocol/ProtocolProtocol.vue rename to src/views/admin/club/protocol/ProtocolProtocol.vue index 8bd9e6d..354862d 100644 --- a/src/views/admin/protocol/ProtocolProtocol.vue +++ b/src/views/admin/club/protocol/ProtocolProtocol.vue @@ -2,6 +2,28 @@

    ↺ laden fehlgeschlagen

    + +
    + + + + + + + + +
    diff --git a/src/views/admin/protocol/ProtocolRouting.vue b/src/views/admin/club/protocol/ProtocolRouting.vue similarity index 97% rename from src/views/admin/protocol/ProtocolRouting.vue rename to src/views/admin/club/protocol/ProtocolRouting.vue index 1d14a30..fe56087 100644 --- a/src/views/admin/protocol/ProtocolRouting.vue +++ b/src/views/admin/club/protocol/ProtocolRouting.vue @@ -70,7 +70,7 @@ export default defineComponent({ { route: "admin-club-protocol-overview", title: "Übersicht" }, { route: "admin-club-protocol-precense", title: "Anwesenheit" }, { route: "admin-club-protocol-voting", title: "Abstimmungen" }, - { route: "admin-club-protocol-decisions", title: "Entscheidungen" }, + { route: "admin-club-protocol-decisions", title: "Beschlüsse" }, { route: "admin-club-protocol-protocol", title: "Protokoll" }, ], }; diff --git a/src/views/admin/protocol/ProtocolVoting.vue b/src/views/admin/club/protocol/ProtocolVoting.vue similarity index 100% rename from src/views/admin/protocol/ProtocolVoting.vue rename to src/views/admin/club/protocol/ProtocolVoting.vue From 60d615010545be5345e3400abf227df1b75859e3 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 8 Oct 2024 10:50:46 +0200 Subject: [PATCH 05/15] input styling --- src/components/Header.vue | 2 +- .../admin/club/protocol/ProtocolPrecense.vue | 22 ++++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/components/Header.vue b/src/components/Header.vue index de1360a..ac72cc8 100644 --- a/src/components/Header.vue +++ b/src/components/Header.vue @@ -1,5 +1,5 @@ @@ -30,7 +85,7 @@ export default defineComponent({ this.fetchProtocolVoting(); }, methods: { - ...mapActions(useProtocolVotingStore, ["fetchProtocolVoting"]), + ...mapActions(useProtocolVotingStore, ["fetchProtocolVoting", "createProtocolVoting"]), }, }); From 225b686de67e92a8b8fef48ad0c098908c112e67 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 18 Oct 2024 15:23:37 +0200 Subject: [PATCH 11/15] trigger protocol render (temporary) --- src/stores/admin/protocol.ts | 6 ++++++ src/views/admin/club/protocol/ProtocolRouting.vue | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/stores/admin/protocol.ts b/src/stores/admin/protocol.ts index 3c637d8..8262291 100644 --- a/src/stores/admin/protocol.ts +++ b/src/stores/admin/protocol.ts @@ -100,5 +100,11 @@ export const useProtocolStore = defineStore("protocol", { }) .catch((err) => {}); }, + printProtocol() { + return http + .post(`/admin/protocol/${this.activeProtocol}/render`) + .then(() => {}) + .catch(() => {}); + }, }, }); diff --git a/src/views/admin/club/protocol/ProtocolRouting.vue b/src/views/admin/club/protocol/ProtocolRouting.vue index b11567f..f5380fd 100644 --- a/src/views/admin/club/protocol/ProtocolRouting.vue +++ b/src/views/admin/club/protocol/ProtocolRouting.vue @@ -6,6 +6,7 @@