Bug 30708: Vue app
This commit contains the main commit message The description of the original need is described in documents attached by the sponsor on the bug report specifically "[En] Preservation module - Main principles". The idea is to develop a whole new module to track the status of the documents that are sent for processings/treatments in order to preserve them (eg. covering). This is a first step, more are certainly coming later. The author and sponsors have worked for several months before providing this MVP version. The different discussion and needs can be found at https://tree.taiga.io/project/joubu-koha-preservation-module/kanban Some ideas of the next steps are also listed. The first iterations have been done using the classic .pl/.tt Koha style but we finally switched to a new Vue module, for more fun. These patches made the following main changes: New files * Koha objects under Koha/Preservation * REST API controllers under Koha/REST/V1/Preservation * preservation/home.pl and preservation/home.tt * Vue components under js/vue/components/Preservation * tests under t/db_dependent/Koha/Preservation and t/db_dependent/api/v1/preservation_* * Cypress tests under t/cypress/integration/Preservation DB: * 3 new sysprefs PreservationModule, PreservationNotForLoanWaitingListIn, PreservationNotForLoanDefaultTrainIn * 1 new permission "preservation" (will be split into subpermissions later) * 5 new tables: - preservation_processings - preservation_trains - preservation_processing_attributes - preservation_trains_items - preservation_processing_attributes_items Terminology and workflow: *Processings* are the different treatments an item can receive during its stay in the preservation module A *processing* is defined by a list of *attributes*. To make the module as easy to use for the librarians in charge of the preservation area a list of processings will be defined when the module will be set up. An *attribute* is a name and a value. That's it. However it also has a type, to define what the value is coming from: *free text*, *authorised value* or *database column*. For instance if you are defining a processing that will handling the book cover you could have 3 *attributes*: - first named "Barcode" that will be automatically filled with "items.barcode" (type *database column*" - second attribute named "color" linked with a new PRES_COLOR authorised value category you would have previously defined with "red", "blue", "green", etc. (type *authorised value*) - third attribute named "notes", because librarians like notes (type *free text*) Important: Even if the attribute is linked with a DB column or AV category, the value will be automatically pre filled but will stay editable (could be a config option to restrict the edition, later, if needed). The *status* of an item will change during the preservation process. First it will arrive in the preservation area and be on a *waiting list*. It is not processed already but is not available anymore for the patrons of the library. That's why we are going to use the "not for loan" (items.notforloan) value for this. This *waiting list* is a fictional concept, it simply lists all the items in the library with a specific *status*. A *train* is... how they call that at the BULAC, a train (same in French!). And we quite like the word so we kept it. It is what it is: a list of items/waggons, one after each other. We could have picked "cart", "list", but the concepts were already used in different places. We are not strongly attached to the term and it can be modified (but it's spread all over the code already and will be tedious to modify!) if you have a very good suggestion :) So, a *train* is where items are going after they have been sent to the waiting list. It's a stack of items that will be sent to a provider. When you create a new train you will be asked for the "Status for item added to this train", that will be the "not for loan" value to set to the items added to this train, and a "Default processing" that will be the processing used. But keep in mind that a train can have items that have different processings (specific case, will see later). When all items have been added to a *train*, you can *close* it. You cannot add items anymore to it! Then you can *send* it, and finally *receive* it. They are just statuses to keep track of the dates, and filter trains by status. However when a train is received you can *copy* an item to another (opened) train. It means that you have the item on hand but something went wrong, you are not happy with the work done by the supplier and want to send it back, so you create a new train (that can have different items, and it is the case where you will have items in a train that don't all have the same processing!). Test plan: A. Prerequisites 0. Just `reset_all` and jump to B, or: 1. Apache configuration You will need to edit /etc/koha/apache-shared-intranet-git.conf and add the following lines after the RewriteRule for erm (line.24?) RewriteCond %{REQUEST_URI} !^/cgi-bin/koha/preservation/.*.pl$ RewriteRule ^/cgi-bin/koha/preservation/.*$ /cgi-bin/koha/preservation/home.pl [PT] The RewriteCond is only useful if you are testing the "print slips" bugs as well, but it cannot hurt to have it! 2. `yarn js:build` to regenerate the Vue app for the preservation module 3. `updatedatabase` 4. `restart_all` B. Settings 0. Create 2 different values for NOTLOAN, eg. 'In preservation' and 'In preservation external' Create different authorised values for a new category, eg. PRES_COLORS: RED, BLUE, GREEN. Feel free to create more categories. 1. You can turn on the "PreservationModule" syspref and go to the Koha homepage to see a new "Preservation" link 2. You landed on the empty home page of the preservation, no worry! We need to fill this page with useful information! (see #2 on the kanban) 3. Go to settings 4. Set "Status for item added to waiting list" to "In preservation" and "Default status for item added to train": "In preservation external" Create a new processing and define some attributes. Ideally at least one of each type. 5. Go to "Waiting list" and add some items 6. Go to "Trains" and create several trains (at least 2). Notice that the "Status for item added to this train" value is set to the value defined in the settings, but can be modified. Notice that this status can be set when a train is created but it won't be possible to edit later. 7. Add items to a train. You can only add items that are already in the waiting list. Add values for the attributes. Notice that the attributes linked with a database column are automatically pre filled. Notice that attributes linked with an authorised value are displayed with a dropdown list but that a different value can be set (remember, this is a feature!). Notice that attributes can be multivalued. 8. Add other items to the waiting list, notice the "Add last X items to a train" link at the top of the waiting list table, click it 9. You can now add several items to a train, directly (for instance if you don't really need to pass through the waiting list). Values can be set for the batch, but attributes linked with a database column are not editable (they will be prefilled automatically) 10. Once you have a train with several items, look at the "show train" view and notice the item list. If all of them are using the same processing then a table is displayed, one column per attribute. However if at least one item of the train has a different processing then the items are not listed in a table. 11. Edit items and confirm that the values are correctly saved. 12. Close, send and receive a train 13. Once a train is closed you can no longer add items to it 14. Once a train is received notice that you can "copy" an item to another (opened) train QA notes: The patch is huge! New enhancements and improvements have been moved to separate bug reports but this cannot be split. We need a ground base to build on top. The size is mainly coming from Vue components, Koha::Objects, REST API controllers and specs, and tests. Nothing hard ;) More to come: - See the kanban! - Print slips (bug 33547 and bug 34030) - Put something on the landing page! - Link with the acquisition module (suppliers, funds, etc.) Sponsored-by: BULAC - http://www.bulac.fr/ Signed-off-by: BULAC - http://www.bulac.fr/ Signed-off-by: Heather Hernandez <heather_hernandez@nps.gov> Signed-off-by: Laurence Rault <laurence.rault@biblibre.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
parent
8972181484
commit
a639ec291f
21 changed files with 3085 additions and 0 deletions
|
@ -105,6 +105,12 @@ export default {
|
|||
this.$__("Delete") +
|
||||
"</a>"
|
||||
)
|
||||
} else if (action == "remove") {
|
||||
content.push(
|
||||
'<a class="remove btn btn-default btn-xs" role="button"><i class="fa fa-remove"></i> ' +
|
||||
this.$__("Remove") +
|
||||
"</a>"
|
||||
)
|
||||
}
|
||||
})
|
||||
return content.join(" ")
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<template></template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {},
|
||||
components: {},
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,194 @@
|
|||
<template>
|
||||
<div v-if="initialized && PreservationModule == 1">
|
||||
<Breadcrumb />
|
||||
<div class="main container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-push-2">
|
||||
<main>
|
||||
<Dialog />
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-2 col-sm-pull-10">
|
||||
<aside>
|
||||
<div id="navmenu">
|
||||
<div id="navmenulist">
|
||||
<h5>{{ $__("Preservation") }}</h5>
|
||||
<ul>
|
||||
<li>
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/home.pl"
|
||||
>
|
||||
<i class="fa fa-home"></i>
|
||||
{{ $__("Home") }}</router-link
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/trains"
|
||||
>
|
||||
<i class="fa fa-train"></i>
|
||||
{{ $__("Trains") }}</router-link
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/waiting-list"
|
||||
>
|
||||
<i class="fa fa-recycle"></i>
|
||||
{{ $__("Waiting list") }}
|
||||
</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/settings"
|
||||
>
|
||||
<i class="fa fa-cog"></i>
|
||||
{{ $__("Settings") }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main container-fluid" v-else>
|
||||
<Dialog />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inject } from "vue"
|
||||
import Breadcrumb from "../Breadcrumb.vue"
|
||||
import Dialog from "../Dialog.vue"
|
||||
import { APIClient } from "../../fetch/api-client.js"
|
||||
import "vue-select/dist/vue-select.css"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const AVStore = inject("AVStore")
|
||||
|
||||
const mainStore = inject("mainStore")
|
||||
|
||||
const { loading, loaded, setError } = mainStore
|
||||
|
||||
const PreservationStore = inject("PreservationStore")
|
||||
return {
|
||||
AVStore,
|
||||
loading,
|
||||
loaded,
|
||||
setError,
|
||||
PreservationStore,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialized: false,
|
||||
PreservationModule: null,
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
this.loading()
|
||||
|
||||
const fetch_config = () => {
|
||||
const sysprefs_client = APIClient.sysprefs
|
||||
const av_client = APIClient.authorised_values
|
||||
let promises = [
|
||||
sysprefs_client.sysprefs
|
||||
.get("PreservationNotForLoanWaitingListIn")
|
||||
.then(
|
||||
value => {
|
||||
this.PreservationStore.settings.not_for_loan_waiting_list_in =
|
||||
value.value
|
||||
},
|
||||
error => {}
|
||||
),
|
||||
sysprefs_client.sysprefs
|
||||
.get("PreservationNotForLoanDefaultTrainIn")
|
||||
.then(
|
||||
value => {
|
||||
this.PreservationStore.settings.not_for_loan_default_train_in =
|
||||
value.value
|
||||
},
|
||||
error => {}
|
||||
),
|
||||
av_client.values.get("NOT_LOAN").then(
|
||||
values => {
|
||||
this.AVStore.av_notforloan = values
|
||||
},
|
||||
error => {}
|
||||
),
|
||||
]
|
||||
|
||||
return Promise.all(promises)
|
||||
}
|
||||
|
||||
const sysprefs_client = APIClient.sysprefs
|
||||
sysprefs_client.sysprefs
|
||||
.get("PreservationModule")
|
||||
.then(value => {
|
||||
this.PreservationModule = value.value
|
||||
if (this.PreservationModule != 1) {
|
||||
return this.setError(
|
||||
this.$__(
|
||||
'The preservation module is disabled, turn on <a href="/cgi-bin/koha/admin/preferences.pl?tab=&op=search&searchfield=PreservationModule">PreservationModule</a> to use it'
|
||||
),
|
||||
false
|
||||
)
|
||||
}
|
||||
return fetch_config()
|
||||
})
|
||||
.then(() => {
|
||||
this.loaded()
|
||||
this.initialized = true
|
||||
})
|
||||
},
|
||||
|
||||
components: {
|
||||
Breadcrumb,
|
||||
Dialog,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#navmenulist a.router-link-active {
|
||||
font-weight: 700;
|
||||
}
|
||||
#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"]),
|
||||
textarea {
|
||||
border-color: rgba(60, 60, 60, 0.26);
|
||||
border-width: 1px;
|
||||
border-radius: 4px;
|
||||
min-width: 30%;
|
||||
}
|
||||
.flatpickr-input {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
#navmenulist ul li a.disabled {
|
||||
color: #666;
|
||||
pointer-events: none;
|
||||
font-weight: 700;
|
||||
}
|
||||
#navmenulist ul li a.disabled.router-link-active {
|
||||
color: #000;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,130 @@
|
|||
<template>
|
||||
<div v-if="!initialized">{{ $__("Loading") }}</div>
|
||||
<div v-else id="settings">
|
||||
<h2>
|
||||
{{ $__("Edit preservation settings") }}
|
||||
</h2>
|
||||
<div>
|
||||
<form @submit="onSubmit($event)">
|
||||
<fieldset class="rows">
|
||||
<legend>{{ $__("General settings") }}</legend>
|
||||
<ol>
|
||||
<li>
|
||||
<label
|
||||
class="required"
|
||||
for="not_for_loan_waiting_list_in"
|
||||
>{{
|
||||
$__(
|
||||
"Status for item added to waiting list"
|
||||
)
|
||||
}}:</label
|
||||
>
|
||||
<v-select
|
||||
id="not_for_loan_waiting_list_in"
|
||||
v-model="settings.not_for_loan_waiting_list_in"
|
||||
label="description"
|
||||
:reduce="av => av.value"
|
||||
:options="av_notforloan"
|
||||
>
|
||||
<template #search="{ attributes, events }">
|
||||
<input
|
||||
:required="
|
||||
!settings.not_for_loan_waiting_list_in
|
||||
"
|
||||
class="vs__search"
|
||||
v-bind="attributes"
|
||||
v-on="events"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
</li>
|
||||
<li>
|
||||
<label for="not_for_loan_default_train_in"
|
||||
>{{
|
||||
$__(
|
||||
"Default status for item added to train"
|
||||
)
|
||||
}}:</label
|
||||
>
|
||||
<v-select
|
||||
id="not_for_loan_default_train_in"
|
||||
v-model="settings.not_for_loan_default_train_in"
|
||||
label="description"
|
||||
:reduce="av => av.value"
|
||||
:options="av_notforloan"
|
||||
/>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Submit" />
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/home.pl"
|
||||
role="button"
|
||||
class="cancel"
|
||||
>{{ $__("Cancel") }}</router-link
|
||||
>
|
||||
</fieldset>
|
||||
</form>
|
||||
<SettingsProcessings />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inject } from "vue"
|
||||
import { APIClient } from "../../fetch/api-client.js"
|
||||
import { storeToRefs } from "pinia"
|
||||
import SettingsProcessings from "./SettingsProcessings.vue"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const AVStore = inject("AVStore")
|
||||
const { av_notforloan } = storeToRefs(AVStore)
|
||||
|
||||
const { setMessage, setWarning } = inject("mainStore")
|
||||
const PreservationStore = inject("PreservationStore")
|
||||
const { settings } = storeToRefs(PreservationStore)
|
||||
|
||||
return { av_notforloan, setMessage, setWarning, settings }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initialized: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkForm(train) {
|
||||
let errors = []
|
||||
|
||||
errors.forEach(function (e) {
|
||||
setWarning(e)
|
||||
})
|
||||
return !errors.length
|
||||
},
|
||||
onSubmit(e) {
|
||||
e.preventDefault()
|
||||
const client = APIClient.sysprefs
|
||||
client.sysprefs
|
||||
.update(
|
||||
"PreservationNotForLoanWaitingListIn",
|
||||
this.settings.not_for_loan_waiting_list_in
|
||||
)
|
||||
.then(
|
||||
client.sysprefs.update(
|
||||
"PreservationNotForLoanDefaultTrainIn",
|
||||
this.settings.not_for_loan_default_train_in || 0
|
||||
)
|
||||
)
|
||||
.then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Settings updated"), true)
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
},
|
||||
components: { SettingsProcessings },
|
||||
name: "Settings",
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,110 @@
|
|||
<template>
|
||||
<fieldset>
|
||||
<legend>{{ $__("Processings") }}</legend>
|
||||
<ol>
|
||||
<li
|
||||
:id="`processing_${counter}`"
|
||||
class="rows"
|
||||
v-for="(processing, counter) in processings"
|
||||
v-bind:key="counter"
|
||||
>
|
||||
<router-link
|
||||
:to="`/cgi-bin/koha/preservation/settings/processings/${processing.processing_id}`"
|
||||
>
|
||||
{{ processing.name }}
|
||||
</router-link>
|
||||
|
||||
<span class="action_links">
|
||||
<a @click="deleteProcessing(processing)"
|
||||
><i class="fa fa-trash"></i>
|
||||
{{ $__("Remove this processing") }}</a
|
||||
>
|
||||
|
||||
<router-link
|
||||
:to="`/cgi-bin/koha/preservation/settings/processings/edit/${processing.processing_id}`"
|
||||
><i class="fa fa-pencil"></i>
|
||||
{{ $__("Edit this processing") }}</router-link
|
||||
>
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/settings/processings/add"
|
||||
role="button"
|
||||
class="btn btn-default"
|
||||
><font-awesome-icon icon="plus" />
|
||||
{{ $__("Add new processing") }}</router-link
|
||||
>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inject } from "vue"
|
||||
import { APIClient } from "../../fetch/api-client.js"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const { setConfirmationDialog, setMessage } = inject("mainStore")
|
||||
return { setConfirmationDialog, setMessage }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
processings: [],
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
// FIXME Do we want that or a props passed from parent?
|
||||
const client = APIClient.preservation
|
||||
client.processings.getAll().then(
|
||||
processings => {
|
||||
this.processings = processings
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
deleteProcessing(processing) {
|
||||
this.setConfirmationDialog(
|
||||
{
|
||||
title: this.$__(
|
||||
"Are you sure you want to remove this processing?"
|
||||
),
|
||||
message: processing.name,
|
||||
accept_label: this.$__("Yes, delete"),
|
||||
cancel_label: this.$__("No, do not delete"),
|
||||
},
|
||||
() => {
|
||||
const client = APIClient.preservation
|
||||
client.processings.delete(processing.processing_id).then(
|
||||
success => {
|
||||
this.setMessage(
|
||||
this.$__("Processing %s deleted").format(
|
||||
processing.name
|
||||
),
|
||||
true
|
||||
)
|
||||
client.processings.getAll().then(
|
||||
processings => {
|
||||
this.processings = processings
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
props: {},
|
||||
name: "SettingsProcessings",
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.action_links a {
|
||||
padding-left: 0.2em;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,286 @@
|
|||
<template>
|
||||
<div v-if="!initialized">{{ $__("Loading") }}</div>
|
||||
<div v-else id="processings_add">
|
||||
<h2 v-if="processing.processing_id">
|
||||
{{ $__("Edit processing #%s").format(processing.processing_id) }}
|
||||
</h2>
|
||||
<h2 v-else>{{ $__("New processing") }}</h2>
|
||||
<div>
|
||||
<form @submit="onSubmit($event)">
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li>
|
||||
<label class="required" for="processing_name"
|
||||
>{{ $__("Processing name") }}:</label
|
||||
>
|
||||
<input
|
||||
id="processing_name"
|
||||
v-model="processing.name"
|
||||
:placeholder="$__('Processing name')"
|
||||
required
|
||||
/>
|
||||
<span class="required">{{ $__("Required") }}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="rows">
|
||||
<legend>{{ $__("Attributes") }}</legend>
|
||||
<fieldset
|
||||
:id="`attribute_${counter}`"
|
||||
class="rows"
|
||||
v-for="(attribute, counter) in processing.attributes"
|
||||
v-bind:key="counter"
|
||||
>
|
||||
<legend>
|
||||
{{ $__("Attribute %s").format(counter + 1) }}
|
||||
<a
|
||||
href="#"
|
||||
@click.prevent="deleteAttribute(counter)"
|
||||
><i class="fa fa-trash"></i>
|
||||
{{ $__("Remove this attribute") }}</a
|
||||
>
|
||||
</legend>
|
||||
<ol>
|
||||
<li>
|
||||
<label
|
||||
:for="`attribute_name_${counter}`"
|
||||
class="required"
|
||||
>{{ $__("Name") }}:
|
||||
</label>
|
||||
<input
|
||||
:id="`attribute_name_${counter}`"
|
||||
type="text"
|
||||
:name="`attribute_name_${counter}`"
|
||||
v-model="attribute.name"
|
||||
required
|
||||
/>
|
||||
<span class="required">{{
|
||||
$__("Required")
|
||||
}}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label
|
||||
:for="`attribute_type_${counter}`"
|
||||
class="required"
|
||||
>{{ $__("Type") }}:
|
||||
</label>
|
||||
<v-select
|
||||
:id="`attribute_type_${counter}`"
|
||||
v-model="attribute.type"
|
||||
:options="attribute_types"
|
||||
:reduce="o => o.code"
|
||||
@option:selected="
|
||||
attribute.option_source = null
|
||||
"
|
||||
>
|
||||
<template #search="{ attributes, events }">
|
||||
<input
|
||||
:required="!attribute.type"
|
||||
class="vs__search"
|
||||
v-bind="attributes"
|
||||
v-on="events"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
<span class="required">{{
|
||||
$__("Required")
|
||||
}}</span>
|
||||
</li>
|
||||
<li v-if="attribute.type == 'authorised_value'">
|
||||
<label
|
||||
:for="`attribute_option_${counter}`"
|
||||
class="required"
|
||||
>{{ $__("Options") }}:
|
||||
</label>
|
||||
<v-select
|
||||
:id="`attribute_option_${counter}`"
|
||||
v-model="attribute.option_source"
|
||||
:options="authorised_value_categories"
|
||||
>
|
||||
<template #search="{ attributes, events }">
|
||||
<input
|
||||
:required="!attribute.option_source"
|
||||
class="vs__search"
|
||||
v-bind="attributes"
|
||||
v-on="events"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
<span class="required">{{
|
||||
$__("Required")
|
||||
}}</span>
|
||||
</li>
|
||||
<li v-if="attribute.type == 'db_column'">
|
||||
<label
|
||||
:for="`attribute_option_${counter}`"
|
||||
class="required"
|
||||
>{{ $__("Options") }}:
|
||||
</label>
|
||||
<v-select
|
||||
:id="`attribute_option_${counter}`"
|
||||
v-model="attribute.option_source"
|
||||
:options="db_column_options"
|
||||
:reduce="o => o.code"
|
||||
>
|
||||
<template #search="{ attributes, events }">
|
||||
<input
|
||||
:required="!attribute.option_source"
|
||||
class="vs__search"
|
||||
v-bind="attributes"
|
||||
v-on="events"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
<span class="required">{{
|
||||
$__("Required")
|
||||
}}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<a class="btn btn-default" @click="addAttribute"
|
||||
><font-awesome-icon icon="plus" />
|
||||
{{ $__("Add new attribute") }}</a
|
||||
>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Submit" />
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/settings"
|
||||
role="button"
|
||||
class="cancel"
|
||||
>{{ $__("Cancel") }}</router-link
|
||||
>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inject } from "vue"
|
||||
import { APIClient } from "../../fetch/api-client.js"
|
||||
import { storeToRefs } from "pinia"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const AVStore = inject("AVStore")
|
||||
const {} = storeToRefs(AVStore)
|
||||
|
||||
const { setMessage, setWarning } = inject("mainStore")
|
||||
|
||||
const db_column_options = Object.keys(db_columns).map(function (c) {
|
||||
return { label: "%s (%s)".format(db_columns[c], c), code: c }
|
||||
})
|
||||
return {
|
||||
setMessage,
|
||||
setWarning,
|
||||
authorised_value_categories,
|
||||
db_column_options,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
processing: {
|
||||
processing_id: null,
|
||||
name: "",
|
||||
attributes: [],
|
||||
},
|
||||
attribute_types: [
|
||||
{
|
||||
label: this.$__("Authorized value"),
|
||||
code: "authorised_value",
|
||||
},
|
||||
{
|
||||
label: this.$__("Free text"),
|
||||
code: "free_text",
|
||||
},
|
||||
{
|
||||
label: this.$__("Database column"),
|
||||
code: "db_column",
|
||||
},
|
||||
],
|
||||
initialized: false,
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(vm => {
|
||||
if (to.params.processing_id) {
|
||||
vm.processing = vm.getProcessing(to.params.processing_id)
|
||||
} else {
|
||||
vm.initialized = true
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async getProcessing(processing_id) {
|
||||
const client = APIClient.preservation
|
||||
await client.processings.get(processing_id).then(
|
||||
processing => {
|
||||
this.processing = processing
|
||||
this.initialized = true
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
checkForm(processing) {
|
||||
let errors = []
|
||||
|
||||
let attributes = processing.attributes
|
||||
|
||||
errors.forEach(function (e) {
|
||||
setWarning(e)
|
||||
})
|
||||
|
||||
return !errors.length
|
||||
},
|
||||
onSubmit(e) {
|
||||
e.preventDefault()
|
||||
|
||||
let processing = JSON.parse(JSON.stringify(this.processing)) // copy
|
||||
let processing_id = processing.processing_id
|
||||
delete processing.processing_id
|
||||
|
||||
if (!this.checkForm(processing)) {
|
||||
return false
|
||||
}
|
||||
|
||||
processing.attributes = processing.attributes.map(
|
||||
({ processing_id, processing_attribute_id, ...keepAttrs }) =>
|
||||
keepAttrs
|
||||
)
|
||||
|
||||
const client = APIClient.preservation
|
||||
if (processing_id) {
|
||||
client.processings.update(processing, processing_id).then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Processing updated"))
|
||||
this.$router.push("/cgi-bin/koha/preservation/settings")
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
} else {
|
||||
client.processings.create(processing).then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Processing created"))
|
||||
this.$router.push("/cgi-bin/koha/preservation/settings")
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
},
|
||||
addAttribute() {
|
||||
this.processing.attributes.push({
|
||||
name: "",
|
||||
type: null,
|
||||
option_source: null,
|
||||
})
|
||||
},
|
||||
deleteAttribute(counter) {
|
||||
this.processing.attributes.splice(counter, 1)
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
name: "SettingsProcessingsFormAdd",
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,151 @@
|
|||
<template>
|
||||
<div v-if="!initialized">{{ $__("Loading") }}</div>
|
||||
<div v-else id="processing_show">
|
||||
<h2>
|
||||
{{ $__("Processing #%s").format(processing.processing_id) }}
|
||||
<span class="action_links">
|
||||
<router-link
|
||||
:to="`/cgi-bin/koha/preservation/settings/processings/edit/${processing.processing_id}`"
|
||||
:title="$__('Edit')"
|
||||
><i class="fa fa-pencil"></i
|
||||
></router-link>
|
||||
<a @click="doDelete()"><i class="fa fa-trash"></i></a>
|
||||
</span>
|
||||
</h2>
|
||||
<div>
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li>
|
||||
<label>{{ $__("Processing name") }}:</label>
|
||||
<span>
|
||||
{{ processing.name }}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="rows">
|
||||
<legend>{{ $__("Attributes") }}</legend>
|
||||
<ol v-if="processing.attributes.length">
|
||||
<li
|
||||
v-for="(attribute, counter) in processing.attributes"
|
||||
v-bind:key="counter"
|
||||
>
|
||||
<label>{{ attribute.name }}</label>
|
||||
<span v-if="attribute.type == 'authorised_value'">{{
|
||||
$__("Authorized value")
|
||||
}}</span>
|
||||
<span v-else-if="attribute.type == 'free_text'">{{
|
||||
$__("Free text")
|
||||
}}</span>
|
||||
<span v-else-if="attribute.type == 'db_column'">{{
|
||||
$__("Database column")
|
||||
}}</span>
|
||||
<span v-else
|
||||
>{{ $__("Unknown") }} - {{ attribute.type }}</span
|
||||
>
|
||||
</li>
|
||||
</ol>
|
||||
<span v-else>
|
||||
{{
|
||||
$__(
|
||||
"There are no attributes defined for this processing."
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
</fieldset>
|
||||
<fieldset class="action">
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/settings"
|
||||
role="button"
|
||||
class="cancel"
|
||||
>{{ $__("Close") }}</router-link
|
||||
>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inject } from "vue"
|
||||
import { APIClient } from "../../fetch/api-client.js"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const { setConfirmationDialog, setMessage } = inject("mainStore")
|
||||
|
||||
return {
|
||||
setConfirmationDialog,
|
||||
setMessage,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
processing: {
|
||||
processing_id: null,
|
||||
name: "",
|
||||
attributes: [],
|
||||
},
|
||||
initialized: false,
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(vm => {
|
||||
vm.getProcessing(to.params.processing_id)
|
||||
})
|
||||
},
|
||||
beforeRouteUpdate(to, from) {
|
||||
this.processing = this.getProcessing(to.params.processing_id)
|
||||
},
|
||||
methods: {
|
||||
async getProcessing(processing_id) {
|
||||
const client = APIClient.preservation
|
||||
await client.processings.get(processing_id).then(
|
||||
processing => {
|
||||
this.processing = processing
|
||||
this.initialized = true
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
doDelete: function () {
|
||||
this.setConfirmationDialog(
|
||||
{
|
||||
title: this.$__(
|
||||
"Are you sure you want to remove this processing?"
|
||||
),
|
||||
message: this.processing.name,
|
||||
accept_label: this.$__("Yes, delete"),
|
||||
cancel_label: this.$__("No, do not delete"),
|
||||
},
|
||||
() => {
|
||||
const client = APIClient.preservation
|
||||
client.processings
|
||||
.delete(this.processing.processing_id)
|
||||
.then(
|
||||
success => {
|
||||
this.setMessage(
|
||||
this.$__("Processing %s deleted").format(
|
||||
this.processing.name
|
||||
),
|
||||
true
|
||||
)
|
||||
this.$router.push(
|
||||
"/cgi-bin/koha/preservation/settings"
|
||||
)
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
name: "ProcessingsShow",
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.action_links a {
|
||||
padding-left: 0.2em;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,196 @@
|
|||
<template>
|
||||
<div v-if="!initialized">{{ $__("Loading") }}</div>
|
||||
<div v-else id="trains_add">
|
||||
<h2 v-if="train.train_id">
|
||||
{{ $__("Edit train #%s").format(train.train_id) }}
|
||||
</h2>
|
||||
<h2 v-else>{{ $__("New train") }}</h2>
|
||||
<div>
|
||||
<form @submit="onSubmit($event)">
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li>
|
||||
<label class="required" for="train_name"
|
||||
>{{ $__("name") }}:</label
|
||||
>
|
||||
<input
|
||||
id="train_name"
|
||||
v-model="train.name"
|
||||
:placeholder="$__('Name')"
|
||||
required
|
||||
/>
|
||||
<span class="required">{{ $__("Required") }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label class="required" for="train_description"
|
||||
>{{ $__("Description") }}:
|
||||
</label>
|
||||
<textarea
|
||||
id="train_description"
|
||||
v-model="train.description"
|
||||
:placeholder="$__('Description')"
|
||||
required
|
||||
rows="10"
|
||||
cols="50"
|
||||
/>
|
||||
<span class="required">{{ $__("Required") }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label for="not_for_loan_waiting_list_in"
|
||||
>{{
|
||||
$__("Status for item added to this train")
|
||||
}}:</label
|
||||
>
|
||||
<v-select
|
||||
id="not_for_loan"
|
||||
v-model="train.not_for_loan"
|
||||
label="description"
|
||||
:reduce="av => av.value"
|
||||
:options="av_notforloan"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<label
|
||||
class="required"
|
||||
for="train_default_processing"
|
||||
>{{ $__("Default processing") }}:
|
||||
</label>
|
||||
<v-select
|
||||
id="train_default_processing"
|
||||
label="name"
|
||||
v-model="train.default_processing_id"
|
||||
:reduce="p => p.processing_id"
|
||||
:options="processings"
|
||||
:required="!train.default_processing_id"
|
||||
>
|
||||
<template #search="{ attributes, events }">
|
||||
<input
|
||||
:required="!train.default_processing_id"
|
||||
class="vs__search"
|
||||
v-bind="attributes"
|
||||
v-on="events"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Submit" />
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/trains"
|
||||
role="button"
|
||||
class="cancel"
|
||||
>{{ $__("Cancel") }}</router-link
|
||||
>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inject } from "vue"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { APIClient } from "../../fetch/api-client.js"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const AVStore = inject("AVStore")
|
||||
const { av_notforloan } = storeToRefs(AVStore)
|
||||
|
||||
const { setMessage, setWarning } = inject("mainStore")
|
||||
|
||||
const PreservationStore = inject("PreservationStore")
|
||||
const { settings } = storeToRefs(PreservationStore)
|
||||
|
||||
return { av_notforloan, setMessage, setWarning, settings }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
train: {
|
||||
train_id: null,
|
||||
name: "",
|
||||
description: "",
|
||||
not_for_loan: this.settings.not_for_loan_default_train_in,
|
||||
default_processing_id: null,
|
||||
created_on: null,
|
||||
closed_on: null,
|
||||
sent_on: null,
|
||||
received_on: null,
|
||||
},
|
||||
processings: [],
|
||||
initialized: false,
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(vm => {
|
||||
if (to.params.train_id) {
|
||||
vm.train = vm.getTrain(to.params.train_id)
|
||||
} else {
|
||||
vm.initialized = true
|
||||
}
|
||||
})
|
||||
},
|
||||
beforeCreate() {
|
||||
const client = APIClient.preservation
|
||||
client.processings.getAll().then(
|
||||
processings => {
|
||||
this.processings = processings
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
async getTrain(train_id) {
|
||||
const client = APIClient.preservation
|
||||
client.trains.get(train_id).then(train => {
|
||||
this.train = train
|
||||
this.initialized = true
|
||||
})
|
||||
},
|
||||
checkForm(train) {
|
||||
let errors = []
|
||||
|
||||
errors.forEach(function (e) {
|
||||
setWarning(e)
|
||||
})
|
||||
return !errors.length
|
||||
},
|
||||
onSubmit(e) {
|
||||
e.preventDefault()
|
||||
|
||||
let train = JSON.parse(JSON.stringify(this.train)) // copy
|
||||
let train_id = train.train_id
|
||||
if (!this.checkForm(train)) {
|
||||
return false
|
||||
}
|
||||
|
||||
delete train.train_id
|
||||
delete train.default_processing
|
||||
delete train.items
|
||||
|
||||
const client = APIClient.preservation
|
||||
if (train_id) {
|
||||
client.trains.update(train, train_id).then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Train updated"))
|
||||
this.$router.push("/cgi-bin/koha/preservation/trains")
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
} else {
|
||||
client.trains.create(train).then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Train created"))
|
||||
this.$router.push("/cgi-bin/koha/preservation/trains")
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
name: "TrainsFormAdd",
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,327 @@
|
|||
<template>
|
||||
<div v-if="!initialized">{{ $__("Loading") }}</div>
|
||||
<div v-else id="trains_add_item">
|
||||
<h2 v-if="train_item && train_item.train_item_id">
|
||||
{{ $__("Edit item #%s").format(train_item.item_id) }}
|
||||
</h2>
|
||||
<h2 v-else>{{ $__("Add new item to %s").format(train.name) }}</h2>
|
||||
<div v-if="train_item">
|
||||
<form @submit="onSubmit($event)">
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li>
|
||||
<label for="itemnumber"
|
||||
>{{ $__("Itemnumber") }}:</label
|
||||
>
|
||||
<span>{{ train_item.item_id }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label for="processing"
|
||||
>{{ $__("Processing") }}:
|
||||
</label>
|
||||
<v-select
|
||||
id="processing"
|
||||
label="name"
|
||||
v-model="train_item.processing_id"
|
||||
@option:selected="refreshAttributes(1)"
|
||||
:reduce="p => p.processing_id"
|
||||
:options="processings"
|
||||
:clearable="false"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
class="attribute"
|
||||
v-for="(attribute, counter) in attributes"
|
||||
v-bind:key="counter"
|
||||
>
|
||||
<label :for="`attribute_${counter}`"
|
||||
>{{ attribute.name }}:
|
||||
</label>
|
||||
<span v-if="attribute.type == 'authorised_value'">
|
||||
<v-select
|
||||
:id="`attribute_${counter}`"
|
||||
v-model="attribute.value"
|
||||
label="description"
|
||||
:reduce="av => av.value"
|
||||
:options="
|
||||
av_options[attribute.option_source]
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
<span v-else-if="attribute.type == 'free_text'">
|
||||
<input
|
||||
:id="`attribute_${counter}`"
|
||||
v-model="attribute.value"
|
||||
/>
|
||||
</span>
|
||||
<span v-else-if="attribute.type == 'db_column'">
|
||||
<input
|
||||
:id="`attribute_${counter}`"
|
||||
v-model="attribute.value"
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Submit" />
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/trains"
|
||||
role="button"
|
||||
class="cancel"
|
||||
>{{ $__("Cancel") }}</router-link
|
||||
>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div v-else>
|
||||
<form @submit="getItemFromWaitingList($event)">
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li>
|
||||
<label class="required" for="barcode"
|
||||
>{{ $__("barcode") }}:</label
|
||||
>
|
||||
<input
|
||||
id="barcode"
|
||||
v-model="barcode"
|
||||
:placeholder="$__('barcode')"
|
||||
required
|
||||
/>
|
||||
<span class="required">{{ $__("Required") }}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Submit" />
|
||||
<router-link
|
||||
:to="`/cgi-bin/koha/preservation/trains/${train.train_id}`"
|
||||
role="button"
|
||||
class="cancel"
|
||||
>{{ $__("Cancel") }}</router-link
|
||||
>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inject } from "vue"
|
||||
import { APIClient } from "../../fetch/api-client"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const { setMessage, setWarning, loading, loaded } = inject("mainStore")
|
||||
return {
|
||||
setMessage,
|
||||
setWarning,
|
||||
loading,
|
||||
loaded,
|
||||
api_mappings,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
train: {
|
||||
train_id: null,
|
||||
name: "",
|
||||
description: "",
|
||||
},
|
||||
item: { item_id: null },
|
||||
train_item: null,
|
||||
barcode: "",
|
||||
processings: [],
|
||||
processing: null,
|
||||
initialized: false,
|
||||
av_options: {},
|
||||
default_values: {},
|
||||
attributes: [],
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
const client = APIClient.preservation
|
||||
client.processings
|
||||
.getAll()
|
||||
.then(processings => (this.processings = processings))
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(vm => {
|
||||
if (to.params.train_item_id) {
|
||||
vm.train = vm
|
||||
.getTrain(to.params.train_id)
|
||||
.then(() =>
|
||||
vm
|
||||
.getTrainItem(
|
||||
to.params.train_id,
|
||||
to.params.train_item_id
|
||||
)
|
||||
.then(() =>
|
||||
vm
|
||||
.refreshAttributes()
|
||||
.then(() => (vm.initialized = true))
|
||||
)
|
||||
)
|
||||
} else {
|
||||
vm.train = vm
|
||||
.getTrain(to.params.train_id)
|
||||
.then(() => (vm.initialized = true))
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async getTrain(train_id) {
|
||||
const client = APIClient.preservation
|
||||
await client.trains.get(train_id).then(
|
||||
train => {
|
||||
this.train = train
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
async getTrainItem(train_id, train_item_id) {
|
||||
const client = APIClient.preservation
|
||||
await client.train_items.get(train_id, train_item_id).then(
|
||||
train_item => {
|
||||
this.train_item = train_item
|
||||
this.item = train_item.catalogue_item
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
async getItemFromWaitingList(e) {
|
||||
e.preventDefault()
|
||||
const client = APIClient.preservation
|
||||
client.waiting_list_items.get_from_barcode(this.barcode).then(
|
||||
item => {
|
||||
if (!item) {
|
||||
this.setWarning(
|
||||
this.$__(
|
||||
"Cannot find item with this barcode. It must be in the waiting list."
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
this.item = item
|
||||
this.train_item = {
|
||||
item_id: item.item_id,
|
||||
processing_id: this.train.default_processing_id,
|
||||
}
|
||||
this.refreshAttributes(1)
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
columnApiMapping(db_column) {
|
||||
let table_col = db_column.split(".")
|
||||
let table = table_col[0]
|
||||
let col = table_col[1]
|
||||
let api_attribute = this.api_mappings[table][col] || col
|
||||
return table == "biblio" || table == "biblioitems"
|
||||
? this.item.biblio[api_attribute]
|
||||
: this.item[api_attribute]
|
||||
},
|
||||
updateDefaultValues() {
|
||||
this.processing.attributes
|
||||
.filter(attribute => attribute.type == "db_column")
|
||||
.forEach(attribute => {
|
||||
this.default_values[attribute.processing_attribute_id] =
|
||||
this.columnApiMapping(attribute.option_source)
|
||||
})
|
||||
},
|
||||
async refreshAttributes(apply_default_value) {
|
||||
this.loading()
|
||||
|
||||
const client = APIClient.preservation
|
||||
await client.processings.get(this.train_item.processing_id).then(
|
||||
processing => (this.processing = processing),
|
||||
error => {}
|
||||
)
|
||||
this.updateDefaultValues()
|
||||
this.attributes = this.processing.attributes.map(attribute => {
|
||||
let value = ""
|
||||
if (!apply_default_value) {
|
||||
let existing_attribute = this.train_item.attributes.find(
|
||||
a =>
|
||||
a.processing_attribute_id ==
|
||||
attribute.processing_attribute_id
|
||||
)
|
||||
if (existing_attribute) {
|
||||
value = existing_attribute.value
|
||||
}
|
||||
} else if (attribute.type == "db_column") {
|
||||
value =
|
||||
this.default_values[attribute.processing_attribute_id]
|
||||
}
|
||||
return {
|
||||
processing_attribute_id: attribute.processing_attribute_id,
|
||||
name: attribute.name,
|
||||
type: attribute.type,
|
||||
option_source: attribute.option_source,
|
||||
value,
|
||||
}
|
||||
})
|
||||
const client_av = APIClient.authorised_values
|
||||
let av_cat_array = this.processing.attributes
|
||||
.filter(attribute => attribute.type == "authorised_value")
|
||||
.map(attribute => attribute.option_source)
|
||||
|
||||
client_av.values
|
||||
.getCategoriesWithValues([
|
||||
...new Set(av_cat_array.map(av_cat => '"' + av_cat + '"')),
|
||||
]) // unique
|
||||
.then(av_categories => {
|
||||
av_cat_array.forEach(av_cat => {
|
||||
let av_match = av_categories.find(
|
||||
element => element.category_name == av_cat
|
||||
)
|
||||
this.av_options[av_cat] = av_match.authorised_values
|
||||
})
|
||||
})
|
||||
.then(() => this.loaded())
|
||||
},
|
||||
onSubmit(e) {
|
||||
e.preventDefault()
|
||||
|
||||
let train_item_id = this.train_item.train_item_id
|
||||
let train_item = {
|
||||
item_id: this.train_item.item_id,
|
||||
processing_id: this.train_item.processing_id,
|
||||
attributes: this.attributes.map(a => ({
|
||||
processing_attribute_id: a.processing_attribute_id,
|
||||
value: a.value,
|
||||
})),
|
||||
}
|
||||
|
||||
const client = APIClient.preservation
|
||||
if (train_item_id) {
|
||||
client.train_items
|
||||
.update(train_item, this.train.train_id, train_item_id)
|
||||
.then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Item updated"))
|
||||
this.$router.push(
|
||||
"/cgi-bin/koha/preservation/trains/" +
|
||||
this.train.train_id
|
||||
)
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
} else {
|
||||
client.train_items.create(train_item, this.train.train_id).then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Item added to train"))
|
||||
this.$router.push(
|
||||
"/cgi-bin/koha/preservation/trains/" +
|
||||
this.train.train_id
|
||||
)
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
name: "TrainsFormAdd",
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,270 @@
|
|||
<template>
|
||||
<div v-if="!initialized">{{ $__("Loading") }}</div>
|
||||
<div v-else id="trains_list">
|
||||
<Toolbar />
|
||||
<fieldset v-if="count_trains > 0" class="filters">
|
||||
<label>{{ $__("Filter by") }}:</label>
|
||||
<input
|
||||
type="radio"
|
||||
id="all_status_filter"
|
||||
v-model="filters.status"
|
||||
value=""
|
||||
/><label for="all_status_filter">{{ $__("All") }}</label>
|
||||
<input
|
||||
type="radio"
|
||||
id="closed_status_filter"
|
||||
v-model="filters.status"
|
||||
value="closed"
|
||||
/><label for="closed_status_filter">{{ $__("Closed") }}</label>
|
||||
<input
|
||||
type="radio"
|
||||
id="sent_status_filter"
|
||||
v-model="filters.status"
|
||||
value="sent"
|
||||
/><label for="sent_status_filter">{{ $__("Sent") }}</label>
|
||||
<input
|
||||
type="radio"
|
||||
id="received_status_filter"
|
||||
v-model="filters.status"
|
||||
value="received"
|
||||
/><label for="received_status_filter">{{ $__("Received") }}</label>
|
||||
<input
|
||||
@click="filter_table"
|
||||
id="filter_table"
|
||||
type="button"
|
||||
:value="$__('Filter')"
|
||||
/>
|
||||
</fieldset>
|
||||
<div v-if="count_trains > 0" class="page-section">
|
||||
<KohaTable
|
||||
ref="table"
|
||||
v-bind="tableOptions"
|
||||
@show="doShow"
|
||||
@edit="doEdit"
|
||||
@delete="doDelete"
|
||||
@addItems="doAddItems"
|
||||
></KohaTable>
|
||||
</div>
|
||||
|
||||
<div v-else class="dialog message">
|
||||
{{ $__("There are no trains defined") }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import flatPickr from "vue-flatpickr-component"
|
||||
import Toolbar from "./TrainsToolbar.vue"
|
||||
import { inject, ref, reactive } from "vue"
|
||||
import { APIClient } from "../../fetch/api-client"
|
||||
import { build_url } from "../../composables/datatables"
|
||||
import KohaTable from "../KohaTable.vue"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const AVStore = inject("AVStore")
|
||||
const { get_lib_from_av, map_av_dt_filter } = AVStore
|
||||
const { setConfirmationDialog, setMessage } = inject("mainStore")
|
||||
const table = ref()
|
||||
const filters = reactive({ status: "" })
|
||||
return {
|
||||
get_lib_from_av,
|
||||
map_av_dt_filter,
|
||||
setConfirmationDialog,
|
||||
setMessage,
|
||||
table,
|
||||
filters,
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
this.filters.status = this.$route.query.status || ""
|
||||
return {
|
||||
fp_config: flatpickr_defaults,
|
||||
count_trains: 0,
|
||||
initialized: false,
|
||||
tableOptions: {
|
||||
columns: this.getTableColumns(),
|
||||
url: this.table_url,
|
||||
add_filters: true,
|
||||
actions: {
|
||||
0: ["show"],
|
||||
"-1": [
|
||||
"edit",
|
||||
"delete",
|
||||
{
|
||||
addItems: {
|
||||
text: this.$__("Add items"),
|
||||
icon: "fa fa-plus",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(vm => {
|
||||
vm.getCountTrains()
|
||||
})
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
async getCountTrains() {
|
||||
const client = APIClient.preservation
|
||||
client.trains.count().then(
|
||||
count => {
|
||||
this.count_trains = count
|
||||
this.initialized = true
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
doShow: function (train, dt, event) {
|
||||
event.preventDefault()
|
||||
this.$router.push(
|
||||
"/cgi-bin/koha/preservation/trains/" + train.train_id
|
||||
)
|
||||
},
|
||||
doEdit: function (train, dt, event) {
|
||||
this.$router.push(
|
||||
"/cgi-bin/koha/preservation/trains/edit/" + train.train_id
|
||||
)
|
||||
},
|
||||
doDelete: function (train, dt, event) {
|
||||
this.setConfirmationDialog(
|
||||
{
|
||||
title: this.$__(
|
||||
"Are you sure you want to remove this train?"
|
||||
),
|
||||
message: train.name,
|
||||
accept_label: this.$__("Yes, delete"),
|
||||
cancel_label: this.$__("No, do not delete"),
|
||||
},
|
||||
() => {
|
||||
const client = APIClient.preservation
|
||||
client.trains.delete(train.train_id).then(
|
||||
success => {
|
||||
this.setMessage(
|
||||
this.$__("Train %s deleted").format(train.name),
|
||||
true
|
||||
)
|
||||
dt.draw()
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
doAddItems: function (train, dt, event) {
|
||||
this.$router.push(
|
||||
"/cgi-bin/koha/preservation/trains/" +
|
||||
train.train_id +
|
||||
"/items/add"
|
||||
)
|
||||
},
|
||||
table_url() {
|
||||
let url = "/api/v1/preservation/trains"
|
||||
let q
|
||||
if (this.filters.status == "closed") {
|
||||
q = {
|
||||
"me.closed_on": { "!=": null },
|
||||
"me.sent_on": null,
|
||||
"me.received_on": null,
|
||||
}
|
||||
} else if (this.filters.status == "sent") {
|
||||
q = {
|
||||
"me.closed_on": { "!=": null },
|
||||
"me.sent_on": { "!=": null },
|
||||
"me.received_on": null,
|
||||
}
|
||||
} else if (this.filters.status == "received") {
|
||||
q = {
|
||||
"me.closed_on": { "!=": null },
|
||||
"me.sent_on": { "!=": null },
|
||||
"me.received_on": { "!=": null },
|
||||
}
|
||||
}
|
||||
if (q) {
|
||||
url += "?" + new URLSearchParams({ q: JSON.stringify(q) })
|
||||
}
|
||||
|
||||
return url
|
||||
},
|
||||
filter_table: async function () {
|
||||
let new_route = build_url(
|
||||
"/cgi-bin/koha/preservation/trains",
|
||||
this.filters
|
||||
)
|
||||
this.$router.push(new_route)
|
||||
if (this.$refs.table) {
|
||||
this.$refs.table.redraw(this.table_url())
|
||||
}
|
||||
},
|
||||
getTableColumns: function () {
|
||||
let escape_str = this.escape_str
|
||||
return [
|
||||
{
|
||||
title: __("Name"),
|
||||
data: "me.train_id:me.name",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return `<a href="/cgi-bin/koha/preservation/trains/${row.train_id}" class="show">${row.name} (#${row.train_id})</a>`
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Created on"),
|
||||
data: "created_on",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return $date(row.created_on)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Closed on"),
|
||||
data: "closed_on",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return $date(row.closed_on)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Sent on"),
|
||||
data: "sent_on",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return $date(row.sent_on)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Received on"),
|
||||
data: "received_on",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return $date(row.received_on)
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
components: { flatPickr, Toolbar, KohaTable },
|
||||
name: "trainsList",
|
||||
emits: ["select-train", "close"],
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#train_list {
|
||||
display: table;
|
||||
}
|
||||
.filters > input[type="radio"] {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
.filters > input[type="button"] {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,548 @@
|
|||
<template>
|
||||
<transition name="modal">
|
||||
<div v-if="show_modal" class="modal">
|
||||
<h2>{{ $__("Copy item to the following train") }}</h2>
|
||||
<form @submit="copyItem($event)">
|
||||
<div class="page-section">
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li>
|
||||
<label class="required" for="train_list"
|
||||
>{{ $__("Select a train") }}:</label
|
||||
>
|
||||
<v-select
|
||||
v-model="train_id_selected_for_copy"
|
||||
label="name"
|
||||
:options="train_list"
|
||||
:reduce="t => t.train_id"
|
||||
>
|
||||
<template #search="{ attributes, events }">
|
||||
<input
|
||||
:required="
|
||||
!train_id_selected_for_copy
|
||||
"
|
||||
class="vs__search"
|
||||
v-bind="attributes"
|
||||
v-on="events"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
<span class="required">{{
|
||||
$__("Required")
|
||||
}}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Copy" />
|
||||
<input
|
||||
type="button"
|
||||
@click="show_modal = false"
|
||||
:value="$__('Close')"
|
||||
/>
|
||||
</fieldset>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</transition>
|
||||
<div v-if="!initialized">{{ $__("Loading") }}</div>
|
||||
<div v-else id="trains_show">
|
||||
<div id="toolbar" class="btn-toolbar">
|
||||
<router-link
|
||||
:to="`/cgi-bin/koha/preservation/trains/${train.train_id}/items/add`"
|
||||
class="btn btn-default"
|
||||
><font-awesome-icon icon="plus" />
|
||||
{{ $__("Add items") }}</router-link
|
||||
>
|
||||
<router-link
|
||||
:to="`/cgi-bin/koha/preservation/trains/edit/${train.train_id}`"
|
||||
class="btn btn-default"
|
||||
><font-awesome-icon icon="pencil" />
|
||||
{{ $__("Edit") }}</router-link
|
||||
>
|
||||
<a @click="deleteTrain(train)" class="btn btn-default"
|
||||
><font-awesome-icon icon="trash" /> {{ $__("Delete") }}</a
|
||||
>
|
||||
<a
|
||||
v-if="!train.closed_on"
|
||||
class="btn btn-default"
|
||||
@click="closeTrain"
|
||||
><font-awesome-icon icon="remove" /> {{ $__("Close") }}</a
|
||||
>
|
||||
<a
|
||||
v-else-if="!train.sent_on"
|
||||
class="btn btn-default"
|
||||
@click="sendTrain"
|
||||
><font-awesome-icon icon="paper-plane" /> {{ $__("Send") }}</a
|
||||
>
|
||||
<a
|
||||
v-else-if="!train.received_on"
|
||||
class="btn btn-default"
|
||||
@click="receiveTrain"
|
||||
><font-awesome-icon icon="inbox" /> {{ $__("Receive") }}</a
|
||||
>
|
||||
</div>
|
||||
<h2>
|
||||
{{ $__("Train #%s").format(train.train_id) }}
|
||||
</h2>
|
||||
<div>
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li>
|
||||
<label>{{ $__("Name") }}:</label>
|
||||
<span>
|
||||
{{ train.name }}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>{{ $__("Description") }}:</label>
|
||||
<span>
|
||||
{{ train.description }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="train.closed_on">
|
||||
<label>{{ $__("Closed on") }}:</label>
|
||||
<span>
|
||||
{{ format_date(train.closed_on) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="train.sent_on">
|
||||
<label>{{ $__("Sent on") }}:</label>
|
||||
<span>
|
||||
{{ format_date(train.sent_on) }}
|
||||
</span>
|
||||
</li>
|
||||
<li v-if="train.received_on">
|
||||
<label>{{ $__("Received on") }}:</label>
|
||||
<span>
|
||||
{{ format_date(train.received_on) }}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<label
|
||||
>{{
|
||||
$__("Status for item added to this train")
|
||||
}}:</label
|
||||
>
|
||||
<span>{{
|
||||
get_lib_from_av("av_notforloan", train.not_for_loan)
|
||||
}}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label>{{ $__("Default processing") }}:</label>
|
||||
<span>
|
||||
{{ train.default_processing.name }}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset v-if="train.items.length" class="rows">
|
||||
<legend>{{ $__("Items") }}</legend>
|
||||
<table v-if="item_table.display" :id="table_id"></table>
|
||||
<ol v-else>
|
||||
<li
|
||||
:id="`item_${counter}`"
|
||||
class="rows"
|
||||
v-for="(item, counter) in train.items"
|
||||
v-bind:key="counter"
|
||||
>
|
||||
<!-- FIXME Counter here may change, we should pass an order by clause when retrieving the items -->
|
||||
<label
|
||||
>{{ counter + 1 }}
|
||||
<span class="action_links">
|
||||
<router-link
|
||||
:to="`/cgi-bin/koha/preservation/trains/${train.train_id}/items/edit/${item.train_item_id}`"
|
||||
:title="$__('Edit')"
|
||||
><i class="fa fa-pencil"></i></router-link
|
||||
></span>
|
||||
</label>
|
||||
<div class="attributes_values">
|
||||
<span
|
||||
:id="`attribute_${counter_attribute}`"
|
||||
class="attribute_value"
|
||||
v-for="(
|
||||
attribute, counter_attribute
|
||||
) in item.attributes"
|
||||
v-bind:key="counter_attribute"
|
||||
>
|
||||
<!-- FIXME We need to display the description of the AV here -->
|
||||
{{ attribute.processing_attribute.name }}={{
|
||||
attribute.value
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="action">
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/trains"
|
||||
role="button"
|
||||
class="cancel"
|
||||
>{{ $__("Close") }}</router-link
|
||||
>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { inject, createVNode, render } from "vue"
|
||||
import { APIClient } from "../../fetch/api-client"
|
||||
import { useDataTable } from "../../composables/datatables"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const format_date = $date
|
||||
|
||||
const AVStore = inject("AVStore")
|
||||
const { get_lib_from_av } = AVStore
|
||||
|
||||
const { setConfirmationDialog, setMessage } = inject("mainStore")
|
||||
|
||||
const table_id = "item_list"
|
||||
useDataTable(table_id)
|
||||
|
||||
return {
|
||||
format_date,
|
||||
get_lib_from_av,
|
||||
table_id,
|
||||
setConfirmationDialog,
|
||||
setMessage,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
train: {
|
||||
train_id: null,
|
||||
name: "",
|
||||
description: "",
|
||||
},
|
||||
initialized: false,
|
||||
show_modal: false,
|
||||
item_table: {
|
||||
display: false,
|
||||
data: [],
|
||||
columns: [],
|
||||
},
|
||||
train_list: [],
|
||||
train_id_selected_for_copy: null,
|
||||
train_item_id_to_copy: null,
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(vm => {
|
||||
vm.getTrain(to.params.train_id).then(() => vm.build_datatable())
|
||||
vm.getTrainList()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async getTrain(train_id) {
|
||||
const client = APIClient.preservation
|
||||
await client.trains.get(train_id).then(
|
||||
train => {
|
||||
this.train = train
|
||||
let display = this.train.items.every(
|
||||
item =>
|
||||
item.processing_id ==
|
||||
this.train.default_processing_id
|
||||
)
|
||||
if (display) {
|
||||
this.item_table.data = []
|
||||
this.train.items.forEach(item => {
|
||||
let item_row = {}
|
||||
this.train.default_processing.attributes.forEach(
|
||||
attribute => {
|
||||
if (item.attributes.length <= 0) return ""
|
||||
item_row[
|
||||
attribute.processing_attribute_id
|
||||
] = item.attributes.find(
|
||||
a =>
|
||||
a.processing_attribute_id ==
|
||||
attribute.processing_attribute_id
|
||||
).value
|
||||
}
|
||||
)
|
||||
item_row.item = item
|
||||
this.item_table.data.push(item_row)
|
||||
})
|
||||
this.item_table.columns = []
|
||||
this.item_table.columns.push({
|
||||
name: "id",
|
||||
title: this.$__("ID"),
|
||||
render: (data, type, row) => {
|
||||
return 42
|
||||
},
|
||||
})
|
||||
train.default_processing.attributes.forEach(a =>
|
||||
this.item_table.columns.push({
|
||||
name: a.name,
|
||||
title: a.name,
|
||||
data: a.processing_attribute_id,
|
||||
})
|
||||
)
|
||||
this.item_table.columns.push({
|
||||
name: "actions",
|
||||
className: "actions noExport",
|
||||
title: this.$__("Actions"),
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
render: (data, type, row) => {
|
||||
return ""
|
||||
},
|
||||
})
|
||||
}
|
||||
this.initialized = true
|
||||
this.item_table.display = display
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
getTrainList: function () {
|
||||
const client = APIClient.preservation
|
||||
let q = { "me.closed_on": null }
|
||||
client.trains.getAll(q).then(
|
||||
trains => (this.train_list = trains),
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
deleteTrain: function (train) {
|
||||
this.setConfirmationDialog(
|
||||
{
|
||||
title: this.$__(
|
||||
"Are you sure you want to remove this train?"
|
||||
),
|
||||
message: train.name,
|
||||
accept_label: this.$__("Yes, delete"),
|
||||
cancel_label: this.$__("No, do not delete"),
|
||||
},
|
||||
() => {
|
||||
const client = APIClient.preservation
|
||||
client.trains.delete(train.train_id).then(
|
||||
success => {
|
||||
this.setMessage(
|
||||
this.$__("Train %s deleted").format(train.name),
|
||||
true
|
||||
)
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
async updateTrainDate(attribute) {
|
||||
let train = JSON.parse(JSON.stringify(this.train))
|
||||
let train_id = train.train_id
|
||||
delete train.train_id
|
||||
delete train.items
|
||||
delete train.default_processing
|
||||
train[attribute] = new Date()
|
||||
const client = APIClient.preservation
|
||||
if (train_id) {
|
||||
client.trains
|
||||
.update(train, train_id)
|
||||
.then(() => this.getTrain(this.train.train_id))
|
||||
} else {
|
||||
client.trains
|
||||
.create(train)
|
||||
.then(() => this.getTrain(this.train.train_id))
|
||||
}
|
||||
},
|
||||
closeTrain() {
|
||||
this.updateTrainDate("closed_on")
|
||||
},
|
||||
sendTrain() {
|
||||
this.updateTrainDate("sent_on")
|
||||
},
|
||||
receiveTrain() {
|
||||
this.updateTrainDate("received_on")
|
||||
},
|
||||
editItem(train_item_id) {
|
||||
this.$router.push(
|
||||
`/cgi-bin/koha/preservation/trains/${this.train.train_id}/items/edit/${train_item_id}`
|
||||
)
|
||||
},
|
||||
removeItem(train_item_id) {
|
||||
this.setConfirmationDialog(
|
||||
{
|
||||
title: this.$__(
|
||||
"Are you sure you want to remove this item?"
|
||||
),
|
||||
accept_label: this.$__("Yes, remove"),
|
||||
cancel_label: this.$__("No, do not remove"),
|
||||
},
|
||||
() => {
|
||||
const client = APIClient.preservation
|
||||
client.train_items
|
||||
.delete(this.train.train_id, train_item_id)
|
||||
.then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Item removed"), true)
|
||||
this.getTrain(this.train.train_id).then(() => {
|
||||
$("#" + this.table_id)
|
||||
.DataTable()
|
||||
.destroy()
|
||||
this.build_datatable()
|
||||
})
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
selectTrainForCopy(train_item_id) {
|
||||
this.show_modal = true
|
||||
this.train_item_id_to_copy = train_item_id
|
||||
},
|
||||
copyItem(event) {
|
||||
event.preventDefault()
|
||||
const client = APIClient.preservation
|
||||
let new_train_item = {}
|
||||
client.train_items
|
||||
.get(this.train.train_id, this.train_item_id_to_copy)
|
||||
.then(train_item => {
|
||||
new_train_item = {
|
||||
attributes: train_item.attributes.map(attr => {
|
||||
return {
|
||||
processing_attribute_id:
|
||||
attr.processing_attribute_id,
|
||||
value: attr.value,
|
||||
}
|
||||
}),
|
||||
item_id: train_item.item_id,
|
||||
processing_id: train_item.processing_id,
|
||||
}
|
||||
})
|
||||
.then(() =>
|
||||
client.train_items
|
||||
.create(new_train_item, this.train_id_selected_for_copy)
|
||||
.then(
|
||||
success => {
|
||||
this.setMessage(
|
||||
this.$__("Item copied successfully.")
|
||||
)
|
||||
this.show_modal = false
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
)
|
||||
},
|
||||
build_datatable: function () {
|
||||
let table_id = this.table_id
|
||||
let item_table = this.item_table
|
||||
let removeItem = this.removeItem
|
||||
let editItem = this.editItem
|
||||
let selectTrainForCopy = this.selectTrainForCopy
|
||||
let train = this.train
|
||||
|
||||
let table = KohaTable(table_id, {
|
||||
data: item_table.data,
|
||||
ordering: false,
|
||||
autoWidth: false,
|
||||
columns: item_table.columns,
|
||||
drawCallback: function (settings) {
|
||||
var api = new $.fn.dataTable.Api(settings)
|
||||
$.each($(this).find("td.actions"), function (index, e) {
|
||||
let tr = $(this).parent()
|
||||
let train_item_id = api.row(tr).data()
|
||||
.item.train_item_id
|
||||
|
||||
let editButton = createVNode(
|
||||
"a",
|
||||
{
|
||||
class: "btn btn-default btn-xs",
|
||||
role: "button",
|
||||
onClick: () => {
|
||||
editItem(train_item_id)
|
||||
},
|
||||
},
|
||||
[
|
||||
createVNode("i", {
|
||||
class: "fa fa-pencil",
|
||||
"aria-hidden": "true",
|
||||
}),
|
||||
__("Edit"),
|
||||
]
|
||||
)
|
||||
|
||||
let removeButton = createVNode(
|
||||
"a",
|
||||
{
|
||||
class: "btn btn-default btn-xs",
|
||||
role: "button",
|
||||
onClick: () => {
|
||||
removeItem(train_item_id)
|
||||
},
|
||||
},
|
||||
[
|
||||
createVNode("i", {
|
||||
class: "fa fa-trash",
|
||||
"aria-hidden": "true",
|
||||
}),
|
||||
__("Remove"),
|
||||
]
|
||||
)
|
||||
let buttons = [editButton, "", removeButton]
|
||||
|
||||
if (train.received_on !== null) {
|
||||
buttons.push("")
|
||||
buttons.push(
|
||||
createVNode(
|
||||
"a",
|
||||
{
|
||||
class: "btn btn-default btn-xs",
|
||||
role: "button",
|
||||
onClick: () => {
|
||||
selectTrainForCopy(train_item_id)
|
||||
},
|
||||
},
|
||||
[
|
||||
createVNode("i", {
|
||||
class: "fa fa-copy",
|
||||
"aria-hidden": "true",
|
||||
}),
|
||||
__("Copy"),
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let n = createVNode("span", {}, buttons)
|
||||
render(n, e)
|
||||
})
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
name: "TrainsShow",
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.action_links a {
|
||||
padding-left: 0.2em;
|
||||
font-size: 11px;
|
||||
}
|
||||
.attributes_values {
|
||||
float: left;
|
||||
}
|
||||
.attribute_value {
|
||||
display: block;
|
||||
}
|
||||
.modal {
|
||||
position: fixed;
|
||||
z-index: 9998;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 35%;
|
||||
height: 30%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: table;
|
||||
transition: opacity 0.3s ease;
|
||||
margin: auto;
|
||||
padding: 20px 30px;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<div id="toolbar" class="btn-toolbar">
|
||||
<router-link
|
||||
to="/cgi-bin/koha/preservation/trains/add"
|
||||
class="btn btn-default"
|
||||
><font-awesome-icon icon="plus" />
|
||||
{{ $__("New train") }}</router-link
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TrainsToolbar",
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,344 @@
|
|||
<template>
|
||||
<transition name="modal_add_to_waiting_list">
|
||||
<div v-if="show_modal_add_to_waiting_list" class="modal">
|
||||
<h2>{{ $__("Add items to waiting list") }}</h2>
|
||||
<form @submit="addItemsToWaitingList($event)">
|
||||
<div class="page-section">
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li>
|
||||
<label class="required" for="barcode_list"
|
||||
>{{ $__("Barcode list") }}:</label
|
||||
>
|
||||
<textarea
|
||||
id="barcode_list"
|
||||
v-model="barcode_list"
|
||||
:placeholder="$__('Barcodes')"
|
||||
rows="10"
|
||||
cols="50"
|
||||
required
|
||||
/>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Submit" />
|
||||
<input
|
||||
type="button"
|
||||
@click="show_modal_add_to_waiting_list = false"
|
||||
:value="$__('Close')"
|
||||
/>
|
||||
</fieldset>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</transition>
|
||||
<transition name="modal_add_to_train">
|
||||
<div v-if="show_modal_add_to_train" class="modal">
|
||||
<h2>{{ $__("Add items to a train") }}</h2>
|
||||
<form @submit="addItemsToTrain($event)">
|
||||
<div class="page-section">
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li>
|
||||
<label class="required" for="train_list"
|
||||
>{{ $__("Select a train") }}:</label
|
||||
>
|
||||
<v-select
|
||||
id="train_id"
|
||||
v-model="train_id_selected_for_add"
|
||||
label="name"
|
||||
:options="train_list"
|
||||
:reduce="t => t.train_id"
|
||||
>
|
||||
<template #search="{ attributes, events }">
|
||||
<input
|
||||
:required="
|
||||
!train_id_selected_for_add
|
||||
"
|
||||
class="vs__search"
|
||||
v-bind="attributes"
|
||||
v-on="events"
|
||||
/>
|
||||
</template>
|
||||
</v-select>
|
||||
<span class="required">{{
|
||||
$__("Required")
|
||||
}}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<fieldset class="action">
|
||||
<input type="submit" value="Submit" />
|
||||
<input
|
||||
type="button"
|
||||
@click="show_modal_add_to_train = false"
|
||||
:value="$__('Close')"
|
||||
/>
|
||||
</fieldset>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</transition>
|
||||
<div v-if="!initialized">{{ $__("Loading") }}</div>
|
||||
<div v-else-if="!settings.not_for_loan_waiting_list_in" id="waiting-list">
|
||||
{{ $__("You need to configure this module first.") }}
|
||||
</div>
|
||||
<div v-else id="waiting-list">
|
||||
<div id="toolbar" class="btn-toolbar">
|
||||
<a
|
||||
class="btn btn-default"
|
||||
@click="show_modal_add_to_waiting_list = true"
|
||||
><font-awesome-icon icon="plus" />
|
||||
{{ $__("Add to waiting list") }}</a
|
||||
>
|
||||
<a
|
||||
v-if="last_items.length > 0"
|
||||
class="btn btn-default"
|
||||
@click="show_modal_add_to_train = true"
|
||||
><font-awesome-icon icon="plus" />
|
||||
{{
|
||||
$__("Add last %s items to a train").format(
|
||||
last_items.length
|
||||
)
|
||||
}}</a
|
||||
>
|
||||
</div>
|
||||
<div v-if="count_waiting_list_items > 0" class="page-section">
|
||||
<KohaTable
|
||||
ref="table"
|
||||
v-bind="tableOptions"
|
||||
@remove="doRemoveItem"
|
||||
></KohaTable>
|
||||
</div>
|
||||
<div v-else class="dialog message">
|
||||
{{ $__("There are no items in the waiting list") }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import flatPickr from "vue-flatpickr-component"
|
||||
import { inject, ref } from "vue"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { APIClient } from "../../fetch/api-client"
|
||||
import KohaTable from "../KohaTable.vue"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const table = ref()
|
||||
|
||||
const PreservationStore = inject("PreservationStore")
|
||||
const { settings } = storeToRefs(PreservationStore)
|
||||
|
||||
const { setMessage, setConfirmationDialog, loading, loaded } =
|
||||
inject("mainStore")
|
||||
|
||||
return {
|
||||
table,
|
||||
settings,
|
||||
setMessage,
|
||||
setConfirmationDialog,
|
||||
loading,
|
||||
loaded,
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
fp_config: flatpickr_defaults,
|
||||
count_waiting_list_items: 0,
|
||||
barcode_list: "",
|
||||
initialized: false,
|
||||
show_modal_add_to_waiting_list: false,
|
||||
show_modal_add_to_train: false,
|
||||
tableOptions: {
|
||||
columns: this.getTableColumns(),
|
||||
url: "/api/v1/preservation/waiting-list/items",
|
||||
options: { embed: "biblio" },
|
||||
add_filters: true,
|
||||
actions: {
|
||||
0: ["show"],
|
||||
"-1": ["remove"],
|
||||
},
|
||||
},
|
||||
last_items: [],
|
||||
train_list: [],
|
||||
train_id_selected_for_add: null,
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(vm => {
|
||||
vm.getCountWaitingListItems()
|
||||
vm.getTrainList()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
async getCountWaitingListItems() {
|
||||
const client = APIClient.preservation
|
||||
client.waiting_list_items.count().then(count => {
|
||||
this.count_waiting_list_items = count
|
||||
this.initialized = true
|
||||
})
|
||||
},
|
||||
getTrainList: function () {
|
||||
const client = APIClient.preservation
|
||||
client.trains.getAll().then(
|
||||
trains => (this.train_list = trains),
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
addItemsToTrain: function (e) {
|
||||
e.preventDefault()
|
||||
this.loading()
|
||||
let item_ids = Object.values(this.last_items)
|
||||
const client = APIClient.preservation
|
||||
let promises = []
|
||||
this.last_items.forEach(i =>
|
||||
promises.push(
|
||||
client.train_items.create(
|
||||
{ item_id: i.item_id },
|
||||
this.train_id_selected_for_add
|
||||
)
|
||||
)
|
||||
)
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
this.setMessage(
|
||||
this.$__(
|
||||
"The items have been added to train %s."
|
||||
).format(this.train_id_selected_for_add),
|
||||
true
|
||||
)
|
||||
|
||||
this.$refs.table.redraw(
|
||||
"/api/v1/preservation/waiting-list/items"
|
||||
)
|
||||
this.show_modal_add_to_train = false
|
||||
this.last_items = []
|
||||
})
|
||||
.then(() => this.loaded())
|
||||
},
|
||||
addItemsToWaitingList: function (e) {
|
||||
e.preventDefault()
|
||||
this.show_modal_add_to_waiting_list = false
|
||||
let items = []
|
||||
this.barcode_list
|
||||
.split("\n")
|
||||
.forEach(barcode => items.push({ barcode }))
|
||||
const client = APIClient.preservation
|
||||
client.waiting_list_items.createAll(items).then(
|
||||
result => {
|
||||
if (result.length) {
|
||||
this.setMessage(
|
||||
this.$__("%s new items added.").format(
|
||||
result.length
|
||||
),
|
||||
true
|
||||
)
|
||||
this.last_items = result
|
||||
if (this.$refs.table) {
|
||||
this.$refs.table.redraw(
|
||||
"/api/v1/preservation/waiting-list/items"
|
||||
)
|
||||
} else {
|
||||
this.getCountWaitingListItems()
|
||||
}
|
||||
} else {
|
||||
this.setMessage(this.$__("No items added"))
|
||||
}
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
this.barcode_list = ""
|
||||
},
|
||||
doShow: function (biblio, dt, event) {
|
||||
event.preventDefault()
|
||||
location.href =
|
||||
"/cgi-bin/koha/catalogue/detail.pl?biblionumber=" +
|
||||
biblio.biblio_id
|
||||
},
|
||||
doRemoveItem: function (item, dt, event) {
|
||||
this.setConfirmationDialog(
|
||||
{
|
||||
title: this.$__(
|
||||
"Are you sure you want to remove this item from the waiting list?"
|
||||
),
|
||||
message: item.barcode,
|
||||
accept_label: this.$__("Yes, remove"),
|
||||
cancel_label: this.$__("No, do not remove"),
|
||||
},
|
||||
() => {
|
||||
const client = APIClient.preservation
|
||||
client.waiting_list_items.delete(item.item_id).then(
|
||||
success => {
|
||||
this.setMessage(
|
||||
this.$__("Item removed from the waiting list"),
|
||||
true
|
||||
)
|
||||
dt.draw()
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
getTableColumns: function () {
|
||||
let escape_str = this.escape_str
|
||||
return [
|
||||
{
|
||||
data: "biblio.title",
|
||||
title: __("Title"),
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return `<a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=${row.biblio.biblio_id}">${row.biblio.title}</a>`
|
||||
},
|
||||
},
|
||||
{
|
||||
data: "biblio.author",
|
||||
title: __("Author"),
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
},
|
||||
{
|
||||
data: "callnumber",
|
||||
title: __("Callnumber"),
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
},
|
||||
{
|
||||
data: "external_id",
|
||||
title: __("Barcode"),
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
components: { flatPickr, KohaTable },
|
||||
name: "WaitingList",
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#waiting_list {
|
||||
display: table;
|
||||
}
|
||||
.modal {
|
||||
position: fixed;
|
||||
z-index: 9990;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 35%;
|
||||
height: 30%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: table;
|
||||
transition: opacity 0.3s ease;
|
||||
margin: auto;
|
||||
padding: 20px 30px;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
</style>
|
|
@ -4,6 +4,7 @@ import AcquisitionAPIClient from "./acquisition-api-client";
|
|||
import AVAPIClient from "./authorised-values-api-client";
|
||||
import ItemAPIClient from "./item-api-client";
|
||||
import SysprefAPIClient from "./system-preferences-api-client";
|
||||
import PreservationAPIClient from "./preservation-api-client";
|
||||
|
||||
export const APIClient = {
|
||||
erm: new ERMAPIClient(),
|
||||
|
@ -12,4 +13,5 @@ export const APIClient = {
|
|||
authorised_values: new AVAPIClient(),
|
||||
item: new ItemAPIClient(),
|
||||
sysprefs: new SysprefAPIClient(),
|
||||
preservation: new PreservationAPIClient(),
|
||||
};
|
||||
|
|
|
@ -9,6 +9,10 @@ export class AVAPIClient extends HttpClient {
|
|||
|
||||
get values() {
|
||||
return {
|
||||
get: category =>
|
||||
this.get({
|
||||
endpoint: `/${category}/authorised_values`,
|
||||
}),
|
||||
getCategoriesWithValues: cat_array =>
|
||||
this.get({
|
||||
endpoint:
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
import HttpClient from "./http-client";
|
||||
|
||||
export class PreservationAPIClient extends HttpClient {
|
||||
constructor() {
|
||||
super({
|
||||
baseURL: "/api/v1/preservation/",
|
||||
});
|
||||
}
|
||||
|
||||
get trains() {
|
||||
return {
|
||||
get: (id) =>
|
||||
this.get({
|
||||
endpoint: "trains/" + id,
|
||||
headers: {
|
||||
"x-koha-embed":
|
||||
"default_processing,default_processing.attributes,items,items.attributes,items.attributes.processing_attribute",
|
||||
},
|
||||
}),
|
||||
getAll: (query = {}) =>
|
||||
this.get({
|
||||
endpoint: "trains?" +
|
||||
new URLSearchParams({
|
||||
_per_page: -1,
|
||||
...(query && { q: JSON.stringify(query) }),
|
||||
}),
|
||||
}),
|
||||
delete: (id) =>
|
||||
this.delete({
|
||||
endpoint: "trains/" + id,
|
||||
}),
|
||||
create: (train) =>
|
||||
this.post({
|
||||
endpoint: "trains",
|
||||
body: train,
|
||||
}),
|
||||
update: (train, id) =>
|
||||
this.put({
|
||||
endpoint: "trains/" + id,
|
||||
body: train,
|
||||
}),
|
||||
count: (query = {}) =>
|
||||
this.count({
|
||||
endpoint:
|
||||
"trains?" +
|
||||
new URLSearchParams({
|
||||
_page: 1,
|
||||
_per_page: 1,
|
||||
...(query && { q: JSON.stringify(query) }),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
get processings() {
|
||||
return {
|
||||
get: (id) =>
|
||||
this.get({
|
||||
endpoint: "processings/" + id,
|
||||
headers: {
|
||||
"x-koha-embed": "attributes",
|
||||
},
|
||||
}),
|
||||
getAll: (query) =>
|
||||
this.get({
|
||||
endpoint: "processings?" +
|
||||
new URLSearchParams({
|
||||
_per_page: -1,
|
||||
...(query && { q: JSON.stringify(query) }),
|
||||
}),
|
||||
}),
|
||||
|
||||
delete: (id) =>
|
||||
this.delete({
|
||||
endpoint: "processings/" + id,
|
||||
}),
|
||||
create: (processing) =>
|
||||
this.post({
|
||||
endpoint: "processings",
|
||||
body: processing,
|
||||
}),
|
||||
update: (processing, id) =>
|
||||
this.put({
|
||||
endpoint: "processings/" + id,
|
||||
body: processing,
|
||||
}),
|
||||
count: (query = {}) =>
|
||||
this.count({
|
||||
endpoint:
|
||||
"processings?" +
|
||||
new URLSearchParams({
|
||||
_page: 1,
|
||||
_per_page: 1,
|
||||
...(query && { q: JSON.stringify(query) }),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
get train_items() {
|
||||
return {
|
||||
get: (train_id, id) =>
|
||||
this.get({
|
||||
endpoint: "trains/" + train_id + "/items/" + id,
|
||||
headers: {
|
||||
"x-koha-embed":
|
||||
"attributes,catalogue_item,catalogue_item.biblio",
|
||||
},
|
||||
}),
|
||||
delete: (train_id, id) =>
|
||||
this.delete({
|
||||
endpoint: "trains/" + train_id + "/items/" + id,
|
||||
}),
|
||||
create: (train_item, train_id) =>
|
||||
this.post({
|
||||
endpoint: "trains/" + train_id + "/items",
|
||||
body: train_item,
|
||||
}),
|
||||
update: (train_item, train_id, id) =>
|
||||
this.put({
|
||||
endpoint: "trains/" + train_id + "/items/" + id,
|
||||
body: train_item,
|
||||
}),
|
||||
count: (train_id, query = {}) =>
|
||||
this.count({
|
||||
endpoint:
|
||||
"trains/" +
|
||||
train_id +
|
||||
"/items?" +
|
||||
new URLSearchParams({
|
||||
_page: 1,
|
||||
_per_page: 1,
|
||||
...(query && { q: JSON.stringify(query) }),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
get waiting_list_items() {
|
||||
return {
|
||||
get_from_barcode: (barcode) => {
|
||||
const q = {
|
||||
"me.barcode": barcode,
|
||||
};
|
||||
|
||||
const params = {
|
||||
_page: 1,
|
||||
_per_page: 1,
|
||||
q: JSON.stringify(q),
|
||||
};
|
||||
return this.get({
|
||||
endpoint:
|
||||
"waiting-list/items?" + new URLSearchParams(params),
|
||||
headers: {
|
||||
"x-koha-embed": "biblio",
|
||||
},
|
||||
}).then((response) => {
|
||||
return response.length ? response[0] : undefined;
|
||||
});
|
||||
},
|
||||
delete: (id) =>
|
||||
this.delete({
|
||||
endpoint: "waiting-list/items/" + id,
|
||||
}),
|
||||
createAll: (items) =>
|
||||
this.post({
|
||||
endpoint: "waiting-list/items",
|
||||
body: items,
|
||||
}),
|
||||
count: (query = {}) =>
|
||||
this.count({
|
||||
endpoint:
|
||||
"waiting-list/items?" +
|
||||
new URLSearchParams({
|
||||
_page: 1,
|
||||
_per_page: 1,
|
||||
...(query && { q: JSON.stringify(query) }),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default PreservationAPIClient;
|
74
koha-tmpl/intranet-tmpl/prog/js/vue/modules/preservation.ts
Normal file
74
koha-tmpl/intranet-tmpl/prog/js/vue/modules/preservation.ts
Normal file
|
@ -0,0 +1,74 @@
|
|||
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/Preservation/Main.vue";
|
||||
|
||||
import { routes } from "../routes/preservation";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
linkActiveClass: "current",
|
||||
routes,
|
||||
});
|
||||
|
||||
import { useMainStore } from "../stores/main";
|
||||
import { useAVStore } from "../stores/authorised-values";
|
||||
import { usePreservationStore } from "../stores/preservation";
|
||||
|
||||
const pinia = createPinia();
|
||||
|
||||
const i18n = {
|
||||
install: (app, options) => {
|
||||
app.config.globalProperties.$__ = key => {
|
||||
return window["__"](key);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
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;
|
||||
const mainStore = useMainStore(pinia);
|
||||
app.provide("mainStore", mainStore);
|
||||
app.provide("AVStore", useAVStore(pinia));
|
||||
app.provide("PreservationStore", usePreservationStore(pinia));
|
||||
|
||||
app.mount("#preservation");
|
||||
|
||||
const { removeMessages } = mainStore;
|
||||
router.beforeEach((to, from) => {
|
||||
removeMessages(); // This will actually flag the messages as displayed already
|
||||
});
|
220
koha-tmpl/intranet-tmpl/prog/js/vue/routes/preservation.js
Normal file
220
koha-tmpl/intranet-tmpl/prog/js/vue/routes/preservation.js
Normal file
|
@ -0,0 +1,220 @@
|
|||
import Home from "../components/Preservation/Home.vue";
|
||||
import TrainsList from "../components/Preservation/TrainsList.vue";
|
||||
import TrainsShow from "../components/Preservation/TrainsShow.vue";
|
||||
import TrainsFormAdd from "../components/Preservation/TrainsFormAdd.vue";
|
||||
import TrainsFormAddItem from "../components/Preservation/TrainsFormAddItem.vue";
|
||||
import WaitingList from "../components/Preservation/WaitingList.vue";
|
||||
import Settings from "../components/Preservation/Settings.vue";
|
||||
import SettingsProcessingsShow from "../components/Preservation/SettingsProcessingsShow.vue";
|
||||
import SettingsProcessingsFormAdd from "../components/Preservation/SettingsProcessingsFormAdd.vue";
|
||||
|
||||
const breadcrumbs = {
|
||||
home: {
|
||||
text: "Home", // $t("Home")
|
||||
path: "/cgi-bin/koha/mainpage.pl",
|
||||
},
|
||||
preservation_home: {
|
||||
text: "Preservation", //$t("Preservation")
|
||||
path: "/cgi-bin/koha/preservation/home.pl",
|
||||
},
|
||||
trains: {
|
||||
text: "Trains", // $t("Trains")
|
||||
path: "/cgi-bin/koha/preservation/trains",
|
||||
},
|
||||
waiting_list: {
|
||||
text: "Waiting list", // $t("Waiting list")
|
||||
path: "/cgi-bin/koha/preservation/waiting-list",
|
||||
},
|
||||
settings: {
|
||||
home: {
|
||||
text: "Settings", // $t("Settings")
|
||||
path: "/cgi-bin/koha/preservation/settings",
|
||||
},
|
||||
processings: {
|
||||
home: {
|
||||
text: "Processings", //$t("Processings")
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const breadcrumb_paths = {
|
||||
trains: [
|
||||
breadcrumbs.home,
|
||||
breadcrumbs.preservation_home,
|
||||
breadcrumbs.trains,
|
||||
],
|
||||
settings: [
|
||||
breadcrumbs.home,
|
||||
breadcrumbs.preservation_home,
|
||||
breadcrumbs.settings.home,
|
||||
],
|
||||
settings_processings: [
|
||||
breadcrumbs.home,
|
||||
breadcrumbs.preservation_home,
|
||||
breadcrumbs.settings.home,
|
||||
],
|
||||
};
|
||||
|
||||
function build_breadcrumb(parent_breadcrumb, current) {
|
||||
let breadcrumb = parent_breadcrumb.flat(Infinity);
|
||||
if (current) {
|
||||
breadcrumb.push({
|
||||
text: current,
|
||||
});
|
||||
}
|
||||
return breadcrumb;
|
||||
}
|
||||
|
||||
export const routes = [
|
||||
{
|
||||
path: "/cgi-bin/koha/mainpage.pl",
|
||||
beforeEnter(to, from, next) {
|
||||
window.location.href = "/cgi-bin/koha/mainpage.pl";
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/cgi-bin/koha/preservation/home.pl",
|
||||
component: Home,
|
||||
meta: {
|
||||
breadcrumb: () => [breadcrumbs.home, breadcrumbs.preservation_home],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/cgi-bin/koha/preservation/trains",
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
component: TrainsList,
|
||||
meta: {
|
||||
breadcrumb: () => breadcrumb_paths.trains,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: ":train_id",
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
component: TrainsShow,
|
||||
meta: {
|
||||
breadcrumb: () =>
|
||||
build_breadcrumb(
|
||||
breadcrumb_paths.trains,
|
||||
"Show train" // $t("Show train")
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "items",
|
||||
children: [
|
||||
{
|
||||
path: "add",
|
||||
component: TrainsFormAddItem,
|
||||
meta: {
|
||||
breadcrumb: () =>
|
||||
build_breadcrumb(
|
||||
breadcrumb_paths.trains,
|
||||
"Add item to train" // $t("Add item to train")
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "edit/:train_item_id",
|
||||
component: TrainsFormAddItem,
|
||||
meta: {
|
||||
breadcrumb: () =>
|
||||
build_breadcrumb(
|
||||
breadcrumb_paths.trains,
|
||||
"Edit item in train" // $t("Edit item in train")
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "add",
|
||||
component: TrainsFormAdd,
|
||||
meta: {
|
||||
breadcrumb: () =>
|
||||
build_breadcrumb(
|
||||
breadcrumb_paths.trains,
|
||||
"Add train" // $t("Add train")
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "edit/:train_id",
|
||||
component: TrainsFormAdd,
|
||||
meta: {
|
||||
breadcrumb: () =>
|
||||
build_breadcrumb(
|
||||
breadcrumb_paths.trains,
|
||||
"Edit train" // $t("Edit train")
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/cgi-bin/koha/preservation/waiting-list",
|
||||
component: WaitingList,
|
||||
meta: {
|
||||
breadcrumb: () => [
|
||||
breadcrumbs.home,
|
||||
breadcrumbs.preservation_home,
|
||||
breadcrumbs.waiting_list,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/cgi-bin/koha/preservation/settings",
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
component: Settings,
|
||||
meta: {
|
||||
breadcrumb: () => breadcrumb_paths.settings,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "processings",
|
||||
children: [
|
||||
{
|
||||
path: ":processing_id",
|
||||
component: SettingsProcessingsShow,
|
||||
meta: {
|
||||
breadcrumb: () =>
|
||||
build_breadcrumb(
|
||||
breadcrumb_paths.settings_processings,
|
||||
"Show processing" // $t("Show processing")
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "add",
|
||||
component: SettingsProcessingsFormAdd,
|
||||
meta: {
|
||||
breadcrumb: () =>
|
||||
build_breadcrumb(
|
||||
breadcrumb_paths.settings_processings,
|
||||
"Add processing" // $t("Add processing")
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "edit/:processing_id",
|
||||
component: SettingsProcessingsFormAdd,
|
||||
meta: {
|
||||
breadcrumb: () =>
|
||||
build_breadcrumb(
|
||||
breadcrumb_paths.settings_processings,
|
||||
"Edit processing" // $t("Edit processing")
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -36,6 +36,7 @@ export const useAVStore = defineStore("authorised_values", {
|
|||
av_package_types: [],
|
||||
av_package_content_types: [],
|
||||
av_title_publication_types: [],
|
||||
av_notforloan: [],
|
||||
}),
|
||||
actions: {
|
||||
get_lib_from_av(arr_name, av) {
|
||||
|
|
10
koha-tmpl/intranet-tmpl/prog/js/vue/stores/preservation.js
Normal file
10
koha-tmpl/intranet-tmpl/prog/js/vue/stores/preservation.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const usePreservationStore = defineStore("preservation", {
|
||||
state: () => ({
|
||||
settings: {
|
||||
not_for_loan_waiting_list_in: null,
|
||||
not_for_loan_default_train_in: 0,
|
||||
},
|
||||
}),
|
||||
});
|
|
@ -6,6 +6,7 @@ const webpack = require('webpack');
|
|||
module.exports = {
|
||||
entry: {
|
||||
erm: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/erm.ts",
|
||||
preservation: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/preservation.ts",
|
||||
},
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
|
|
Loading…
Reference in a new issue