From 772dcf52ef1373bb3ac3eca4a885496488ddb527 Mon Sep 17 00:00:00 2001 From: Jake Deery Date: Mon, 1 Dec 2025 14:41:21 +0000 Subject: [PATCH] Bug 14962: On Display code for new Vue app This patch adds the On Display Vue app. Please make sure you rebuild your JavaScript prior to testing this patch. Please see the test files commit for instructions on how to test this bundle of patches. --- .../Display/DisplaysBatchAddItems.vue | 191 +++++++ .../Display/DisplaysBatchRemoveItems.vue | 169 ++++++ .../components/Display/DisplaysResource.vue | 501 ++++++++++++++++++ .../prog/js/vue/components/Display/Home.vue | 9 + .../prog/js/vue/components/Display/Main.vue | 115 ++++ .../prog/js/vue/fetch/api-client.js | 8 + .../prog/js/vue/fetch/http-client.js | 6 + .../prog/js/vue/modules/display.ts | 72 +++ .../prog/js/vue/routes/display.js | 82 +++ .../prog/js/vue/stores/display.js | 27 + 10 files changed, 1180 insertions(+) create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysBatchAddItems.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysBatchRemoveItems.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysResource.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/Home.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/Main.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/modules/display.ts create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/routes/display.js create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/stores/display.js diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysBatchAddItems.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysBatchAddItems.vue new file mode 100644 index 00000000000..1558b2aa8cf --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysBatchAddItems.vue @@ -0,0 +1,191 @@ + + + + + diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysBatchRemoveItems.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysBatchRemoveItems.vue new file mode 100644 index 00000000000..90288bfb8f1 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysBatchRemoveItems.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysResource.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysResource.vue new file mode 100644 index 00000000000..5ff2ba08db2 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/DisplaysResource.vue @@ -0,0 +1,501 @@ + + diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/Home.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/Home.vue new file mode 100644 index 00000000000..2968a34c108 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/Home.vue @@ -0,0 +1,9 @@ + + + diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/Main.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/Main.vue new file mode 100644 index 00000000000..e41ae4b170a --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Display/Main.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js index 2906a9c296b..52d258df719 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js @@ -6,7 +6,11 @@ import AcquisitionAPIClient from "@fetch/acquisition-api-client"; import AdditionalFieldsAPIClient from "@fetch/additional-fields-api-client"; import AVAPIClient from "@fetch/authorised-values-api-client"; import CashAPIClient from "@fetch/cash-api-client"; +import BiblioAPIClient from '@fetch/biblio-api-client.js'; +import DisplayAPIClient from "@fetch/display-api-client"; import ItemAPIClient from "@fetch/item-api-client"; +import ItemTypeAPIClient from '@fetch/item-type-api-client.js'; +import LibraryAPIClient from "@fetch/library-api-client"; import RecordSourcesAPIClient from "@fetch/record-sources-api-client"; import SysprefAPIClient from "@fetch/system-preferences-api-client"; import SIP2APIClient from "@fetch/sip2-api-client"; @@ -19,7 +23,11 @@ export const APIClient = { additional_fields: new AdditionalFieldsAPIClient(HttpClient), authorised_values: new AVAPIClient(HttpClient), cash: new CashAPIClient(HttpClient), + biblio: new BiblioAPIClient(HttpClient), + display: new DisplayAPIClient(HttpClient), item: new ItemAPIClient(HttpClient), + item_type: new ItemTypeAPIClient(HttpClient), + library: new LibraryAPIClient(HttpClient), sysprefs: new SysprefAPIClient(HttpClient), sip2: new SIP2APIClient(HttpClient), preservation: new PreservationAPIClient(HttpClient), diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js index f31856bf106..64551056e93 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/http-client.js @@ -150,6 +150,11 @@ class HttpClient { } delete(params = {}) { + const body = params.body + ? typeof params.body === "string" + ? params.body + : JSON.stringify(params.body) + : undefined; let csrf_token = { "CSRF-TOKEN": this.csrf_token }; let headers = { ...csrf_token, ...params.headers }; return this._fetchJSON( @@ -158,6 +163,7 @@ class HttpClient { { parseResponse: false, ...params.options, + body, method: "DELETE", }, params.return_response ?? true, diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/modules/display.ts b/koha-tmpl/intranet-tmpl/prog/js/vue/modules/display.ts new file mode 100644 index 00000000000..c8ff1734554 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/modules/display.ts @@ -0,0 +1,72 @@ +import { createApp } from "vue"; +import { createWebHistory, createRouter } from "vue-router"; +import { createPinia } from "pinia"; + +import { library } from "@fortawesome/fontawesome-svg-core"; +import { + faPlus, + faMinus, + faPencil, + faTrash, + faSpinner, + faClose, + faPaperPlane, + faInbox, +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; +import vSelect from "vue-select"; + +library.add( + faPlus, + faMinus, + faPencil, + faTrash, + faSpinner, + faClose, + faPaperPlane, + faInbox +); + +import App from "../components/Display/Main.vue"; + +import { routes as routesDef } from "../routes/display"; + +import { useMainStore } from "../stores/main"; +import { useDisplayStore } from "../stores/display"; +import { useNavigationStore } from "../stores/navigation"; +import i18n from "@koha-vue/i18n"; + +const pinia = createPinia(); + +const mainStore = useMainStore(pinia); +const navigationStore = useNavigationStore(pinia); +const routes = navigationStore.setRoutes(routesDef); + +const router = createRouter({ + history: createWebHistory(), + linkActiveClass: "current", + routes, +}); + +const app = createApp(App); + +const rootComponent = app + .use(i18n) + .use(pinia) + .use(router) + .component("font-awesome-icon", FontAwesomeIcon) + .component("v-select", vSelect); + +app.config.unwrapInjectedRef = true; +app.provide("mainStore", mainStore); +app.provide("navigationStore", navigationStore); +const DisplayStore = useDisplayStore(pinia); +app.provide("DisplayStore", DisplayStore); + +app.mount("#display"); + +const { removeMessages } = mainStore; +router.beforeEach((to, from) => { + navigationStore.$patch({ current: to.matched, params: to.params || {} }); + removeMessages(); // This will actually flag the messages as displayed already +}); diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/routes/display.js b/koha-tmpl/intranet-tmpl/prog/js/vue/routes/display.js new file mode 100644 index 00000000000..b83cd47b751 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/routes/display.js @@ -0,0 +1,82 @@ +import { markRaw } from "vue"; + +import Home from "../components/Display/Home.vue"; +import DisplaysBatchAddItems from "../components/Display/DisplaysBatchAddItems.vue"; +import DisplaysBatchRemoveItems from "../components/Display/DisplaysBatchRemoveItems.vue"; + +import ResourceWrapper from "../components/ResourceWrapper.vue"; + +import { $__ } from "@koha-vue/i18n"; + +export const routes = [ + { + path: "/cgi-bin/koha/display/display-home.pl", + is_default: true, + is_base: true, + title: $__("Displays"), + children: [ + { + path: "", + name: "Home", + component: markRaw(Home), + redirect: "/cgi-bin/koha/display/displays", + is_navigation_item: false, + }, + { + path: "/cgi-bin/koha/display/displays", + title: $__("Displays"), + icon: "fa-solid fa-image-portrait", + is_end_node: true, + resource: "Display/DisplaysResource.vue", + children: [ + { + path: "", + name: "DisplaysList", + component: markRaw(ResourceWrapper), + }, + { + path: ":display_id", + name: "DisplaysShow", + component: markRaw(ResourceWrapper), + title: "{display_name}", + }, + { + path: "add", + name: "DisplaysFormAdd", + component: markRaw(ResourceWrapper), + title: $__("Add display"), + }, + { + path: "edit/:display_id", + name: "DisplaysFormAddEdit", + component: markRaw(ResourceWrapper), + title: "{display_name}", + breadcrumbFormat: ({ match, params, query }) => { + match.name = "DisplaysShow"; + return match; + }, + additionalBreadcrumbs: [ + { title: $__("Modify display"), disabled: true }, + ], + }, + { + path: "batch-add", + name: "DisplaysBatchAddItems", + component: markRaw( + DisplaysBatchAddItems + ), + title: $__("Batch add items from file"), + }, + { + path: "batch-remove", + name: "DisplaysBatchRemoveItems", + component: markRaw( + DisplaysBatchRemoveItems + ), + title: $__("Batch remove items from file"), + }, + ], + }, + ], + }, +]; diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/stores/display.js b/koha-tmpl/intranet-tmpl/prog/js/vue/stores/display.js new file mode 100644 index 00000000000..894fa1fbfd4 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/stores/display.js @@ -0,0 +1,27 @@ +import { defineStore } from "pinia"; +import { reactive, toRefs } from "vue"; +import { withAuthorisedValueActions } from "../composables/authorisedValues"; + +export const useDisplayStore = defineStore("display", () => { + const store = reactive({ + displayReturnOverMapping: [], + config: { + settings: { + enabled: 0, + }, + }, + authorisedValues: { + av_loc: "LOC", + av_ccode: "CCODE", + }, + }); + + const sharedActions = { + ...withAuthorisedValueActions(store), + }; + + return { + ...toRefs(store), + ...sharedActions, + }; +}); -- 2.50.1 (Apple Git-155)