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 @@ + + + + +