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/sco.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/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 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 = [
|
||||
{
|
||||
resolve: {
|
||||
|
|
@ -104,87 +194,8 @@ module.exports = [
|
|||
"datatables.net-buttons/js/buttons.colVis": "DataTable",
|
||||
},
|
||||
},
|
||||
{
|
||||
resolve: {
|
||||
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",
|
||||
},
|
||||
},
|
||||
islandsExport("intranet"),
|
||||
islandsExport("opac"),
|
||||
{
|
||||
entry: {
|
||||
"api-client.cjs": [
|
||||
|
|
|
|||
Loading…
Reference in New Issue