From 0664ac9e9ec9a7d235f7e640e8b25234d12f5dde Mon Sep 17 00:00:00 2001 From: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com> Date: Tue, 13 Aug 2024 14:16:33 +0000 Subject: [PATCH] Bug 10190: Add components to list/add and edit circulation triggers These new components are used by the new 'Circulation triggers' administration page to display existing trigger rules and add the ability to add new rules and edit existing ones. Sponsored-by: Glasgow Colleges Library Group <https://library.cityofglasgowcollege.ac.uk> Signed-off-by: George Harkins <George.Harkins@cityofglasgowcollege.ac.uk> --- .../CirculationTriggersFormAdd.vue | 524 ++++++++++++++++++ .../CirculationTriggersList.vue | 258 +++++++++ .../CirculationTriggers/TriggerContext.vue | 50 ++ .../CirculationTriggers/TriggersTable.vue | 266 +++++++++ 4 files changed, 1098 insertions(+) create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersFormAdd.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersList.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/TriggerContext.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/TriggersTable.vue diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersFormAdd.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersFormAdd.vue new file mode 100644 index 00000000000..87568766c55 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersFormAdd.vue @@ -0,0 +1,524 @@ +<template> + <router-link :to="{ name: 'CirculationTriggersList' }" class="close_modal"> + <i class="fa fa-fw fa-close"></i> + </router-link> + <div v-if="initialized" class="modal-content"> + <form @submit="addCircRule($event)"> + <div class="modal-header"> + <h1 v-if="!editMode">{{ $__("Add circulation trigger") }}</h1> + <h1 v-else>{{ $__("Edit circulation trigger") }}</h1> + </div> + <div class="modal-body"> + <div class="page-section bg-info" v-if="circRules.length"> + <h2>{{ $__("Trigger context") }}</h2> + <TriggerContext :ruleInfo="ruleInfo" /> + <h2>{{ $__("Existing rules") }}</h2> + <p>{{ $__("Notice") }} {{ " " + newTriggerNumber - 1 }}</p> + <TriggersTable + :circRules="circRules" + :triggerNumber="newTriggerNumber - 1" + :modal="true" + :ruleBeingEdited="ruleBeingEdited" + /> + </div> + <fieldset class="rows"> + <ol> + <li> + <label for="library_id" class="required" + >{{ $__("Library") }}:</label + > + <v-select + id="library_id" + v-model="circRuleTrigger.library_id" + label="name" + :reduce="lib => lib.library_id" + :options="libraries" + @update:modelValue="handleContextChange($event)" + :disabled="editMode ? true : false" + > + <template #search="{ attributes, events }"> + <input + :required="!circRuleTrigger.library_id" + class="vs__search" + v-bind="attributes" + v-on="events" + /> + </template> + </v-select> + <span class="required">{{ $__("Required") }}</span> + </li> + <li> + <label for="patron_category_id" class="required" + >{{ $__("Patron category") }}:</label + > + <v-select + id="patron_category_id" + v-model="circRuleTrigger.patron_category_id" + label="name" + :reduce="cat => cat.patron_category_id" + :options="categories" + @update:modelValue="handleContextChange($event)" + :disabled="editMode ? true : false" + > + <template #search="{ attributes, events }"> + <input + :required=" + !circRuleTrigger.patron_category_id + " + class="vs__search" + v-bind="attributes" + v-on="events" + /> + </template> + </v-select> + <span class="required">{{ $__("Required") }}</span> + </li> + <li> + <label for="item_type_id" class="required" + >{{ $__("Item type") }}:</label + > + <v-select + id="item_type_id" + v-model="circRuleTrigger.item_type_id" + label="description" + :reduce="type => type.item_type_id" + :options="itemTypes" + @update:modelValue="handleContextChange($event)" + :disabled="editMode ? true : false" + > + <template #search="{ attributes, events }"> + <input + :required=" + !circRuleTrigger.item_type_id + " + class="vs__search" + v-bind="attributes" + v-on="events" + /> + </template> + </v-select> + <span class="required">{{ $__("Required") }}</span> + </li> + <li> + <label for="overdue_delay" + >{{ $__("Delay") }}: + </label> + <input + id="overdue_delay" + v-model="circRuleTrigger.delay" + type="number" + /> + </li> + <li> + <label for="letter_code" + >{{ $__("Letter") }}:</label + > + <v-select + id="letter_code" + v-model="circRuleTrigger.notice" + label="name" + :reduce="type => type.code" + :options="letters" + > + <template #search="{ attributes, events }"> + <input + class="vs__search" + v-bind="attributes" + v-on="events" + /> + </template> + </v-select> + </li> + <li> + <label for="mtt" + >{{ $__("Transport type(s)") }}:</label + > + <v-select + id="mtt" + v-model="circRuleTrigger.mtt" + label="name" + :reduce="type => type.code" + :options="mtts" + multiple + > + <template #search="{ attributes, events }"> + <input + class="vs__search" + v-bind="attributes" + v-on="events" + /> + </template> + </v-select> + </li> + <li> + <label for="restricts" + >{{ $__("Restricts checkouts") }}:</label + > + <input + type="checkbox" + id="restricts" + :checked="false" + true-value="1" + false-value="0" + v-model="circRuleTrigger.restrict" + /> + </li> + </ol> + </fieldset> + </div> + <div class="modal-footer"> + <ButtonSubmit /> + <router-link + :to="{ + name: 'CirculationTriggersList', + }" + >Cancel</router-link + > + </div> + </form> + </div> + <div v-else> + <p>{{ $__("Loading...") }}</p> + </div> +</template> + +<script> +import { APIClient } from "../../../fetch/api-client.js"; +import TriggersTable from "./TriggersTable.vue"; +import { inject } from "vue"; +import { storeToRefs } from "pinia"; +import ButtonSubmit from "../../ButtonSubmit.vue"; +import TriggerContext from "./TriggerContext.vue"; + +export default { + setup() { + const circRulesStore = inject("circRulesStore"); + const { splitCircRulesByTriggerNumber } = circRulesStore; + const { letters } = storeToRefs(circRulesStore); + + return { + splitCircRulesByTriggerNumber, + letters, + }; + }, + data() { + return { + initialized: false, + libraries: null, + categories: null, + itemTypes: null, + circRules: [], + circRuleTrigger: { + item_type_id: "*", + library_id: "*", + patron_category_id: "*", + delay: null, + notice: null, + mtt: null, + restrict: "0", + }, + effectiveRule: { + item_type_id: "*", + library_id: "*", + patron_category_id: "*", + delay: null, + notice: null, + mtt: null, + restrict: "0", + }, + newTriggerNumber: 1, + mtts: [ + { code: "email", name: "Email" }, + { code: "sms", name: "SMS" }, + { code: "print", name: "Print" }, + ], + ruleInfo: { + issuelength: null, + decreaseloanholds: null, + fine: null, + chargeperiod: null, + lengthunit: null, + }, + editMode: false, + ruleBeingEdited: null, + }; + }, + beforeRouteEnter(to, from, next) { + next(vm => { + vm.getLibraries().then(() => + vm.getCategories().then(() => + vm.getItemTypes().then(() => { + const { query } = to; + vm.checkForExistingRules(query).then( + () => (vm.initialized = true) + ); + }) + ) + ); + }); + }, + methods: { + async addCircRule(e) { + e.preventDefault(); + + const context = { + library_id: this.circRuleTrigger.library_id || "*", + item_type_id: this.circRuleTrigger.item_type_id || "*", + patron_category_id: + this.circRuleTrigger.patron_category_id || "*", + }; + + const delay = + this.circRuleTrigger.delay === this.effectiveRule.delay + ? null + : this.circRuleTrigger.delay; + const notice = + this.circRuleTrigger.notice === this.effectiveRule.notice + ? null + : this.circRuleTrigger.notice; + const restrict = + this.circRuleTrigger.restrict === this.effectiveRule.restrict + ? null + : this.circRuleTrigger.restrict; + const mtt = + this.circRuleTrigger.mtt.join(",") === + this.effectiveRule.mtt.join(",") + ? null + : this.circRuleTrigger.mtt.join(","); + + const circRule = { + context, + }; + circRule[`overdue_${this.newTriggerNumber}_delay`] = delay; + circRule[`overdue_${this.newTriggerNumber}_notice`] = notice; + circRule[`overdue_${this.newTriggerNumber}_restrict`] = restrict; + circRule[`overdue_${this.newTriggerNumber}_mtt`] = mtt; + + if (this.editMode) { + Object.keys(circRule).forEach(key => { + if (key === "context") return; + if (!circRule[key]) delete circRule[key]; + }); + } + + const client = APIClient.circRule; + await client.circRules.update(circRule).then( + () => { + this.$router + .push({ name: "CirculationTriggersList" }) + .then(() => this.$router.go(0)); + }, + error => {} + ); + }, + async getLibraries() { + const client = APIClient.library; + await client.libraries.getAll().then( + libraries => { + libraries.unshift({ + library_id: "*", + name: "All libraries", + }); + this.libraries = libraries; + }, + error => {} + ); + }, + async getCategories() { + const client = APIClient.patron; + await client.patronCategories.getAll().then( + categories => { + categories.unshift({ + patron_category_id: "*", + name: "All categories", + }); + this.categories = categories; + }, + error => {} + ); + }, + async getItemTypes() { + const client = APIClient.item; + await client.itemTypes.getAll().then( + types => { + types.unshift({ + item_type_id: "*", + description: "All item types", + }); + this.itemTypes = types; + }, + error => {} + ); + }, + async handleContextChange() { + await this.checkForExistingRules(); + }, + async checkForExistingRules(routeParams) { + // We always pass library_id so we need to check for the existence of either item type or patron category + const editMode = routeParams && routeParams.triggerNumber; + if (editMode) { + this.editMode = editMode; + this.ruleBeingEdited = routeParams.triggerNumber; + } + const library_id = + routeParams && routeParams.library_id + ? routeParams.library_id + : this.circRuleTrigger.library_id || "*"; + const item_type_id = + routeParams && routeParams.item_type_id + ? routeParams.item_type_id + : this.circRuleTrigger.item_type_id || "*"; + const patron_category_id = + routeParams && routeParams.patron_category_id + ? routeParams.patron_category_id + : this.circRuleTrigger.patron_category_id || "*"; + const params = { + library_id, + item_type_id, + patron_category_id, + }; + + const client = APIClient.circRule; + await client.circRules.getAll({}, params).then( + rules => { + if (rules.length === 0) { + this.newTriggerNumber = 1; + } else { + const { rulesPerTrigger } = + this.splitCircRulesByTriggerNumber(rules); + this.circRules = rulesPerTrigger; + this.ruleInfo = { + issuelength: rules[0].issuelength, + decreaseloanholds: rules[0].decreaseloanholds, + fine: rules[0].fine, + chargeperiod: rules[0].chargeperiod, + lengthunit: rules[0].lengthunit, + }; + } + }, + error => {} + ); + + params.effective = false; + await client.circRules.getAll({}, params).then( + rules => { + if (rules.length === 0) { + this.newTriggerNumber = 1; + + this.assignTriggerValues( + "circRuleTrigger", + this.circRules, + this.newTriggerNumber, + params + ); + this.assignTriggerValues( + "effectiveRule", + this.circRules, + this.newTriggerNumber, + params + ); + } else { + const regex = /overdue_(\d+)_delay/g; + const numberOfTriggers = Object.keys(rules[0]).filter( + key => regex.test(key) && rules[0][key] !== null + ).length; + this.newTriggerNumber = editMode + ? routeParams.triggerNumber + : numberOfTriggers + 1; + const { rulesPerTrigger } = + this.splitCircRulesByTriggerNumber(rules); + const triggerToDisplay = editMode + ? routeParams.triggerNumber + : numberOfTriggers; + this.assignTriggerValues( + "circRuleTrigger", + rulesPerTrigger, + triggerToDisplay + ); + this.assignTriggerValues( + "effectiveRule", + rulesPerTrigger, + triggerToDisplay + ); + } + }, + error => {} + ); + }, + assignTriggerValues(prop, rules, triggerNumber, context = null) { + this[prop] = { + item_type_id: context + ? context.item_type_id + : rules[triggerNumber - 1].context.item_type_id || "*", + library_id: context + ? context.library_id + : rules[triggerNumber - 1].context.library_id || "*", + patron_category_id: context + ? context.patron_category_id + : rules[triggerNumber - 1].context.patron_category_id || + "*", + delay: rules[triggerNumber - 1][ + `overdue_${triggerNumber}_delay` + ], + notice: rules[triggerNumber - 1][ + `overdue_${triggerNumber}_notice` + ], + mtt: rules[triggerNumber - 1][`overdue_${triggerNumber}_mtt`] + ? rules[triggerNumber - 1][ + `overdue_${triggerNumber}_mtt` + ].split(",") + : [], + restrict: + rules[triggerNumber - 1][ + `overdue_${triggerNumber}_restrict` + ], + }; + }, + }, + watch: { + $route: { + immediate: true, + handler: function (newVal, oldVal) { + if ( + oldVal && + oldVal.query.triggerNumber && + newVal.query.triggerNumber !== oldVal.query.triggerNumber + ) { + this.$router.go(0); + } + }, + }, + }, + components: { TriggersTable, ButtonSubmit, TriggerContext }, +}; +</script> + +<style scoped> +.close_modal { + position: absolute; + top: 0.5rem; + right: 0.5rem; + font-size: 2rem; +} + +form li { + display: flex; + align-items: center; +} + +.dialog.alert + fieldset:not(.bg-danger):not(.bg-warning):not(.bg-info):not( + .bg-success + ):not(.bg-primary):not(.action), +.dialog.error + fieldset:not(.bg-danger):not(.bg-warning):not(.bg-info):not( + .bg-success + ):not(.bg-primary):not(.action) { + margin: 0; + background-color: rgba(255, 255, 255, 1); +} + +.router-link-active { + margin-left: 10px; +} +</style> diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersList.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersList.vue new file mode 100644 index 00000000000..06810ce0879 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/CirculationTriggersList.vue @@ -0,0 +1,258 @@ +<template> + <Toolbar> + <ToolbarButton + :to="{ + name: 'CirculationTriggersFormAdd', + query: { library_id: selectedLibrary }, + }" + icon="plus" + :title="$__('Add new trigger')" + /> + </Toolbar> + <div v-if="initialized"> + <h1>Circulation triggers</h1> + <div class="page-section bg-info"> + <p> + Rules are applied from most specific to less specific, using the + first found in this order: + </p> + <ul> + <li>same library, same patron category, same item type</li> + <li>same library, same patron category, all item types</li> + <li>same library, all patron categories, same item type</li> + <li>same library, all patron categories, all item types</li> + <li> + default (all libraries), same patron category, same item + type + </li> + <li> + default (all libraries), same patron category, all item + types + </li> + <li> + default (all libraries), all patron categories, same item + type + </li> + <li> + default (all libraries), all patron categories, all item + types + </li> + </ul> + </div> + <div class="page-section" v-if="initialized"> + <label for="library_select">{{ $__("Select a library") }}:</label> + <v-select + id="library_select" + v-model="selectedLibrary" + label="name" + :reduce="lib => lib.library_id" + :options="libraries" + @update:modelValue="handleLibrarySelection($event)" + > + <template #search="{ attributes, events }"> + <input + :required="!selectedLibrary" + class="vs__search" + v-bind="attributes" + v-on="events" + /> + </template> + </v-select> + </div> + </div> + <div v-if="initialized"> + <div id="circ_triggers_tabs" class="toptabs numbered"> + <ul class="nav nav-tabs" role="tablist"> + <li + v-for="(number, i) in numberOfTabs" + role="presentation" + v-bind:class=" + tabSelected === `Notice ${number}` ? 'active' : '' + " + :key="`noticeTab${i}`" + > + <a + href="#" + role="tab" + @click="changeTabContent" + :data-content="`Notice ${number}`" + >{{ $__("Trigger") + " " + number }}</a + > + </li> + </ul> + </div> + <div class="tab_content"> + <template v-for="(number, i) in numberOfTabs"> + <div + v-if="tabSelected === `Notice ${number}`" + :key="`noticeTabContent${i}`" + > + <TriggersTable + :circRules="circRules" + :triggerNumber="number" + :categories="categories" + :itemTypes="itemTypes" + /> + </div> + </template> + </div> + </div> + <div v-if="showModal" class="modal_centered"> + <div + class="modal-dialog modal-dialog-centered modal-lg" + role="document" + > + <router-view></router-view> + </div> + </div> +</template> + +<script> +import Toolbar from "../../Toolbar.vue"; +import ToolbarButton from "../../ToolbarButton.vue"; +import { APIClient } from "../../../fetch/api-client.js"; +import TriggersTable from "./TriggersTable.vue"; +import { inject } from "vue"; + +export default { + setup() { + const { splitCircRulesByTriggerNumber } = inject("circRulesStore"); + + return { + splitCircRulesByTriggerNumber, + }; + }, + data() { + return { + initialized: false, + libraries: null, + selectedLibrary: "*", + circRules: null, + numberOfTabs: [1], + tabSelected: "Notice 1", + showModal: false, + }; + }, + beforeRouteEnter(to, from, next) { + next(vm => { + vm.getLibraries().then(() => + vm + .getCategories() + .then(() => + vm + .getItemTypes() + .then(() => + vm + .getCircRules({}, true) + .then(() => (vm.initialized = true)) + ) + ) + ); + }); + }, + methods: { + async getLibraries() { + const libClient = APIClient.library; + await libClient.libraries.getAll().then( + libraries => { + libraries.unshift({ + library_id: "*", + name: "All libraries", + }); + this.libraries = libraries; + }, + error => {} + ); + }, + async getCategories() { + const client = APIClient.patron; + await client.patronCategories.getAll().then( + categories => { + categories.unshift({ + patron_category_id: "*", + name: "All", + }); + this.categories = categories; + }, + error => {} + ); + }, + async getItemTypes() { + const client = APIClient.item; + await client.itemTypes.getAll().then( + types => { + types.unshift({ + item_type_id: "*", + description: "All", + }); + this.itemTypes = types; + }, + error => {} + ); + }, + async getCircRules(params = {}, pageLoad) { + params.effective = false; + if (pageLoad) { + params.library_id = "*"; + } + const client = APIClient.circRule; + await client.circRules.getAll({}, params).then( + rules => { + const { numberOfTabs, rulesPerTrigger: circRules } = + this.splitCircRulesByTriggerNumber(rules); + this.numberOfTabs = numberOfTabs; + this.circRules = circRules; + }, + error => {} + ); + }, + async handleLibrarySelection(e) { + if (!e) e = ""; + await this.getCircRules({ library_id: e }); + }, + changeTabContent(e) { + this.tabSelected = e.target.getAttribute("data-content"); + }, + }, + watch: { + $route: { + immediate: true, + handler: function (newVal, oldVal) { + this.showModal = newVal.meta && newVal.meta.showModal; + }, + }, + }, + components: { TriggersTable, Toolbar, ToolbarButton }, +}; +</script> + +<style scoped> +.v-select { + display: inline-block; + background-color: white; + width: 30%; + margin-left: 10px; +} +.active { + cursor: pointer; +} +.toptabs { + margin-bottom: 0; +} + +.modal_centered { + position: fixed; + z-index: 9998; + display: table; + transition: opacity 0.3s ease; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33); +} +.modal-dialog { + overflow: auto; + height: 90%; +} +</style> diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/TriggerContext.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/TriggerContext.vue new file mode 100644 index 00000000000..1e9704dedbe --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/TriggerContext.vue @@ -0,0 +1,50 @@ +<template> + <div class="page-section"> + <table> + <thead> + <th> + {{ $__("Loan period") }} + </th> + <th> + {{ $__("Unit") }} + </th> + <th> + {{ $__("Decreased loan period for high holds") }} + </th> + <th> + {{ $__("Fine amount") }} + </th> + <th> + {{ $__("Fine charging interval") }} + </th> + </thead> + <tbody> + <tr> + <td> + {{ ruleInfo.issuelength }} + </td> + <td> + {{ ruleInfo.lengthunit }} + </td> + <td> + {{ ruleInfo.decreaseloanholds }} + </td> + <td> + {{ ruleInfo.fine }} + </td> + <td> + {{ ruleInfo.chargeperiod }} + </td> + </tr> + </tbody> + </table> + </div> +</template> + +<script> +export default { + props: ["ruleInfo"], +}; +</script> + +<style></style> diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/TriggersTable.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/TriggersTable.vue new file mode 100644 index 00000000000..798aab18452 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/Admin/CirculationTriggers/TriggersTable.vue @@ -0,0 +1,266 @@ +<template> + <div class="page-section"> + <table> + <thead> + <th v-if="!modal"> + {{ $__("Patron category") }} + </th> + <th v-if="!modal"> + {{ $__("Item type") }} + </th> + <th v-if="modal"> + {{ $__("Notice") }} + </th> + <th> + {{ $__("Delay") }} + </th> + <th> + {{ $__("Notice") }} + </th> + <th> + {{ $__("Email") }} + </th> + <th> + {{ $__("Print") }} + </th> + <th> + {{ $__("SMS") }} + </th> + <th> + {{ $__("Restricts checkouts") }} + </th> + <th v-if="!modal"> + {{ $__("Actions") }} + </th> + </thead> + <tbody> + <tr + v-for="(rule, i) in filterCircRulesByTabNumber( + triggerNumber + )" + v-bind:key="'rule' + i" + > + <td + v-if="!modal" + :class=" + modal && i + 1 === parseInt(ruleBeingEdited) + ? 'selected_rule' + : '' + " + > + {{ + handleContext( + rule.context.patron_category_id, + categories, + "patron_category_id" + ) + }} + </td> + <td + v-if="!modal" + :class=" + modal && i + 1 === parseInt(ruleBeingEdited) + ? 'selected_rule' + : '' + " + > + {{ + handleContext( + rule.context.item_type_id, + itemTypes, + "item_type_id", + "description" + ) + }} + </td> + <td + v-if="modal" + :class=" + modal && i + 1 === parseInt(ruleBeingEdited) + ? 'selected_rule' + : '' + " + > + {{ i + 1 }} + </td> + <td + :class=" + modal && i + 1 === parseInt(ruleBeingEdited) + ? 'selected_rule' + : '' + " + > + {{ + rule[ + "overdue_" + + (modal ? i + 1 : triggerNumber) + + "_delay" + ] + ? rule[ + "overdue_" + + (modal ? i + 1 : triggerNumber) + + "_delay" + ] + + " " + + $__("days") + : 0 + " " + $__("days") + }} + </td> + <td + :class=" + modal && i + 1 === parseInt(ruleBeingEdited) + ? 'selected_rule' + : '' + " + > + {{ + rule[ + "overdue_" + + (modal ? i + 1 : triggerNumber) + + "_notice" + ] + }} + </td> + <td + :class=" + modal && i + 1 === parseInt(ruleBeingEdited) + ? 'selected_rule' + : '' + " + > + {{ + handleTransport( + rule[ + "overdue_" + + (modal ? i + 1 : triggerNumber) + + "_mtt" + ], + "email" + ) + }} + </td> + <td + :class=" + modal && i + 1 === parseInt(ruleBeingEdited) + ? 'selected_rule' + : '' + " + > + {{ + handleTransport( + rule[ + "overdue_" + + (modal ? i + 1 : triggerNumber) + + "_mtt" + ], + "print" + ) + }} + </td> + <td + :class=" + modal && i + 1 === parseInt(ruleBeingEdited) + ? 'selected_rule' + : '' + " + > + {{ + handleTransport( + rule[ + "overdue_" + + (modal ? i + 1 : triggerNumber) + + "_mtt" + ], + "sms" + ) + }} + </td> + <td + :class=" + modal && i + 1 === parseInt(ruleBeingEdited) + ? 'selected_rule' + : '' + " + > + {{ + handleRestrictions( + rule[ + "overdue_" + + (modal ? i + 1 : triggerNumber) + + "_restrict" + ] + ) + }} + </td> + <td v-if="!modal" class="actions"> + <router-link + :to="{ + name: 'CirculationTriggersFormEdit', + query: { + library_id: rule.context.library_id, + item_type_id: rule.context.item_type_id, + patron_category_id: + rule.context.patron_category_id, + triggerNumber, + }, + }" + class="btn btn-default btn-xs" + ><i class="fa-solid fa-pencil"></i> + Edit</router-link + > + </td> + </tr> + </tbody> + </table> + </div> +</template> + +<script> +export default { + props: [ + "circRules", + "triggerNumber", + "modal", + "ruleBeingEdited", + "categories", + "itemTypes", + ], + methods: { + handleContext(value, data, type, displayProperty = "name") { + const item = data.find(item => item[type] === value); + return item[displayProperty]; + }, + handleTransport(value, type) { + return value + ? value.includes(type) + ? this.$__("Yes") + : this.$__("No") + : this.$__("Default"); + }, + handleRestrictions(value) { + return value === "1" ? this.$__("Yes") : this.$__("No"); + }, + filterCircRulesByTabNumber(number) { + if (this.modal) return this.circRules; + return this.circRules.filter( + rule => + rule.triggerNumber === number && + (rule[`overdue_${number}_delay`] || + rule[`overdue_${number}_notice`] || + rule[`overdue_${number}_mtt`] || + rule[`overdue_${number}_restrict`]) + ); + }, + }, +}; +</script> + +<style scoped> +.selected_rule { + background-color: yellow !important; +} + +.actions a { + margin-right: 5px; +} +</style> -- 2.48.1