Bug 38201: Add a ResourceFormAdd component
Dries out adding and editing resources Signed-off-by: Michaela Sieber <michaela.sieber@kit.edu> Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org> Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
parent
e7a53871f5
commit
2dfa887f24
|
|
@ -1,9 +1,5 @@
|
|||
<template>
|
||||
<slot />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent, inject } from "vue";
|
||||
import { inject } from "vue";
|
||||
|
||||
export default {
|
||||
setup(props) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,18 @@
|
|||
doResourceDelete,
|
||||
}"
|
||||
/>
|
||||
<ResourceFormAdd
|
||||
v-if="['add', 'edit'].includes(action)"
|
||||
v-bind="{
|
||||
id_attr,
|
||||
api_client,
|
||||
i18n,
|
||||
resource_attrs,
|
||||
list_component,
|
||||
resource: newResource,
|
||||
onSubmit,
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -19,9 +31,10 @@ import BaseResource from "../BaseResource.vue";
|
|||
import { storeToRefs } from "pinia";
|
||||
import { APIClient } from "../../fetch/api-client.js";
|
||||
import ResourceShow from "../ResourceShow.vue";
|
||||
import ResourceFormAdd from "../ResourceFormAdd.vue";
|
||||
|
||||
export default {
|
||||
components: { ResourceShow },
|
||||
components: { ResourceShow, ResourceFormAdd },
|
||||
extends: BaseResource,
|
||||
props: {
|
||||
action: String,
|
||||
|
|
@ -110,6 +123,12 @@ export default {
|
|||
label: __("Status"),
|
||||
av_cat: "av_agreement_statuses",
|
||||
show_in_table: true,
|
||||
onSelected: resource => {
|
||||
if (resource.status !== "closed") {
|
||||
resource.closure_reason = null;
|
||||
}
|
||||
return resource;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "closure_reason",
|
||||
|
|
@ -117,6 +136,7 @@ export default {
|
|||
label: __("Closure reason"),
|
||||
av_cat: "av_agreement_closure_reasons",
|
||||
show_in_table: true,
|
||||
disabled: agreement => agreement.status !== "closed",
|
||||
},
|
||||
{
|
||||
name: "is_perpetual",
|
||||
|
|
@ -594,8 +614,174 @@ export default {
|
|||
],
|
||||
},
|
||||
],
|
||||
agreement: {
|
||||
agreement_id: null,
|
||||
name: "",
|
||||
vendor_id: null,
|
||||
description: "",
|
||||
status: "",
|
||||
closure_reason: "",
|
||||
is_perpetual: false,
|
||||
renewal_priority: "",
|
||||
license_info: "",
|
||||
periods: [],
|
||||
user_roles: [],
|
||||
agreement_licenses: [],
|
||||
agreement_relationships: [],
|
||||
documents: [],
|
||||
extended_attributes: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
checkForm(agreement) {
|
||||
let errors = [];
|
||||
|
||||
let agreement_licenses = agreement.agreement_licenses;
|
||||
// Do not use al.license.name here! Its name is not the one linked with al.license_id
|
||||
// At this point al.license is meaningless, form/template only modified al.license_id
|
||||
const license_ids = agreement_licenses.map(al => al.license_id);
|
||||
const duplicate_license_ids = license_ids.filter(
|
||||
(id, i) => license_ids.indexOf(id) !== i
|
||||
);
|
||||
|
||||
if (duplicate_license_ids.length) {
|
||||
errors.push(this.$__("A license is used several times"));
|
||||
}
|
||||
|
||||
const related_agreement_ids = agreement.agreement_relationships.map(
|
||||
rs => rs.related_agreement_id
|
||||
);
|
||||
const duplicate_related_agreement_ids =
|
||||
related_agreement_ids.filter(
|
||||
(id, i) => related_agreement_ids.indexOf(id) !== i
|
||||
);
|
||||
|
||||
if (duplicate_related_agreement_ids.length) {
|
||||
errors.push(
|
||||
this.$__(
|
||||
"An agreement is used as relationship several times"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
agreement_licenses.filter(al => al.status == "controlling")
|
||||
.length > 1
|
||||
) {
|
||||
errors.push(
|
||||
this.$__("Only one controlling license is allowed")
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
agreement_licenses.filter(al => al.status == "controlling")
|
||||
.length > 1
|
||||
) {
|
||||
errors.push(
|
||||
this.$__("Only one controlling license is allowed")
|
||||
);
|
||||
}
|
||||
|
||||
let documents_with_uploaded_files = agreement.documents.filter(
|
||||
doc => typeof doc.file_content !== "undefined"
|
||||
);
|
||||
if (
|
||||
documents_with_uploaded_files.filter(
|
||||
doc => atob(doc.file_content).length >= max_allowed_packet
|
||||
).length >= 1
|
||||
) {
|
||||
errors.push(
|
||||
this.$__("File size exceeds maximum allowed: %s MB").format(
|
||||
(max_allowed_packet / (1024 * 1024)).toFixed(2)
|
||||
)
|
||||
);
|
||||
}
|
||||
agreement.user_roles.forEach((user, i) => {
|
||||
if (user.patron_str === "") {
|
||||
errors.push(
|
||||
this.$__("Agreement user %s is missing a user").format(
|
||||
i + 1
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
this.setWarning(errors.join("<br>"));
|
||||
return !errors.length;
|
||||
},
|
||||
onSubmit(e, agreementToSave) {
|
||||
e.preventDefault();
|
||||
|
||||
//let agreement= Object.assign( {} ,this.agreement); // copy
|
||||
let agreement = JSON.parse(JSON.stringify(agreementToSave)); // copy
|
||||
let agreement_id = agreement.agreement_id;
|
||||
|
||||
if (!this.checkForm(agreement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
delete agreement.agreement_id;
|
||||
delete agreement.vendor;
|
||||
delete agreement._strings;
|
||||
agreement.is_perpetual = agreement.is_perpetual ? true : false;
|
||||
|
||||
if (agreement.vendor_id == "") {
|
||||
agreement.vendor_id = null;
|
||||
}
|
||||
|
||||
agreement.periods = agreement.periods.map(
|
||||
({ agreement_id, agreement_period_id, ...keepAttrs }) =>
|
||||
keepAttrs
|
||||
);
|
||||
|
||||
agreement.user_roles = agreement.user_roles.map(
|
||||
({ patron, patron_str, ...keepAttrs }) => keepAttrs
|
||||
);
|
||||
|
||||
agreement.agreement_licenses = agreement.agreement_licenses.map(
|
||||
({
|
||||
license,
|
||||
agreement_id,
|
||||
agreement_license_id,
|
||||
...keepAttrs
|
||||
}) => keepAttrs
|
||||
);
|
||||
|
||||
agreement.agreement_relationships =
|
||||
agreement.agreement_relationships.map(
|
||||
({ related_agreement, ...keepAttrs }) => keepAttrs
|
||||
);
|
||||
|
||||
agreement.documents = agreement.documents.map(
|
||||
({ file_type, uploaded_on, ...keepAttrs }) => keepAttrs
|
||||
);
|
||||
|
||||
delete agreement.agreement_packages;
|
||||
|
||||
if (agreement_id) {
|
||||
this.api_client.update(agreement, agreement_id).then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Agreement updated"));
|
||||
this.$router.push({ name: "AgreementsList" });
|
||||
},
|
||||
error => {}
|
||||
);
|
||||
} else {
|
||||
this.api_client.create(agreement).then(
|
||||
success => {
|
||||
this.setMessage(this.$__("Agreement created"));
|
||||
this.$router.push({ name: "AgreementsList" });
|
||||
},
|
||||
error => {}
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
newResource() {
|
||||
return this[this.resource_name];
|
||||
},
|
||||
},
|
||||
created() {
|
||||
//IMPROVEME: We need this for now to assign the correct av array from setup to the attr options in data
|
||||
this.assignAVs(this.resource_attrs);
|
||||
|
|
|
|||
|
|
@ -91,20 +91,6 @@ export default {
|
|||
}
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
"agreement.status": function (newVal, oldVal) {
|
||||
var index = this.resource_attrs.findIndex(function (attr) {
|
||||
return attr.name === "closure_reason";
|
||||
});
|
||||
|
||||
if (newVal != "closed") {
|
||||
this.agreement.closure_reason = "";
|
||||
this.resource_attrs[index].disabled = true;
|
||||
} else {
|
||||
this.resource_attrs[index].disabled = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async getAgreement(agreement_id) {
|
||||
const client = APIClient.erm;
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@
|
|||
:reduce="av => av[attr.requiredKey]"
|
||||
:options="selectOptions"
|
||||
:required="!resource[attr.name] && attr.required"
|
||||
:disabled="attr.disabled"
|
||||
:disabled="disabled"
|
||||
@option:selected="attr.onSelected && attr.onSelected(resource)"
|
||||
>
|
||||
<template v-if="attr.required" #search="{ attributes, events }">
|
||||
<input
|
||||
|
|
@ -101,7 +102,11 @@
|
|||
<span v-if="attr.required" class="required">{{ $__("Required") }}</span>
|
||||
</div>
|
||||
<template v-else-if="attr.type == 'relationship' && attr.componentPath">
|
||||
<component :is="requiredComponent" v-bind="requiredProps()"></component>
|
||||
<component
|
||||
:is="requiredComponent"
|
||||
v-bind="requiredProps()"
|
||||
v-on="getEventHandlers()"
|
||||
></component>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
|
|
@ -134,6 +139,13 @@ export default {
|
|||
}
|
||||
return this.options;
|
||||
},
|
||||
disabled() {
|
||||
if (typeof this.attr.disabled === "function") {
|
||||
return this.attr.disabled(this.resource);
|
||||
} else {
|
||||
return this.attr.disabled || false;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isVModelRequired(componentPath) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
<template>
|
||||
<div v-if="!initialized">{{ $__("Loading") }}</div>
|
||||
<div v-else id="resources_add">
|
||||
<h2 v-if="resourceToAddOrEdit.resource_id">
|
||||
{{
|
||||
$__("Edit") +
|
||||
" " +
|
||||
i18n.display_name +
|
||||
" #" +
|
||||
resourceToAddOrEdit.resource_id
|
||||
}}
|
||||
</h2>
|
||||
<h2 v-else>{{ $__("New") + " " + i18n.display_name }}</h2>
|
||||
<div>
|
||||
<form @submit="onSubmit($event, resourceToAddOrEdit)">
|
||||
<fieldset class="rows">
|
||||
<ol>
|
||||
<li
|
||||
v-for="(attr, index) in resource_attrs.filter(
|
||||
attr => attr.type !== 'relationship'
|
||||
)"
|
||||
v-bind:key="index"
|
||||
>
|
||||
<FormElement
|
||||
:resource="resourceToAddOrEdit"
|
||||
:attr="attr"
|
||||
:index="index"
|
||||
/>
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<template
|
||||
v-for="(attr, index) in resource_attrs.filter(
|
||||
attr => attr.type === 'relationship'
|
||||
)"
|
||||
v-bind:key="'rel-' + index"
|
||||
>
|
||||
<FormElement :resource="resourceToAddOrEdit" :attr="attr" />
|
||||
</template>
|
||||
<fieldset class="action">
|
||||
<ButtonSubmit />
|
||||
<router-link
|
||||
:to="{ name: list_component }"
|
||||
role="button"
|
||||
class="cancel"
|
||||
>{{ $__("Cancel") }}</router-link
|
||||
>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormElement from "./FormElement.vue";
|
||||
import ButtonSubmit from "./ButtonSubmit.vue";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
initialized: false,
|
||||
resourceToEdit: null,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
id_attr: String,
|
||||
api_client: Object,
|
||||
i18n: Object,
|
||||
resource_attrs: Array,
|
||||
list_component: String,
|
||||
resource: Object,
|
||||
onSubmit: Function,
|
||||
},
|
||||
created() {
|
||||
if (this.$route.params[this.id_attr]) {
|
||||
this.getResource(this.$route.params[this.id_attr]);
|
||||
} else {
|
||||
this.initialized = true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getResource(resource_id) {
|
||||
this.api_client.get(resource_id).then(
|
||||
resource => {
|
||||
this.resourceToEdit = resource;
|
||||
this.initialized = true;
|
||||
},
|
||||
error => {}
|
||||
);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
resourceToAddOrEdit() {
|
||||
if (this.resourceToEdit) {
|
||||
return this.resourceToEdit;
|
||||
}
|
||||
return this.resource;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
ButtonSubmit,
|
||||
FormElement,
|
||||
},
|
||||
name: "ResourceFormAdd",
|
||||
};
|
||||
</script>
|
||||
|
|
@ -4,11 +4,11 @@
|
|||
<Toolbar>
|
||||
<ToolbarButton
|
||||
action="edit"
|
||||
@go-to-edit-resource="goToResourceEdit"
|
||||
@go-to-edit-resource="goToResourceEdit(resource)"
|
||||
/>
|
||||
<ToolbarButton
|
||||
action="delete"
|
||||
@delete-resource="doResourceDelete"
|
||||
@delete-resource="doResourceDelete(resource)"
|
||||
/>
|
||||
</Toolbar>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,11 +9,6 @@ import ResourceShow from "./ResourceShow.vue";
|
|||
|
||||
export default {
|
||||
components: { ResourceShow, BaseResource },
|
||||
data() {
|
||||
return {
|
||||
component: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getComponent() {
|
||||
const routeResource = this.$route.meta.self.parent.resource;
|
||||
|
|
@ -23,12 +18,7 @@ export default {
|
|||
const importPath = `./${
|
||||
resourceModule ? `${resourceModule}/` : ""
|
||||
}${componentName}Resource.vue`;
|
||||
const component = defineAsyncComponent(
|
||||
() => import(`${importPath}`)
|
||||
);
|
||||
this.component = component;
|
||||
|
||||
return component;
|
||||
return defineAsyncComponent(() => import(`${importPath}`));
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -67,13 +67,13 @@ export const routes = [
|
|||
{
|
||||
path: "add",
|
||||
name: "AgreementsFormAdd",
|
||||
component: markRaw(AgreementsFormAdd),
|
||||
component: markRaw(ResourceWrapper),
|
||||
title: $__("Add agreement"),
|
||||
},
|
||||
{
|
||||
path: "edit/:agreement_id",
|
||||
name: "AgreementsFormAddEdit",
|
||||
component: markRaw(AgreementsFormAdd),
|
||||
component: markRaw(ResourceWrapper),
|
||||
title: $__("Edit agreement"),
|
||||
},
|
||||
],
|
||||
|
|
|
|||
Loading…
Reference in New Issue