From 73dc3a1d8c9bfa2a00808e981a825e0eeec76b43 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Wed, 31 Jul 2024 09:18:02 -0400 Subject: [PATCH 01/12] Add `vue-router` --- package-lock.json | 22 +++++++++++++++++++++- package.json | 8 ++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69d1c37..5d7e084 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,8 @@ "name": "arches_references", "license": "AGPL-3.0-only", "dependencies": { - "arches": "archesproject/arches#dev/7.6.x" + "arches": "archesproject/arches#dev/7.6.x", + "vue-router": "^4.4.0" }, "devDependencies": { "arches-dev-dependencies": "archesproject/arches-dev-dependencies#dev/7.6.x" @@ -5546,6 +5547,11 @@ "he": "^1.2.0" } }, + "node_modules/@vue/devtools-api": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.3.tgz", + "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==" + }, "node_modules/@vue/language-core": { "version": "2.0.29", "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.29.tgz", @@ -16715,6 +16721,20 @@ } } }, + "node_modules/vue-router": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.0.tgz", + "integrity": "sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==", + "dependencies": { + "@vue/devtools-api": "^6.5.1" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, "node_modules/vue-template-compiler": { "version": "2.7.16", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", diff --git a/package.json b/package.json index ca939de..1d8b7c8 100644 --- a/package.json +++ b/package.json @@ -18,17 +18,17 @@ "vitest": "vitest --run --coverage" }, "dependencies": { - "arches": "archesproject/arches#dev/7.6.x" + "arches": "archesproject/arches#dev/7.6.x", + "vue-router": "^4.4.0" }, "devDependencies": { "arches-dev-dependencies": "archesproject/arches-dev-dependencies#dev/7.6.x" }, - "nodeModulesPaths": { - }, + "nodeModulesPaths": {}, "overrides": { "moment-timezone": "^0.5.45", "nomnom": "npm:@gerhobbelt/nomnom", - "rimraf": "^5.0.7", + "rimraf": "^5.0.7", "underscore": "^1.13.6" } } From f23b0c32cc096e1105b62fb7ee84d5ba69c9d1ed Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Wed, 31 Jul 2024 09:43:25 -0400 Subject: [PATCH 02/12] Remove trailing slashes from URLs --- arches_references/urls.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/arches_references/urls.py b/arches_references/urls.py index dd56bc6..8d04c44 100644 --- a/arches_references/urls.py +++ b/arches_references/urls.py @@ -13,50 +13,50 @@ ) urlpatterns = [ - path("api/controlled_lists/", ListsView.as_view(), name="controlled_lists"), + path("api/controlled_lists", ListsView.as_view(), name="controlled_lists"), path( - "api/controlled_list//", + "api/controlled_list/", ListView.as_view(), name="controlled_list", ), - path("api/controlled_list/", ListView.as_view(), name="controlled_list_add"), + path("api/controlled_list", ListView.as_view(), name="controlled_list_add"), path( - "api/controlled_list_item//", + "api/controlled_list_item/", ListItemView.as_view(), name="controlled_list_item", ), path( - "api/controlled_list_item/", + "api/controlled_list_item", ListItemView.as_view(), name="controlled_list_item_add", ), path( - "api/controlled_list_item_value//", + "api/controlled_list_item_value/", ListItemValueView.as_view(), name="controlled_list_item_value", ), path( - "api/controlled_list_item_value/", + "api/controlled_list_item_value", ListItemValueView.as_view(), name="controlled_list_item_value_add", ), path( - "api/controlled_list_item_image//", + "api/controlled_list_item_image/", ListItemImageView.as_view(), name="controlled_list_item_image", ), path( - "api/controlled_list_item_image/", + "api/controlled_list_item_image", ListItemImageView.as_view(), name="controlled_list_item_image_add", ), path( - "api/controlled_list_item_image_metadata//", + "api/controlled_list_item_image_metadata/", ListItemImageMetadataView.as_view(), name="controlled_list_item_image_metadata", ), path( - "api/controlled_list_item_image_metadata/", + "api/controlled_list_item_image_metadata", ListItemImageMetadataView.as_view(), name="controlled_list_item_image_metadata_add", ), From c244a60d6e95361c55eb2feb3f5380e5044041c6 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Mon, 12 Aug 2024 13:05:03 -0400 Subject: [PATCH 03/12] Standardize APIBase inheritance --- arches_references/views.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/arches_references/views.py b/arches_references/views.py index 8a2d0ca..5c60bda 100644 --- a/arches_references/views.py +++ b/arches_references/views.py @@ -1,13 +1,12 @@ -import logging from http import HTTPStatus from uuid import UUID from django.core.exceptions import ValidationError from django.db import transaction from django.db.models import Max -from django.views.generic import View from django.utils.decorators import method_decorator from django.utils.translation import gettext as _ +from django.views.generic import View from arches.app.models.utils import field_names from arches.app.utils.betterJSONSerializer import JSONDeserializer @@ -25,8 +24,6 @@ NodeProxy, ) -logger = logging.getLogger(__name__) - def _prefetch_terms(request): """Children at arbitrary depth will still be returned, but tell @@ -90,7 +87,7 @@ def get(self, request): @method_decorator( group_required("RDM Administrator", raise_exception=True), name="dispatch" ) -class ListView(View): +class ListView(APIBase): def get(self, request, list_id): """Returns either a flat representation (?flat=true) or a tree (default).""" try: @@ -176,7 +173,7 @@ def delete(self, request, list_id): @method_decorator( group_required("RDM Administrator", raise_exception=True), name="dispatch" ) -class ListItemView(View): +class ListItemView(APIBase): def post(self, request): data = JSONDeserializer().deserialize(request.body) try: @@ -247,7 +244,7 @@ def delete(self, request, item_id): @method_decorator( group_required("RDM Administrator", raise_exception=True), name="dispatch" ) -class ListItemValueView(View): +class ListItemValueView(APIBase): def post(self, request): data = JSONDeserializer().deserialize(request.body) value = ListItemValue(**data) @@ -301,7 +298,7 @@ def delete(self, request, value_id): @method_decorator( group_required("RDM Administrator", raise_exception=True), name="dispatch" ) -class ListItemImageView(View): +class ListItemImageView(APIBase): def post(self, request): uploaded_file = request.FILES["item_image"] img = ListItemImage( @@ -328,7 +325,7 @@ def delete(self, request, image_id): @method_decorator( group_required("RDM Administrator", raise_exception=True), name="dispatch" ) -class ListItemImageMetadataView(View): +class ListItemImageMetadataView(APIBase): def post(self, request): data = JSONDeserializer().deserialize(request.body) data.pop("metadata_label", None) From 88e6ef9a41eb123c9b0c33ccc2159ec1156cd16b Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Wed, 31 Jul 2024 10:25:39 -0400 Subject: [PATCH 04/12] Implement front-end routing #4 --- CHANGELOG.md | 5 +- .../plugins/controlled-list-manager.js | 15 +++ .../components/ControlledListsMain.vue | 105 ++++++++++++------ .../components/tree/AddDeleteControls.vue | 25 ----- .../components/tree/ListTree.vue | 6 +- .../components/tree/ListTreeControls.vue | 90 ++++++++++++++- .../components/tree/MoveRow.vue | 24 ++-- .../components/tree/TreeRow.vue | 11 +- .../src/arches_references/constants.ts | 6 + .../plugins/ControlledListManager.vue | 14 +-- .../src/arches_references/utils.ts | 24 ++-- 11 files changed, 228 insertions(+), 97 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4ef1c9..6c267dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- Added compatibility with PrimeVue 4 [#16](https://github.com/archesproject/arches-references/pull/16) +- Add compatibility with PrimeVue 4 [#9](https://github.com/archesproject/arches-references/issues/9) +- Add front-end routing [#19](https://github.com/archesproject/arches-references/pull/19) ### Changed -- Generate URIs when not supplied [#17](https://github.com/archesproject/arches-references/pull/17) +- Generate URIs when not supplied [#17](https://github.com/archesproject/arches-references/pull/17) ### Fixed diff --git a/arches_references/media/js/views/components/plugins/controlled-list-manager.js b/arches_references/media/js/views/components/plugins/controlled-list-manager.js index 6ed3916..f6b5c16 100644 --- a/arches_references/media/js/views/components/plugins/controlled-list-manager.js +++ b/arches_references/media/js/views/components/plugins/controlled-list-manager.js @@ -1,12 +1,27 @@ import ko from 'knockout'; import ControlledListManager from '@/arches_references/plugins/ControlledListManager.vue'; +import ControlledListsMain from '@/arches_references/components/ControlledListsMain.vue'; import createVueApplication from 'utils/create-vue-application'; import ControlledListManagerTemplate from 'templates/views/components/plugins/controlled-list-manager.htm'; +import { createRouter, createWebHistory } from 'vue-router'; + +const routes = [ + { path: '/plugins/controlled-list-manager', name: 'splash', component: ControlledListsMain }, + { path: '/plugins/controlled-list-manager/list/:id', name: 'list', component: ControlledListsMain }, + { path: '/plugins/controlled-list-manager/item/:id', name: 'item', component: ControlledListsMain }, +]; + +const router = createRouter({ + history: createWebHistory(), + routes, +}); + ko.components.register('controlled-list-manager', { viewModel: function() { createVueApplication(ControlledListManager).then((vueApp) => { + vueApp.use(router); vueApp.mount('#controlled-list-manager-mounting-point'); }); }, diff --git a/arches_references/src/arches_references/components/ControlledListsMain.vue b/arches_references/src/arches_references/components/ControlledListsMain.vue index 65cc993..be8de0c 100644 --- a/arches_references/src/arches_references/components/ControlledListsMain.vue +++ b/arches_references/src/arches_references/components/ControlledListsMain.vue @@ -1,13 +1,17 @@ diff --git a/arches_references/src/arches_references/plugins/ControlledListManager.vue b/arches_references/src/arches_references/plugins/ControlledListManager.vue index 8302f08..efacaa2 100644 --- a/arches_references/src/arches_references/plugins/ControlledListManager.vue +++ b/arches_references/src/arches_references/plugins/ControlledListManager.vue @@ -1,19 +1,3 @@ - - From d8454568054217d83cbc652354765328fe8360bc Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Mon, 19 Aug 2024 17:40:57 -0400 Subject: [PATCH 11/12] Factor out routes.ts --- .../plugins/controlled-list-manager.js | 8 +----- .../components/ControlledListsMain.vue | 8 +++--- .../components/MainSplitter.vue | 5 ++-- .../components/tree/ListTree.vue | 8 +++--- .../src/arches_references/constants.ts | 6 ----- .../src/arches_references/routes.ts | 25 +++++++++++++++++++ 6 files changed, 37 insertions(+), 23 deletions(-) create mode 100644 arches_references/src/arches_references/routes.ts diff --git a/arches_references/media/js/views/components/plugins/controlled-list-manager.js b/arches_references/media/js/views/components/plugins/controlled-list-manager.js index f6b5c16..3823d50 100644 --- a/arches_references/media/js/views/components/plugins/controlled-list-manager.js +++ b/arches_references/media/js/views/components/plugins/controlled-list-manager.js @@ -1,18 +1,12 @@ import ko from 'knockout'; +import { routes } from '@/arches_references/routes.ts'; import ControlledListManager from '@/arches_references/plugins/ControlledListManager.vue'; -import ControlledListsMain from '@/arches_references/components/ControlledListsMain.vue'; import createVueApplication from 'utils/create-vue-application'; import ControlledListManagerTemplate from 'templates/views/components/plugins/controlled-list-manager.htm'; import { createRouter, createWebHistory } from 'vue-router'; -const routes = [ - { path: '/plugins/controlled-list-manager', name: 'splash', component: ControlledListsMain }, - { path: '/plugins/controlled-list-manager/list/:id', name: 'list', component: ControlledListsMain }, - { path: '/plugins/controlled-list-manager/item/:id', name: 'item', component: ControlledListsMain }, -]; - const router = createRouter({ history: createWebHistory(), routes, diff --git a/arches_references/src/arches_references/components/ControlledListsMain.vue b/arches_references/src/arches_references/components/ControlledListsMain.vue index a98e8fd..a18ea65 100644 --- a/arches_references/src/arches_references/components/ControlledListsMain.vue +++ b/arches_references/src/arches_references/components/ControlledListsMain.vue @@ -8,9 +8,9 @@ import Toast from "primevue/toast"; import { displayedRowKey, - routes, selectedLanguageKey, } from "@/arches_references/constants.ts"; +import { routeNames } from "@/arches_references/routes.ts"; import { dataIsList } from "@/arches_references/utils.ts"; import ListHeader from "@/arches_references/components/misc/ListHeader.vue"; @@ -26,16 +26,16 @@ const displayedRow: Ref = ref(null); const setDisplayedRow = (val: Selectable | null) => { displayedRow.value = val; if (val === null) { - router.push({ name: routes.splash }); + router.push({ name: routeNames.splash }); return; } if (typeof val.id === "number") { return; } if (dataIsList(val)) { - router.push({ name: routes.list, params: { id: val.id } }); + router.push({ name: routeNames.list, params: { id: val.id } }); } else { - router.push({ name: routes.item, params: { id: val.id } }); + router.push({ name: routeNames.item, params: { id: val.id } }); } }; // @ts-expect-error vue-tsc doesn't like arbitrary properties here diff --git a/arches_references/src/arches_references/components/MainSplitter.vue b/arches_references/src/arches_references/components/MainSplitter.vue index bb1a34d..f36822c 100644 --- a/arches_references/src/arches_references/components/MainSplitter.vue +++ b/arches_references/src/arches_references/components/MainSplitter.vue @@ -5,7 +5,8 @@ import ProgressSpinner from "primevue/progressspinner"; import Splitter from "primevue/splitter"; import SplitterPanel from "primevue/splitterpanel"; -import { displayedRowKey, routes } from "@/arches_references/constants.ts"; +import { displayedRowKey } from "@/arches_references/constants.ts"; +import { routeNames } from "@/arches_references/routes.ts"; import { dataIsList } from "@/arches_references/utils.ts"; import ControlledListSplash from "@/arches_references/components/misc/ControlledListSplash.vue"; import ItemEditor from "@/arches_references/components/editor/ItemEditor.vue"; @@ -55,7 +56,7 @@ const panel = computed(() => { > diff --git a/arches_references/src/arches_references/components/tree/ListTree.vue b/arches_references/src/arches_references/components/tree/ListTree.vue index af6ba87..68f2004 100644 --- a/arches_references/src/arches_references/components/tree/ListTree.vue +++ b/arches_references/src/arches_references/components/tree/ListTree.vue @@ -7,9 +7,9 @@ import Tree from "primevue/tree"; import { displayedRowKey, - routes, selectedLanguageKey, } from "@/arches_references/constants.ts"; +import { routeNames } from "@/arches_references/routes.ts"; import { bestLabel, findNodeInTree, @@ -77,12 +77,12 @@ watch( ); const navigate = (newRoute: RouteLocationNormalizedLoadedGeneric) => { switch (newRoute.name) { - case routes.splash: + case routeNames.splash: setDisplayedRow(null); expandedKeys.value = {}; selectedKeys.value = {}; break; - case routes.list: { + case routeNames.list: { if (!tree.value.length) { return; } @@ -101,7 +101,7 @@ const navigate = (newRoute: RouteLocationNormalizedLoadedGeneric) => { } break; } - case routes.item: { + case routeNames.item: { if (!tree.value.length) { return; } diff --git a/arches_references/src/arches_references/constants.ts b/arches_references/src/arches_references/constants.ts index 554cd2f..1ebc673 100644 --- a/arches_references/src/arches_references/constants.ts +++ b/arches_references/src/arches_references/constants.ts @@ -26,12 +26,6 @@ export const SECONDARY = "secondary"; export const SUCCESS = "success"; export const DEFAULT_ERROR_TOAST_LIFE = 8000; -export const routes = { - splash: "splash", - list: "list", - item: "item", -}; - // Django model choices export const METADATA_CHOICES = { title: "title", diff --git a/arches_references/src/arches_references/routes.ts b/arches_references/src/arches_references/routes.ts new file mode 100644 index 0000000..113a120 --- /dev/null +++ b/arches_references/src/arches_references/routes.ts @@ -0,0 +1,25 @@ +import ControlledListsMain from "@/arches_references/components/ControlledListsMain.vue"; + +export const routes = [ + { + path: "/plugins/controlled-list-manager", + name: "splash", + component: ControlledListsMain, + }, + { + path: "/plugins/controlled-list-manager/list/:id", + name: "list", + component: ControlledListsMain, + }, + { + path: "/plugins/controlled-list-manager/item/:id", + name: "item", + component: ControlledListsMain, + }, +]; + +export const routeNames = { + splash: "splash", + list: "list", + item: "item", +}; From 78681c69a10c7ddc98c5b9a67f2904ca20b3c847 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Mon, 19 Aug 2024 17:46:33 -0400 Subject: [PATCH 12/12] Group watch() together --- .../components/tree/ListTree.vue | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/arches_references/src/arches_references/components/tree/ListTree.vue b/arches_references/src/arches_references/components/tree/ListTree.vue index 68f2004..6d4d909 100644 --- a/arches_references/src/arches_references/components/tree/ListTree.vue +++ b/arches_references/src/arches_references/components/tree/ListTree.vue @@ -65,16 +65,7 @@ const { setDisplayedRow } = inject(displayedRowKey) as unknown as { }; const route = useRoute(); -watch( - [ - () => { - return { ...route }; - }, - ], - ([newRoute]) => { - navigate(newRoute); - }, -); + const navigate = (newRoute: RouteLocationNormalizedLoadedGeneric) => { switch (newRoute.name) { case routeNames.splash: @@ -130,6 +121,18 @@ const navigate = (newRoute: RouteLocationNormalizedLoadedGeneric) => { } }; +// React to route changes. +watch( + [ + () => { + return { ...route }; + }, + ], + ([newRoute]) => { + navigate(newRoute); + }, +); + // Navigate on initial load of the tree. watch( tree,