Bug 26355: Add a Vue island for the self-renewal modal
Signed-off-by: Christopher Brannon <cbrannon@cdalibrary.org> Signed-off-by: Andrew Fuerste Henry <andrew@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:
parent
4979f0aead
commit
133946e50c
|
|
@ -13,6 +13,7 @@ koha-tmpl/opac-tmpl/bootstrap/css/print.css
|
||||||
koha-tmpl/opac-tmpl/bootstrap/css/print-rtl.css
|
koha-tmpl/opac-tmpl/bootstrap/css/print-rtl.css
|
||||||
koha-tmpl/opac-tmpl/bootstrap/css/sco.css
|
koha-tmpl/opac-tmpl/bootstrap/css/sco.css
|
||||||
koha-tmpl/opac-tmpl/bootstrap/css/sco-rtl.css
|
koha-tmpl/opac-tmpl/bootstrap/css/sco-rtl.css
|
||||||
|
koha-tmpl/opac-tmpl/bootstrap/js/vue/dist/
|
||||||
|
|
||||||
koha-tmpl/intranet-tmpl/prog/css/maps/
|
koha-tmpl/intranet-tmpl/prog/css/maps/
|
||||||
koha-tmpl/intranet-tmpl/prog/css/bookings.css
|
koha-tmpl/intranet-tmpl/prog/css/bookings.css
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
id="patronSelfRenewal"
|
||||||
|
class="modal modal-full"
|
||||||
|
tabindex="-1"
|
||||||
|
role="dialog"
|
||||||
|
aria-labelledby="patronSelfRenewal"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div class="modal-dialog modal-xl">
|
||||||
|
<div v-if="initialized" class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h1 class="modal-title">
|
||||||
|
{{ $__("Patron self-renewal") }}
|
||||||
|
</h1>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn-close"
|
||||||
|
data-bs-dismiss="modal"
|
||||||
|
aria-label="Close"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<VerificationChecks
|
||||||
|
v-if="activeStep === 'verification'"
|
||||||
|
:renewalSettings="renewalSettings"
|
||||||
|
@verification-successful="onVerificationSuccess"
|
||||||
|
/>
|
||||||
|
<VerificationChecks
|
||||||
|
v-if="activeStep === 'confirmation'"
|
||||||
|
:informationMessage="
|
||||||
|
$__('Are you sure you want to renew your account?')
|
||||||
|
"
|
||||||
|
:renewalSettings="renewalSettings"
|
||||||
|
@verification-successful="submitRenewal"
|
||||||
|
/>
|
||||||
|
<div v-if="activeStep === 'detailsCheck'">
|
||||||
|
<legend>
|
||||||
|
{{ $__("Confirm your account details") }}
|
||||||
|
</legend>
|
||||||
|
<div class="detail_confirmation">
|
||||||
|
<span>{{
|
||||||
|
$__(
|
||||||
|
"You need to confirm your personal details to proceed with your account renewal."
|
||||||
|
)
|
||||||
|
}}</span>
|
||||||
|
<button
|
||||||
|
class="btn btn-default"
|
||||||
|
@click="proceedToDetailsVerification()"
|
||||||
|
>
|
||||||
|
{{ $__("Continue") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-default cancel"
|
||||||
|
data-bs-dismiss="modal"
|
||||||
|
>
|
||||||
|
{{ $__("Close") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onBeforeMount, ref } from "vue";
|
||||||
|
import { APIClient } from "../../../fetch/api-client.js";
|
||||||
|
import VerificationChecks from "./VerificationChecks.vue";
|
||||||
|
import { $__ } from "@koha-vue/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { VerificationChecks },
|
||||||
|
props: {
|
||||||
|
patron: String,
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
const initialized = ref(false);
|
||||||
|
const activeStep = ref(null);
|
||||||
|
const renewalSettings = ref({
|
||||||
|
defaultErrorMessage: $__(
|
||||||
|
"You are not able to self-renew with the provided information. Please visit your library to proceed with your renewal."
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
const client = APIClient.patron;
|
||||||
|
client.self_renewal.start(props.patron).then(
|
||||||
|
response => {
|
||||||
|
renewalSettings.value = {
|
||||||
|
...renewalSettings.value,
|
||||||
|
...response.self_renewal_settings,
|
||||||
|
};
|
||||||
|
activeStep.value = response.self_renewal_settings
|
||||||
|
.self_renewal_information_message
|
||||||
|
? "verification"
|
||||||
|
: response.self_renewal_settings.opac_patron_details ===
|
||||||
|
"1"
|
||||||
|
? "detailsCheck"
|
||||||
|
: "confirmation";
|
||||||
|
initialized.value = true;
|
||||||
|
},
|
||||||
|
error => {}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const proceedToDetailsVerification = () => {
|
||||||
|
window.location.href =
|
||||||
|
"/cgi-bin/koha/opac-memberentry.pl?self_renewal=1";
|
||||||
|
};
|
||||||
|
|
||||||
|
const onVerificationSuccess = () => {
|
||||||
|
if (renewalSettings.value.opac_patron_details === "1") {
|
||||||
|
activeStep.value = "detailsCheck";
|
||||||
|
} else {
|
||||||
|
activeStep.value = "confirmation";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitRenewal = () => {
|
||||||
|
const client = APIClient.patron;
|
||||||
|
client.self_renewal.submit(props.patron, {}).then(
|
||||||
|
response => {
|
||||||
|
let newLocation =
|
||||||
|
"/cgi-bin/koha/opac-user.pl?self_renewal_success=" +
|
||||||
|
response.expiry_date;
|
||||||
|
if (response.confirmation_sent) {
|
||||||
|
newLocation += "&confirmation_sent=1";
|
||||||
|
}
|
||||||
|
document.location = newLocation;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
document.location =
|
||||||
|
"/cgi-bin/koha/opac-user.pl?self_renewal_success=0";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
initialized,
|
||||||
|
activeStep,
|
||||||
|
renewalSettings,
|
||||||
|
onVerificationSuccess,
|
||||||
|
proceedToDetailsVerification,
|
||||||
|
submitRenewal,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.detail_confirmation {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
.detail_confirmation button {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1em;
|
||||||
|
width: 7em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<template>
|
||||||
|
<fieldset class="rows" v-if="!verificationFailure">
|
||||||
|
<span class="verification_question">{{ verificationCheck }}</span>
|
||||||
|
<div class="verification_actions">
|
||||||
|
<button class="btn btn-success" @click="verificationPassed()">
|
||||||
|
{{ $__("Yes") }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-default" @click="verificationFailed()">
|
||||||
|
{{ $__("No") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<span class="error" v-else>{{ errorMessage }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { $__ } from "@koha-vue/i18n";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
renewalSettings: Object,
|
||||||
|
informationMessage: String,
|
||||||
|
},
|
||||||
|
emits: ["verification-successful"],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const verificationFailure = ref(false);
|
||||||
|
const verificationCheck = ref(
|
||||||
|
props.informationMessage ||
|
||||||
|
props.renewalSettings.self_renewal_information_message
|
||||||
|
);
|
||||||
|
|
||||||
|
const verificationPassed = () => {
|
||||||
|
emit("verification-successful");
|
||||||
|
};
|
||||||
|
const verificationFailed = () => {
|
||||||
|
verificationFailure.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorMessage = computed(() => {
|
||||||
|
const { self_renewal_failure_message, defaultErrorMessage } =
|
||||||
|
props.renewalSettings;
|
||||||
|
return self_renewal_failure_message || defaultErrorMessage;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
verificationCheck,
|
||||||
|
verificationPassed,
|
||||||
|
verificationFailed,
|
||||||
|
verificationFailure,
|
||||||
|
errorMessage,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.rows {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.verification_actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
.verification_actions button {
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
.verification_question {
|
||||||
|
margin-bottom: 2em;
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -87,6 +87,19 @@ export const componentRegistry: Map<string, WebComponentDynamicImport> =
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"patron-self-renewal",
|
||||||
|
{
|
||||||
|
importFn: async () => {
|
||||||
|
const module = await import(
|
||||||
|
/* webpackChunkName: "patron-self-renewal" */
|
||||||
|
"../components/Islands/PatronSelfRenewal/PatronSelfRenewal.vue"
|
||||||
|
);
|
||||||
|
return module.default;
|
||||||
|
},
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
173
rspack.config.js
173
rspack.config.js
|
|
@ -3,6 +3,96 @@ const { VueLoaderPlugin } = require("vue-loader");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const rspack = require("@rspack/core");
|
const rspack = require("@rspack/core");
|
||||||
|
|
||||||
|
const islandsExport = application => {
|
||||||
|
const vueDir =
|
||||||
|
application === "intranet"
|
||||||
|
? "intranet-tmpl/prog"
|
||||||
|
: "opac-tmpl/bootstrap";
|
||||||
|
|
||||||
|
return {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@fetch": path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"koha-tmpl/intranet-tmpl/prog/js/fetch"
|
||||||
|
),
|
||||||
|
"@koha-vue": path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"koha-tmpl/intranet-tmpl/prog/js/vue"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
experiments: {
|
||||||
|
outputModule: true,
|
||||||
|
},
|
||||||
|
entry: {
|
||||||
|
islands: [
|
||||||
|
"./koha-tmpl/intranet-tmpl/prog/js/vue/csp-nonce.js",
|
||||||
|
"./koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: "[name].esm.js",
|
||||||
|
path: path.resolve(__dirname, `koha-tmpl/${vueDir}/js/vue/dist/`),
|
||||||
|
chunkFilename: "[name].[contenthash].esm.js",
|
||||||
|
globalObject: "window",
|
||||||
|
library: {
|
||||||
|
type: "module",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.vue$/,
|
||||||
|
loader: "vue-loader",
|
||||||
|
options: {
|
||||||
|
experimentalInlineMatchResource: true,
|
||||||
|
},
|
||||||
|
exclude: [path.resolve(__dirname, "t/cypress/")],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
loader: "builtin:swc-loader",
|
||||||
|
options: {
|
||||||
|
jsc: {
|
||||||
|
parser: {
|
||||||
|
syntax: "typescript",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
appendTsSuffixTo: [/\.vue$/],
|
||||||
|
},
|
||||||
|
exclude: [
|
||||||
|
/node_modules/,
|
||||||
|
path.resolve(__dirname, "t/cypress/"),
|
||||||
|
],
|
||||||
|
type: "javascript/auto",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/i,
|
||||||
|
type: "javascript/auto",
|
||||||
|
use: ["style-loader", "css-loader"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new VueLoaderPlugin(),
|
||||||
|
new rspack.DefinePlugin({
|
||||||
|
__VUE_OPTIONS_API__: true,
|
||||||
|
__VUE_PROD_DEVTOOLS__: false,
|
||||||
|
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
externals: {
|
||||||
|
jquery: "jQuery",
|
||||||
|
"datatables.net": "DataTable",
|
||||||
|
"datatables.net-buttons": "DataTable",
|
||||||
|
"datatables.net-buttons/js/buttons.html5": "DataTable",
|
||||||
|
"datatables.net-buttons/js/buttons.print": "DataTable",
|
||||||
|
"datatables.net-buttons/js/buttons.colVis": "DataTable",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = [
|
module.exports = [
|
||||||
{
|
{
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|
@ -104,87 +194,8 @@ module.exports = [
|
||||||
"datatables.net-buttons/js/buttons.colVis": "DataTable",
|
"datatables.net-buttons/js/buttons.colVis": "DataTable",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
islandsExport("intranet"),
|
||||||
resolve: {
|
islandsExport("opac"),
|
||||||
alias: {
|
|
||||||
"@fetch": path.resolve(
|
|
||||||
__dirname,
|
|
||||||
"koha-tmpl/intranet-tmpl/prog/js/fetch"
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
experiments: {
|
|
||||||
outputModule: true,
|
|
||||||
},
|
|
||||||
entry: {
|
|
||||||
islands: [
|
|
||||||
"./koha-tmpl/intranet-tmpl/prog/js/vue/csp-nonce.js",
|
|
||||||
"./koha-tmpl/intranet-tmpl/prog/js/vue/modules/islands.ts",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
filename: "[name].esm.js",
|
|
||||||
path: path.resolve(
|
|
||||||
__dirname,
|
|
||||||
"koha-tmpl/intranet-tmpl/prog/js/vue/dist/"
|
|
||||||
),
|
|
||||||
chunkFilename: "[name].[contenthash].esm.js",
|
|
||||||
globalObject: "window",
|
|
||||||
library: {
|
|
||||||
type: "module",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.vue$/,
|
|
||||||
loader: "vue-loader",
|
|
||||||
options: {
|
|
||||||
experimentalInlineMatchResource: true,
|
|
||||||
},
|
|
||||||
exclude: [path.resolve(__dirname, "t/cypress/")],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.ts$/,
|
|
||||||
loader: "builtin:swc-loader",
|
|
||||||
options: {
|
|
||||||
jsc: {
|
|
||||||
parser: {
|
|
||||||
syntax: "typescript",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
appendTsSuffixTo: [/\.vue$/],
|
|
||||||
},
|
|
||||||
exclude: [
|
|
||||||
/node_modules/,
|
|
||||||
path.resolve(__dirname, "t/cypress/"),
|
|
||||||
],
|
|
||||||
type: "javascript/auto",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.css$/i,
|
|
||||||
type: "javascript/auto",
|
|
||||||
use: ["style-loader", "css-loader"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new VueLoaderPlugin(),
|
|
||||||
new rspack.DefinePlugin({
|
|
||||||
__VUE_OPTIONS_API__: true,
|
|
||||||
__VUE_PROD_DEVTOOLS__: false,
|
|
||||||
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
externals: {
|
|
||||||
jquery: "jQuery",
|
|
||||||
"datatables.net": "DataTable",
|
|
||||||
"datatables.net-buttons": "DataTable",
|
|
||||||
"datatables.net-buttons/js/buttons.html5": "DataTable",
|
|
||||||
"datatables.net-buttons/js/buttons.print": "DataTable",
|
|
||||||
"datatables.net-buttons/js/buttons.colVis": "DataTable",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
entry: {
|
entry: {
|
||||||
"api-client.cjs": [
|
"api-client.cjs": [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue