Bug 37893: SIP2 VueJS module scaffolding

Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com>
Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Pedro Amorim 2024-10-10 09:31:24 +00:00 committed by Lucas Gass
parent a752b7ecd9
commit 38fe4442d2
No known key found for this signature in database
GPG Key ID: 60B447398B0D36CA
13 changed files with 356 additions and 0 deletions

View File

@ -362,6 +362,7 @@ my $target_map = {
'./rotating_collections' => 'INTRANET_CGI_DIR',
'./serials' => 'INTRANET_CGI_DIR',
'./services' => 'INTRANET_CGI_DIR',
'./sip2' => 'INTRANET_CGI_DIR',
'./skel' => 'NONE',
'./skel/var/lock/koha' => { target => 'LOCK_DIR', trimdir => -1 },
'./skel/var/log/koha' => { target => 'LOG_DIR', trimdir => -1 },

View File

@ -21,6 +21,7 @@ RewriteRule ^/issn/([^\/]*)/?$ /search?q=issn:$1 [PT]
RewriteRule ^(.*)_[0-9]{2}\.[0-9]{7}\.(js|css)$ $1.$2 [L]
RewriteRule ^/cgi-bin/koha/erm/.*$ /cgi-bin/koha/erm/erm.pl [PT]
RewriteRule ^/cgi-bin/koha/sip2/.*$ /cgi-bin/koha/sip2/sip2.pl [PT]
RewriteCond %{REQUEST_URI} !^/cgi-bin/koha/preservation/.*.pl$
RewriteRule ^/cgi-bin/koha/preservation/.*$ /cgi-bin/koha/preservation/home.pl [PT]
RewriteRule ^/cgi-bin/koha/admin/record_sources(.*)?$ /cgi-bin/koha/admin/record_sources.pl$1 [PT]

View File

@ -0,0 +1,22 @@
[% SET footerjs = 1 %]
[% INCLUDE 'doc-head-open.inc' %]
<title> SIP2 &rsaquo; Koha </title>
[% INCLUDE 'doc-head-close.inc' %]
</head>
<body id="sip2_home" class="sip2">
[% WRAPPER 'header.inc' %]
[% INCLUDE 'prefs-admin-search.inc' %]
[% END %]
<div id="sip2">
<!-- this is closed in intranet-bottom.inc -->
[% MACRO jsinclude BLOCK %]
[% INCLUDE 'calendar.inc' %]
[% INCLUDE 'datatables.inc' %]
[% Asset.js("js/vue/dist/sip2.js") | $raw %]
[% END %]
[% INCLUDE 'intranet-bottom.inc' %]
</div>

View File

@ -0,0 +1,18 @@
export class SIP2APIClient {
constructor(HttpClient) {
this.httpClient = new HttpClient({
baseURL: "/api/v1/sip2",
});
}
get institutions() {
return {
getAll: params =>
this.getAll({
endpoint: "institutions",
}),
};
}
}
export default SIP2APIClient;

View File

@ -0,0 +1,11 @@
<template></template>
<script>
export default {
data() {
return {};
},
methods: {},
components: {},
};
</script>

View File

@ -0,0 +1,118 @@
<template>
<div v-if="initialized">
<div id="sub-header">
<Breadcrumbs />
<Help />
</div>
<div class="main container-fluid">
<div class="row">
<div class="col-md-10 order-md-2 order-sm-1">
<main>
<Dialog />
<router-view />
</main>
</div>
<div class="col-md-2 order-sm-2 order-md-1">
<LeftMenu :title="$__('SIP2')"></LeftMenu>
</div>
</div>
</div>
</div>
</template>
<script>
import { inject } from "vue";
import { APIClient } from "../../fetch/api-client.js";
import Breadcrumbs from "../Breadcrumbs.vue";
import Help from "../Help.vue";
import LeftMenu from "../LeftMenu.vue";
import Dialog from "../Dialog.vue";
import "vue-select/dist/vue-select.css";
export default {
setup() {
const AVStore = inject("AVStore");
return {
AVStore,
};
},
data() {
return {
initialized: false,
};
},
beforeCreate() {
const av_client = APIClient.authorised_values;
const authorised_values = {
av_lost: "LOST",
};
let av_cat_array = Object.keys(authorised_values).map(
function (av_cat) {
return '"' + authorised_values[av_cat] + '"';
}
);
av_client.values
.getCategoriesWithValues(av_cat_array)
.then(av_categories => {
Object.entries(authorised_values).forEach(
([av_var, av_cat]) => {
const av_match = av_categories.find(
element => element.category_name == av_cat
);
this.AVStore[av_var] = av_match.authorised_values;
}
);
})
.then(() => {
this.initialized = true;
});
},
components: {
Breadcrumbs,
Dialog,
Help,
LeftMenu,
},
};
</script>
<style>
#menu ul ul,
#navmenulist ul ul {
padding-left: 2em;
font-size: 100%;
}
form .v-select {
display: inline-block;
background-color: white;
width: 30%;
}
.v-select,
input:not([type="submit"]):not([type="search"]):not([type="button"]):not(
[type="checkbox"]
):not([type="radio"]),
textarea {
border-color: rgba(60, 60, 60, 0.26);
border-width: 1px;
border-radius: 4px;
min-width: 30%;
}
#navmenulist ul li a.current.disabled {
background-color: inherit;
border-left: 5px solid #e6e6e6;
color: #000;
}
#navmenulist ul li a.disabled {
color: #666;
pointer-events: none;
font-weight: 700;
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<div id="sip2_institutions_list">
<div class="page-section">
<KohaTable
ref="table"
v-bind="tableOptions"
@install="doInstall"
></KohaTable>
</div>
</div>
</template>
<script>
import Toolbar from "../Toolbar.vue";
import ToolbarButton from "../ToolbarButton.vue";
import KohaTable from "../KohaTable.vue";
export default {
data: function () {
return {
// tableOptions: {
before_route_entered: false,
building_table: false,
};
},
methods: {
table_url: function () {
// FIXME: This is hardcoded
return "http://localhost:3000/api/plugins";
},
},
components: { Toolbar, ToolbarButton, KohaTable },
name: "SIP2InstitutionsList",
};
</script>

