From a4aec936f6f7cb5ccd078f13d98218b653219acd Mon Sep 17 00:00:00 2001 From: Pedro Amorim <pedro.amorim@ptfs-europe.com> Date: Wed, 8 Nov 2023 11:34:19 -0100 Subject: [PATCH] Bug 35287: Vue preparation - AdditionalFields New api client: additional-fields-api-client.js to interact with the REST endpoint introduced by bug 35197 2 new components: - AdditionalFieldsEntry: Component used to extend the FormAdd component including the corresponding additional fields - AdditionalFieldsDisplay: Component used to display the additional fields associated to a data record implementing it (makes use of strings_map) Signed-off-by: Edith Speller <edith.speller@ukhsa.gov.uk> Signed-off-by: Michaela Sieber <michaela.sieber@kit.edu> --- .../components/AdditionalFieldsDisplay.vue | 37 +++ .../vue/components/AdditionalFieldsEntry.vue | 271 ++++++++++++++++++ .../vue/fetch/additional-fields-api-client.js | 20 ++ .../prog/js/vue/fetch/api-client.js | 2 + 4 files changed, 330 insertions(+) create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsDisplay.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsEntry.vue create mode 100644 koha-tmpl/intranet-tmpl/prog/js/vue/fetch/additional-fields-api-client.js diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsDisplay.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsDisplay.vue new file mode 100644 index 00000000000..f7e232913a3 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsDisplay.vue @@ -0,0 +1,37 @@ +<template> + <fieldset + v-if="additional_field_values.length" + class="rows" + id="additional_fields" + > + <legend>{{ $__("Additional fields") }}</legend> + <ol> + <li v-for="additional_field_value in additional_field_values"> + <label + :for="`additional_field_` + additional_field_value.field_id" + > + {{ additional_field_value.field_label }}: + </label> + <span> {{ additional_field_value.value_str }} </span> + </li> + </ol> + </fieldset> +</template> + +<script> +import { inject } from "vue" +import { APIClient } from "../fetch/api-client.js" + +export default { + data() { + return { + fields_to_display: [], + } + }, + name: "AdditionalFieldsDisplay", + props: { + tablename: String, + additional_field_values: Array, + }, +} +</script> diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsEntry.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsEntry.vue new file mode 100644 index 00000000000..246656f3aea --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/AdditionalFieldsEntry.vue @@ -0,0 +1,271 @@ +<template> + <fieldset + v-if="available_fields.length" + class="rows" + id="additional_fields" + > + <legend>{{ $__("Additional fields") }}</legend> + <ol> + <template v-for="available_field in available_fields"> + <template + v-if=" + available_field.authorised_value_category && + !available_field.repeatable + " + > + <li> + <label :for="`additional_field_` + available_field.id" + >{{ available_field.name }}: + </label> + <v-select + :id="`additional_field_` + available_field.id" + :name="available_field.name" + v-model=" + current_additional_fields_values[ + available_field.id + ] + " + :options=" + av_options[ + available_field.authorised_value_category + ] + " + /> + </li> + </template> + <template + v-if=" + available_field.authorised_value_category && + available_field.repeatable + " + > + <li> + <label :for="`additional_field_` + available_field.id" + >{{ available_field.name }}: + </label> + <v-select + :id="`additional_field_` + available_field.id" + :name="available_field.name" + :multiple="available_field.repeatable" + v-model=" + current_additional_fields_values[ + available_field.id + ] + " + :options=" + av_options[ + available_field.authorised_value_category + ] + " + /> + </li> + </template> + + <template v-if="!available_field.authorised_value_category"> + <li + v-for="current in current_additional_fields_values[ + available_field.id + ]" + > + <label :for="`additional_field_` + available_field.id" + >{{ available_field.name }}: + </label> + <input type="text" v-model="current.value" /> + <a + href="#" + class="clear_attribute" + @click="clearField(current, $event)" + > + <i class="fa fa-fw fa-trash-can"></i> + {{ $__("Clear") }} + </a> + <template v-if="available_field.repeatable"> + <a + href="#" + class="clone_attribute" + @click=" + cloneField(available_field, current, $event) + " + > + <i class="fa fa-fw fa-plus"></i> + {{ $__("New") }} + </a> + </template> + </li> + </template> + </template> + </ol> + </fieldset> +</template> + +<script> +import { inject } from "vue" +import { APIClient } from "../fetch/api-client.js" + +export default { + data() { + return { + available_fields: [], + av_options: [], + current_additional_fields_values: {}, + } + }, + beforeCreate() { + const client = APIClient.additional_fields + client.additional_fields.getAll(this.tablename).then( + available_fields => { + this.available_fields = available_fields + this.initialized = true + }, + error => {} + ) + }, + watch: { + current_additional_fields_values: { + deep: true, + handler(current_additional_fields_values) { + this.updateParentAdditionalFieldValues( + current_additional_fields_values + ) + }, + }, + available_fields: function (available_fields) { + if (available_fields) { + const client_av = APIClient.authorised_values + let av_cat_array = available_fields + .map(field => field.authorised_value_category) + .filter(field => field) + + 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.map(av => ({ + value: av.value, + label: av.description, + })) + }) + + // Iterate on available fields + available_fields.forEach(available_field => { + // Initialize current field as empty array + this.current_additional_fields_values[ + available_field.id + ] = [] + + // Grab all existing field values of this field + let existing_field_values = + this.additional_field_values.filter( + afv => + afv.field_id == available_field.id && + afv.value + ) + + // If there are existing field values for this field, add them to current_additional_fields_values + if (existing_field_values.length) { + existing_field_values.forEach( + existing_field_value => { + let label = "" + if ( + available_field.authorised_value_category + ) { + let av_value = this.av_options[ + available_field + .authorised_value_category + ].filter( + av_option => + av_option.value == + existing_field_value.value + ) + label = av_value.length + ? av_value[0].label + : "" + } + this.current_additional_fields_values[ + existing_field_value.field_id + ].push({ + value: existing_field_value.value, + label: label, + }) + } + ) + + // Otherwise add them as empty if not AV field + } else { + if ( + !available_field.authorised_value_category + ) { + this.current_additional_fields_values[ + available_field.id + ] = [ + { + label: "", + value: "", + }, + ] + } + } + }) + }) + } + }, + }, + methods: { + clearField: function (current_field, event) { + event.preventDefault() + current_field.value = "" + }, + cloneField: function (available_field, current, event) { + event.preventDefault() + this.current_additional_fields_values[available_field.id].push({ + value: current.value, + label: available_field.name, + }) + }, + updateParentAdditionalFieldValues: function ( + current_additional_fields_values + ) { + let updatedAdditionalFields = [] + Object.keys(current_additional_fields_values).forEach(field_id => { + if ( + !Array.isArray( + current_additional_fields_values[field_id] + ) && + current_additional_fields_values[field_id] + ) { + current_additional_fields_values[field_id] = [ + current_additional_fields_values[field_id], + ] + } + if (current_additional_fields_values[field_id]) { + let new_extended_attributes = + current_additional_fields_values[field_id].map( + value => ({ + field_id: field_id, + value: value.value, + }) + ) + updatedAdditionalFields = updatedAdditionalFields.concat( + new_extended_attributes + ) + } + }) + + this.$emit("additional-fields-changed", updatedAdditionalFields) + }, + }, + name: "AdditionalFieldsEntry", + props: { + tablename: String, + additional_field_values: Array, + }, +} +</script> diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/additional-fields-api-client.js b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/additional-fields-api-client.js new file mode 100644 index 00000000000..8d12305d51c --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/additional-fields-api-client.js @@ -0,0 +1,20 @@ +import HttpClient from "./http-client"; + +export class AdditionalFieldsAPIClient extends HttpClient { + constructor() { + super({ + baseURL: "/api/v1/additional_fields", + }); + } + + get additional_fields() { + return { + getAll: tablename => + this.get({ + endpoint: "?tablename=" + tablename, + }), + }; + } +} + +export default AdditionalFieldsAPIClient; diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js index 294693c238e..6a35dff20ce 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js @@ -1,6 +1,7 @@ import ERMAPIClient from "./erm-api-client"; import PatronAPIClient from "./patron-api-client"; import AcquisitionAPIClient from "./acquisition-api-client"; +import AdditionalFieldsAPIClient from "./additional-fields-api-client"; import AVAPIClient from "./authorised-values-api-client"; import ItemAPIClient from "./item-api-client"; import SysprefAPIClient from "./system-preferences-api-client"; @@ -10,6 +11,7 @@ export const APIClient = { erm: new ERMAPIClient(), patron: new PatronAPIClient(), acquisition: new AcquisitionAPIClient(), + additional_fields: new AdditionalFieldsAPIClient(), authorised_values: new AVAPIClient(), item: new ItemAPIClient(), sysprefs: new SysprefAPIClient(), -- 2.39.2