Bug 32030: Max document file size - client-side validation
[srvgit] / koha-tmpl / intranet-tmpl / prog / js / vue / components / ERM / AgreementsFormAdd.vue
1 <template>
2     <div v-if="!initialized">{{ $__("Loading") }}</div>
3     <div v-else id="agreements_add">
4         <h2 v-if="agreement.agreement_id">
5             {{ $__("Edit agreement #%s").format(agreement.agreement_id) }}
6         </h2>
7         <h2 v-else>{{ $__("New agreement") }}</h2>
8         <div>
9             <form @submit="onSubmit($event)">
10                 <div class="page-section">
11                     <fieldset class="rows">
12                         <ol>
13                             <li>
14                                 <label class="required" for="agreement_name"
15                                     >{{ $__("Agreement name") }}:</label
16                                 >
17                                 <input
18                                     id="agreement_name"
19                                     v-model="agreement.name"
20                                     :placeholder="$__('Agreement name')"
21                                     required
22                                 />
23                                 <span class="required">{{
24                                     $__("Required")
25                                 }}</span>
26                             </li>
27                             <li>
28                                 <label for="agreement_vendor_id"
29                                     >{{ $__("Vendor") }}:</label
30                                 >
31                                 <v-select
32                                     id="agreement_vendor_id"
33                                     v-model="agreement.vendor_id"
34                                     label="name"
35                                     :reduce="vendor => vendor.id"
36                                     :options="vendors"
37                                 />
38                             </li>
39                             <li>
40                                 <label for="agreement_description"
41                                     >{{ $__("Description") }}:
42                                 </label>
43                                 <textarea
44                                     id="agreement_description"
45                                     v-model="agreement.description"
46                                     :placeholder="$__('Description')"
47                                     rows="10"
48                                     cols="50"
49                                     required
50                                 />
51                                 <span class="required">{{
52                                     $__("Required")
53                                 }}</span>
54                             </li>
55                             <li>
56                                 <label for="agreement_status"
57                                     >{{ $__("Status") }}:</label
58                                 >
59                                 <v-select
60                                     id="agreement_status"
61                                     v-model="agreement.status"
62                                     label="lib"
63                                     :reduce="av => av.authorised_value"
64                                     :options="av_agreement_statuses"
65                                     @option:selected="onStatusChanged"
66                                     :required="!agreement.status"
67                                 >
68                                     <template #search="{ attributes, events }">
69                                         <input
70                                             :required="!agreement.status"
71                                             class="vs__search"
72                                             v-bind="attributes"
73                                             v-on="events"
74                                         />
75                                     </template>
76                                 </v-select>
77                                 <span class="required">{{
78                                     $__("Required")
79                                 }}</span>
80                             </li>
81                             <li>
82                                 <label for="agreement_closure_reason"
83                                     >{{ $__("Closure reason") }}:</label
84                                 >
85                                 <v-select
86                                     id="agreement_closure_reason"
87                                     v-model="agreement.closure_reason"
88                                     label="lib"
89                                     :reduce="av => av.authorised_value"
90                                     :options="av_agreement_closure_reasons"
91                                     :disabled="
92                                         agreement.status == 'closed'
93                                             ? false
94                                             : true
95                                     "
96                                 />
97                             </li>
98                             <li>
99                                 <label
100                                     for="agreement_is_perpetual"
101                                     class="radio"
102                                     >{{ $__("Is perpetual") }}:</label
103                                 >
104                                 <label for="agreement_is_perpetual_yes">
105                                     <input
106                                         type="radio"
107                                         name="is_perpetual"
108                                         id="agreement_is_perpetual_yes"
109                                         :value="true"
110                                         v-model="agreement.is_perpetual"
111                                     />
112                                     Yes
113                                 </label>
114                                 <label for="agreement_is_perpetual_no">
115                                     <input
116                                         type="radio"
117                                         name="is_perpetual"
118                                         id="agreement_is_perpetual_no"
119                                         :value="false"
120                                         v-model="agreement.is_perpetual"
121                                     />
122                                     No
123                                 </label>
124                             </li>
125                             <li>
126                                 <label for="agreement_renewal_priority"
127                                     >{{ $__("Renewal priority") }}:</label
128                                 >
129                                 <v-select
130                                     id="agreement_renewal_priority"
131                                     v-model="agreement.renewal_priority"
132                                     label="lib"
133                                     :reduce="av => av.authorised_value"
134                                     :options="av_agreement_renewal_priorities"
135                                 />
136                             </li>
137                             <li>
138                                 <label for="agreement_license_info"
139                                     >{{ $__("License info") }}:
140                                 </label>
141                                 <textarea
142                                     id="agreement_license_info"
143                                     v-model="agreement.license_info"
144                                     placeholder="License info"
145                                 />
146                             </li>
147                         </ol>
148                     </fieldset>
149                 </div>
150                 <AgreementPeriods :periods="agreement.periods" />
151                 <AgreementUserRoles
152                     :user_roles="agreement.user_roles"
153                     :av_agreement_user_roles="av_agreement_user_roles"
154                 />
155                 <AgreementLicenses
156                     :agreement_licenses="agreement.agreement_licenses"
157                     :av_agreement_license_statuses="
158                         av_agreement_license_statuses
159                     "
160                     :av_agreement_license_location="
161                         av_agreement_license_location
162                     "
163                 />
164                 <AgreementRelationships
165                     :agreement_id="agreement.agreement_id"
166                     :relationships="agreement.agreement_relationships"
167                     :av_agreement_relationships="av_agreement_relationships"
168                 />
169                 <Documents :documents="agreement.documents" />
170                 <fieldset class="action">
171                     <input type="submit" value="Submit" />
172                     <router-link
173                         to="/cgi-bin/koha/erm/agreements"
174                         role="button"
175                         class="cancel"
176                         >{{ $__("Cancel") }}</router-link
177                     >
178                 </fieldset>
179             </form>
180         </div>
181     </div>
182 </template>
183
184 <script>
185 import { inject } from "vue"
186 import AgreementPeriods from "./AgreementPeriods.vue"
187 import AgreementUserRoles from "./AgreementUserRoles.vue"
188 import AgreementLicenses from "./AgreementLicenses.vue"
189 import AgreementRelationships from "./AgreementRelationships.vue"
190 import Documents from "./Documents.vue"
191 import { setMessage, setError, setWarning } from "../../messages"
192 import { fetchAgreement } from "../../fetch"
193 import { storeToRefs } from "pinia"
194
195 export default {
196     setup() {
197         const vendorStore = inject("vendorStore")
198         const { vendors } = storeToRefs(vendorStore)
199
200         const AVStore = inject("AVStore")
201         const {
202             av_agreement_statuses,
203             av_agreement_closure_reasons,
204             av_agreement_renewal_priorities,
205             av_agreement_user_roles,
206             av_agreement_license_statuses,
207             av_agreement_license_location,
208             av_agreement_relationships,
209         } = storeToRefs(AVStore)
210
211         return {
212             vendors,
213             av_agreement_statuses,
214             av_agreement_closure_reasons,
215             av_agreement_renewal_priorities,
216             av_agreement_user_roles,
217             av_agreement_license_statuses,
218             av_agreement_license_location,
219             av_agreement_relationships,
220             max_allowed_packet,
221         }
222     },
223     data() {
224         return {
225             agreement: {
226                 agreement_id: null,
227                 name: "",
228                 vendor_id: null,
229                 description: "",
230                 status: "",
231                 closure_reason: "",
232                 is_perpetual: false,
233                 renewal_priority: "",
234                 license_info: "",
235                 periods: [],
236                 user_roles: [],
237                 agreement_licenses: [],
238                 agreement_relationships: [],
239                 documents: [],
240             },
241             initialized: false,
242         }
243     },
244     beforeRouteEnter(to, from, next) {
245         next(vm => {
246             if (to.params.agreement_id) {
247                 vm.agreement = vm.getAgreement(to.params.agreement_id)
248             } else {
249                 vm.initialized = true
250             }
251         })
252     },
253     methods: {
254         async getAgreement(agreement_id) {
255             const agreement = await fetchAgreement(agreement_id)
256             this.agreement = agreement
257             this.initialized = true
258         },
259         checkForm(agreement) {
260             let errors = []
261
262             let agreement_licenses = agreement.agreement_licenses
263             // Do not use al.license.name here! Its name is not the one linked with al.license_id
264             // At this point al.license is meaningless, form/template only modified al.license_id
265             const license_ids = agreement_licenses.map(al => al.license_id)
266             const duplicate_license_ids = license_ids.filter(
267                 (id, i) => license_ids.indexOf(id) !== i
268             )
269
270             if (duplicate_license_ids.length) {
271                 errors.push(this.$__("A license is used several times"))
272             }
273
274             const related_agreement_ids = agreement.agreement_relationships.map(
275                 rs => rs.related_agreement_id
276             )
277             const duplicate_related_agreement_ids =
278                 related_agreement_ids.filter(
279                     (id, i) => related_agreement_ids.indexOf(id) !== i
280                 )
281
282             if (duplicate_related_agreement_ids.length) {
283                 errors.push(
284                     this.$__(
285                         "An agreement is used as relationship several times"
286                     )
287                 )
288             }
289
290             if (
291                 agreement_licenses.filter(al => al.status == "controlling")
292                     .length > 1
293             ) {
294                 errors.push(this.$__("Only one controlling license is allowed"))
295             }
296
297             if (
298                 agreement_licenses.filter(al => al.status == "controlling")
299                     .length > 1
300             ) {
301                 errors.push(this.$__("Only one controlling license is allowed"))
302             }
303
304             let documents_with_uploaded_files = agreement.documents.filter(
305                 doc => typeof doc.file_content !== "undefined"
306             )
307             if (
308                 documents_with_uploaded_files.filter(
309                     doc => atob(doc.file_content).length >= max_allowed_packet
310                 ).length >= 1
311             ) {
312                 errors.push(
313                     this.$__("File size exceeds maximum allowed: %s MB").format(
314                         (max_allowed_packet / (1024 * 1024)).toFixed(2)
315                     )
316                 )
317             }
318             errors.forEach(function (e) {
319                 setWarning(e)
320             })
321             return !errors.length
322         },
323         onSubmit(e) {
324             e.preventDefault()
325
326             //let agreement= Object.assign( {} ,this.agreement); // copy
327             let agreement = JSON.parse(JSON.stringify(this.agreement)) // copy
328
329             if (!this.checkForm(agreement)) {
330                 return false
331             }
332
333             let apiUrl = "/api/v1/erm/agreements"
334
335             let method = "POST"
336             if (agreement.agreement_id) {
337                 method = "PUT"
338                 apiUrl += "/" + agreement.agreement_id
339             }
340             delete agreement.agreement_id
341             delete agreement.vendor
342             agreement.is_perpetual = agreement.is_perpetual ? true : false
343
344             if (agreement.vendor_id == "") {
345                 agreement.vendor_id = null
346             }
347
348             agreement.periods = agreement.periods.map(
349                 ({ agreement_id, agreement_period_id, ...keepAttrs }) =>
350                     keepAttrs
351             )
352
353             agreement.user_roles = agreement.user_roles.map(
354                 ({ patron, patron_str, ...keepAttrs }) => keepAttrs
355             )
356
357             agreement.agreement_licenses = agreement.agreement_licenses.map(
358                 ({
359                     license,
360                     agreement_id,
361                     agreement_license_id,
362                     ...keepAttrs
363                 }) => keepAttrs
364             )
365
366             agreement.agreement_relationships =
367                 agreement.agreement_relationships.map(
368                     ({ related_agreement, ...keepAttrs }) => keepAttrs
369                 )
370
371             agreement.documents = agreement.documents.map(
372                 ({ file_type, uploaded_on, ...keepAttrs }) => keepAttrs
373             )
374
375             delete agreement.agreement_packages
376
377             const options = {
378                 method: method,
379                 body: JSON.stringify(agreement),
380                 headers: {
381                     "Content-Type": "application/json;charset=utf-8",
382                 },
383             }
384
385             fetch(apiUrl, options)
386                 .then(response => {
387                     if (response.status == 200) {
388                         this.$router.push("/cgi-bin/koha/erm/agreements")
389                         setMessage(this.$__("Agreement updated"))
390                     } else if (response.status == 201) {
391                         this.$router.push("/cgi-bin/koha/erm/agreements")
392                         setMessage(this.$__("Agreement created"))
393                     } else {
394                         setError(response.message || response.statusText)
395                     }
396                 })
397                 .catch(error => {
398                     this.setError(error)
399                 })
400         },
401         onStatusChanged(e) {
402             if (e.authorised_value != "closed") {
403                 this.agreement.closure_reason = ""
404             }
405         },
406     },
407     components: {
408         AgreementPeriods,
409         AgreementUserRoles,
410         AgreementLicenses,
411         AgreementRelationships,
412         Documents,
413     },
414     name: "AgreementsFormAdd",
415 }
416 </script>