View File

@ -8,6 +8,7 @@ import AVAPIClient from "@fetch/authorised-values-api-client";
import ItemAPIClient from "@fetch/item-api-client";
import RecordSourcesAPIClient from "@fetch/record-sources-api-client";
import SysprefAPIClient from "@fetch/system-preferences-api-client";
import SIP2APIClient from "@fetch/ssip2-api-client";
import PreservationAPIClient from "@fetch/preservation-api-client";
export const APIClient = {
@ -18,6 +19,7 @@ export const APIClient = {
authorised_values: new AVAPIClient(HttpClient),
item: new ItemAPIClient(HttpClient),
sysprefs: new SysprefAPIClient(HttpClient),
sip2: new SIP2APIClient(HttpClient),
preservation: new PreservationAPIClient(HttpClient),
record_sources: new RecordSourcesAPIClient(HttpClient),
};

View File

@ -0,0 +1,65 @@
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,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import vSelect from "vue-select";
library.add(faPlus, faMinus, faPencil, faTrash, faSpinner);
import App from "../components/SIP2/Main.vue";
import { routes as routesDef } from "../routes/sip2";
import { useMainStore } from "../stores/main";
import { useVendorStore } from "../stores/vendors";
import { useAVStore } from "../stores/authorised-values";
import { useSIP2Store } from "../stores/sip2";
import { useNavigationStore } from "../stores/navigation";
import i18n from "../i18n";
const pinia = createPinia();
const mainStore = useMainStore(pinia);
const AVStore = useAVStore(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("vendorStore", useVendorStore(pinia));
app.provide("mainStore", mainStore);
app.provide("AVStore", AVStore);
app.provide("navigationStore", navigationStore);
const SIP2Store = useSIP2Store(pinia);
app.provide("SIP2Store", SIP2Store);
app.mount("#sip2");
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
});

View File

@ -0,0 +1,36 @@
import { markRaw } from "vue";
import Home from "../components/SIP2/Home.vue";
import SIP2InstitutionsList from "../components/SIP2/SIP2InstitutionsList.vue";
import { $__ } from "../i18n";
export const routes = [
{
path: "/cgi-bin/koha/sip2/sip2.pl",
is_default: true,
is_base: true,
title: $__("SIP2"),
children: [
{
path: "",
name: "Home",
component: markRaw(Home),
is_navigation_item: false,
},
{
path: "/cgi-bin/koha/sip2/institutions",
title: $__("Institutions"),
icon: "fa fa-building-columns",
is_end_node: true,
children: [
{
path: "",
name: "SIP2InstitutionsList",
component: markRaw(SIP2InstitutionsList),
},
],
},
],
},
];

View File

@ -0,0 +1,9 @@
import { defineStore } from "pinia";
export const useSIP2Store = defineStore("sip2", {
state: () => ({
config: {
displayRestartSIPDialog: true,
},
}),
});

View File

@ -27,6 +27,7 @@ module.exports = [
acquisitions:
"./koha-tmpl/intranet-tmpl/prog/js/vue/modules/acquisitions.ts",
islands: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts",
sip2: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/sip2.ts",
},
output: {
filename: "[name].js",

37
sip2/sip2.pl Normal file
View File

@ -0,0 +1,37 @@
#!/usr/bin/perl
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.
use Modern::Perl;
use CGI qw ( -utf8 );
use C4::Context;
use C4::Auth qw( get_template_and_user );
use C4::Output qw( output_html_with_http_headers );
my $input = CGI->new;
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
{
template_name => "sip2/sip2.tt",
query => $input,
type => "intranet",
# flagsrequired => { 'plugins' => '*' },
}
);
output_html_with_http_headers $input, $cookie, $template->output;