Bug 33066: Introduce a KohaTable Vue component
[koha.git] / koha-tmpl / intranet-tmpl / prog / js / vue / components / ERM / AgreementsList.vue
1 <template>
2     <div v-if="!initialized">{{ $__("Loading") }}</div>
3     <div v-else id="agreements_list">
4         <Toolbar v-if="before_route_entered" />
5         <fieldset v-if="agreement_count > 0" class="filters">
6             <label for="expired_filter">{{ $__("Filter by expired") }}:</label>
7             <input
8                 type="checkbox"
9                 id="expired_filter"
10                 v-model="filters.by_expired"
11                 @keyup.enter="filter_table"
12             />
13             {{ $__("on") }}
14             <flat-pickr
15                 id="max_expiration_date_filter"
16                 v-model="filters.max_expiration_date"
17                 :config="fp_config"
18             />
19
20             <label for="by_mine_filter">{{ $__("Show mine only") }}:</label>
21             <input
22                 type="checkbox"
23                 id="by_mine_filter"
24                 v-model="filters.by_mine"
25                 @keyup.enter="filter_table"
26             />
27
28             <input
29                 @click="filter_table"
30                 id="filter_table"
31                 type="button"
32                 :value="$__('Filter')"
33             />
34         </fieldset>
35         <div v-if="agreement_count > 0" class="page-section">
36             <KohaTable
37                 ref="table"
38                 v-bind="tableOptions"
39                 @show="doShow"
40                 @edit="doEdit"
41                 @delete="doDelete"
42             ></KohaTable>
43         </div>
44         <div v-else class="dialog message">
45             {{ $__("There are no agreements defined") }}
46         </div>
47     </div>
48 </template>
49
50 <script>
51 import flatPickr from "vue-flatpickr-component"
52 import Toolbar from "./AgreementsToolbar.vue"
53 import { inject, createVNode, render, ref } from "vue"
54 import { APIClient } from "../../fetch/api-client.js"
55 import { storeToRefs } from "pinia"
56 import { build_url } from "../../composables/datatables"
57 import KohaTable from "../KohaTable.vue"
58
59 export default {
60     setup() {
61         const vendorStore = inject("vendorStore")
62         const { vendors } = storeToRefs(vendorStore)
63
64         const AVStore = inject("AVStore")
65         const { get_lib_from_av, map_av_dt_filter } = AVStore
66
67         const { setConfirmationDialog, setMessage } = inject("mainStore")
68
69         const table = ref()
70
71         return {
72             vendors,
73             get_lib_from_av,
74             map_av_dt_filter,
75             logged_in_user,
76             table,
77             setConfirmationDialog,
78             setMessage,
79             escape_str,
80             agreement_table_settings,
81         }
82     },
83     data: function () {
84         return {
85             fp_config: flatpickr_defaults,
86             agreement_count: 0,
87             initialized: false,
88             filters: {
89                 by_expired: this.$route.query.by_expired || false,
90                 max_expiration_date:
91                     this.$route.query.max_expiration_date || "",
92                 by_mine: this.$route.query.by_mine || false,
93             },
94             before_route_entered: false,
95             building_table: false,
96             tableOptions: {
97                 columns: this.getTableColumns(),
98                 url: () => this.table_url(),
99                 table_settings: this.agreement_table_settings,
100                 add_filters: true,
101                 actions: {
102                     0: ["show"],
103                     "-1": ["edit", "delete"],
104                 },
105             },
106         }
107     },
108     beforeRouteEnter(to, from, next) {
109         next(vm => {
110             vm.before_route_entered = true // FIXME This is ugly, but we need to distinguish when it's used as main component or child component (from EHoldingsEBSCOPAckagesShow for instance)
111             if (!vm.building_table) {
112                 vm.building_table = true
113                 vm.getAgreementCount()
114                 vm.initialized = true
115             }
116         })
117     },
118     methods: {
119         async getAgreementCount() {
120             const client = APIClient.erm
121             await client.agreements.count().then(
122                 count => {
123                     this.agreement_count = count
124                 },
125                 error => {}
126             )
127         },
128         doShow: function (agreement, dt, event) {
129             event.preventDefault()
130             this.$router.push(
131                 "/cgi-bin/koha/erm/agreements/" + agreement.agreement_id
132             )
133         },
134         doEdit: function (agreement, dt, event) {
135             this.$router.push(
136                 "/cgi-bin/koha/erm/agreements/edit/" + agreement.agreement_id
137             )
138         },
139         doDelete: function (agreement, dt, event) {
140             this.setConfirmationDialog(
141                 {
142                     title: this.$__(
143                         "Are you sure you want to delete this agreement?"
144                     ),
145                     message: agreement.name,
146                     accept_label: this.$__("Yes, delete"),
147                     cancel_label: this.$__("No, do not delete"),
148                 },
149                 () => {
150                     const client = APIClient.erm
151                     client.agreements.delete(agreement.agreement_id).then(
152                         success => {
153                             this.setMessage(
154                                 this.$__("Agreement %s deleted").format(
155                                     agreement.name
156                                 ),
157                                 true
158                             )
159                             dt.draw()
160                         },
161                         error => {}
162                     )
163                 }
164             )
165         },
166         table_url: function () {
167             let url = "/api/v1/erm/agreements"
168             if (this.filters.by_expired)
169                 url +=
170                     "?max_expiration_date=" + this.filters.max_expiration_date
171             return url
172         },
173         select_agreement: function (agreement_id) {
174             this.$emit("select-agreement", agreement_id)
175             this.$emit("close")
176         },
177         filter_table: async function () {
178             if (this.before_route_entered) {
179                 let new_route = build_url(
180                     "/cgi-bin/koha/erm/agreements",
181                     this.filters
182                 )
183                 this.$router.push(new_route)
184             }
185             if (this.filters.by_expired) {
186                 if (!this.filters.max_expiration_date)
187                     this.filters.max_expiration_date = new Date()
188                         .toISOString()
189                         .substring(0, 10)
190             }
191             this.$refs.table.redraw(this.table_url())
192         },
193         build_datatable: function () {
194             let show_agreement = this.show_agreement
195             let edit_agreement = this.edit_agreement
196             let delete_agreement = this.delete_agreement
197             let select_agreement = this.select_agreement
198             let get_lib_from_av = this.get_lib_from_av
199             let map_av_dt_filter = this.map_av_dt_filter
200             let datatable_url = this.datatable_url
201             let default_search = this.$route.query.q
202             let actions = this.before_route_entered ? "edit_delete" : "select"
203             let filters = this.filters
204             let table_id = this.table_id
205             let logged_in_user = this.logged_in_user
206
207             window["vendors"] = this.vendors.map(e => {
208                 e["_id"] = e["id"]
209                 e["_str"] = e["name"]
210                 return e
211             })
212             let vendors_map = this.vendors.reduce((map, e) => {
213                 map[e.id] = e
214                 return map
215             }, {})
216             let avs = [
217                 "av_agreement_statuses",
218                 "av_agreement_closure_reasons",
219                 "av_agreement_renewal_priorities",
220             ]
221             avs.forEach(function (av_cat) {
222                 window[av_cat] = map_av_dt_filter(av_cat)
223             })
224
225             window["av_agreement_is_perpetual"] = [
226                 { _id: 0, _str: _("No") },
227                 { _id: 1, _str: _("Yes") },
228             ]
229
230             let additional_filters = {
231                 "user_roles.user_id": function () {
232                     return filters.by_mine ? logged_in_user.borrowernumber : ""
233                 },
234             }
235             const table = $("#" + table_id).kohaTable(
236                 {
237                     ajax: {
238                         url: datatable_url,
239                     },
240                     embed: ["user_roles"],
241                     order: [[0, "asc"]],
242                     autoWidth: false,
243                     search: { search: default_search },
244                     columnDefs: [
245                         {
246                             targets: [0, 2],
247                             render: function (data, type, row, meta) {
248                                 if (type == "display") {
249                                     return escape_str(data)
250                                 }
251                                 return data
252                             },
253                         },
254                     ],
255                     columns: [
256                         {
257                             title: __("Name"),
258                             data: "me.agreement_id:me.name",
259                             searchable: true,
260                             orderable: true,
261                             render: function (data, type, row, meta) {
262                                 // Rendering done in drawCallback
263                                 return ""
264                             },
265                         },
266                         {
267                             title: __("Vendor"),
268                             data: "vendor_id",
269                             searchable: true,
270                             orderable: true,
271                             render: function (data, type, row, meta) {
272                                 return row.vendor_id != undefined
273                                     ? escape_str(
274                                           vendors_map[row.vendor_id].name
275                                       )
276                                     : ""
277                             },
278                         },
279                         {
280                             title: __("Description"),
281                             data: "description",
282                             searchable: true,
283                             orderable: true,
284                         },
285                         {
286                             title: __("Status"),
287                             data: "status",
288                             searchable: true,
289                             orderable: true,
290                             render: function (data, type, row, meta) {
291                                 return escape_str(
292                                     get_lib_from_av(
293                                         "av_agreement_statuses",
294                                         row.status
295                                     )
296                                 )
297                             },
298                         },
299                         {
300                             title: __("Closure reason"),
301                             data: "closure_reason",
302                             searchable: true,
303                             orderable: true,
304                             render: function (data, type, row, meta) {
305                                 return escape_str(
306                                     get_lib_from_av(
307                                         "av_agreement_closure_reasons",
308                                         row.closure_reason
309                                     )
310                                 )
311                             },
312                         },
313                         {
314                             title: __("Is perpetual"),
315                             data: "is_perpetual",
316                             searchable: true,
317                             orderable: true,
318                             render: function (data, type, row, meta) {
319                                 return escape_str(
320                                     row.is_perpetual ? _("Yes") : _("No")
321                                 )
322                             },
323                         },
324                         {
325                             title: __("Renewal priority"),
326                             data: "renewal_priority",
327                             searchable: true,
328                             orderable: true,
329                             render: function (data, type, row, meta) {
330                                 return escape_str(
331                                     get_lib_from_av(
332                                         "av_agreement_renewal_priorities",
333                                         row.renewal_priority
334                                     )
335                                 )
336                             },
337                         },
338                         {
339                             title: __("Actions"),
340                             data: function (row, type, val, meta) {
341                                 return '<div class="actions"></div>'
342                             },
343                             className: "actions noExport",
344                             searchable: false,
345                             orderable: false,
346                         },
347                     ],
348                     drawCallback: function (settings) {
349                         var api = new $.fn.dataTable.Api(settings)
350
351                         if (actions == "edit_delete") {
352                             $.each(
353                                 $(this).find("td .actions"),
354                                 function (index, e) {
355                                     let tr = $(this).parent().parent()
356                                     let agreement_id = api
357                                         .row(tr)
358                                         .data().agreement_id
359                                     let agreement_name = api.row(tr).data().name
360                                     let editButton = createVNode(
361                                         "a",
362                                         {
363                                             class: "btn btn-default btn-xs",
364                                             role: "button",
365                                             onClick: () => {
366                                                 edit_agreement(agreement_id)
367                                             },
368                                         },
369                                         [
370                                             createVNode("i", {
371                                                 class: "fa fa-pencil",
372                                                 "aria-hidden": "true",
373                                             }),
374                                             __("Edit"),
375                                         ]
376                                     )
377
378                                     let deleteButton = createVNode(
379                                         "a",
380                                         {
381                                             class: "btn btn-default btn-xs",
382                                             role: "button",
383                                             onClick: () => {
384                                                 delete_agreement(
385                                                     agreement_id,
386                                                     agreement_name
387                                                 )
388                                             },
389                                         },
390                                         [
391                                             createVNode("i", {
392                                                 class: "fa fa-trash",
393                                                 "aria-hidden": "true",
394                                             }),
395                                             __("Delete"),
396                                         ]
397                                     )
398
399                                     let n = createVNode("span", {}, [
400                                         editButton,
401                                         " ",
402                                         deleteButton,
403                                     ])
404                                     render(n, e)
405                                 }
406                             )
407                         } else {
408                             $.each(
409                                 $(this).find("td .actions"),
410                                 function (index, e) {
411                                     let tr = $(this).parent().parent()
412                                     let agreement_id = api
413                                         .row(tr)
414                                         .data().agreement_id
415                                     let selectButton = createVNode(
416                                         "a",
417                                         {
418                                             class: "btn btn-default btn-xs",
419                                             role: "button",
420                                             onClick: () => {
421                                                 select_agreement(agreement_id)
422                                             },
423                                         },
424                                         [
425                                             createVNode("i", {
426                                                 class: "fa fa-check",
427                                                 "aria-hidden": "true",
428                                             }),
429                                             __("Select"),
430                                         ]
431                                     )
432
433                                     let n = createVNode("span", {}, [
434                                         selectButton,
435                                     ])
436                                     render(n, e)
437                                 }
438                             )
439                         }
440
441                         $.each(
442                             $(this).find("tbody tr td:first-child"),
443                             function (index, e) {
444                                 let tr = $(this).parent()
445                                 let row = api.row(tr).data()
446                                 if (!row) return // Happen if the table is empty
447                                 let n = createVNode(
448                                     "a",
449                                     {
450                                         role: "button",
451                                         onClick: () => {
452                                             show_agreement(row.agreement_id)
453                                         },
454                                     },
455                                     `${row.name} (#${row.agreement_id})`
456                                 )
457                                 render(n, e)
458                             }
459                         )
460                     },
461                     preDrawCallback: function (settings) {
462                         $("#" + table_id)
463                             .find("thead th")
464                             .eq(1)
465                             .attr("data-filter", "vendors")
466                         $("#" + table_id)
467                             .find("thead th")
468                             .eq(3)
469                             .attr("data-filter", "av_agreement_statuses")
470                         $("#" + table_id)
471                             .find("thead th")
472                             .eq(4)
473                             .attr("data-filter", "av_agreement_closure_reasons")
474                         $("#" + table_id)
475                             .find("thead th")
476                             .eq(5)
477                             .attr("data-filter", "av_agreement_is_perpetual")
478                         $("#" + table_id)
479                             .find("thead th")
480                             .eq(6)
481                             .attr(
482                                 "data-filter",
483                                 "av_agreement_renewal_priorities"
484                             )
485                     },
486                 },
487                 agreement_table_settings,
488                 1,
489                 additional_filters
490             )
491         },
492         getTableColumns: function () {
493             let get_lib_from_av = this.get_lib_from_av
494             let escape_str = this.escape_str
495             window["vendors"] = this.vendors.map(e => {
496                 e["_id"] = e["id"]
497                 e["_str"] = e["name"]
498                 return e
499             })
500             let vendors_map = this.vendors.reduce((map, e) => {
501                 map[e.id] = e
502                 return map
503             }, {})
504             let avs = [
505                 "av_agreement_statuses",
506                 "av_agreement_closure_reasons",
507                 "av_agreement_renewal_priorities",
508             ]
509             let c = this
510             avs.forEach(function (av_cat) {
511                 window[av_cat] = c.map_av_dt_filter(av_cat)
512             })
513
514             window["av_agreement_is_perpetual"] = [
515                 { _id: 0, _str: _("No") },
516                 { _id: 1, _str: _("Yes") },
517             ]
518             return [
519                 {
520                     title: __("Name"),
521                     data: "me.agreement_id:me.name",
522                     searchable: true,
523                     orderable: true,
524                     render: function (data, type, row, meta) {
525                         // Rendering done in drawCallback
526                         return (
527                             '<a href="/cgi-bin/koha/erm/agreements/' +
528                             row.agreement_id +
529                             '" class="show">show</a>'
530                         )
531                     },
532                 },
533                 {
534                     title: __("Vendor"),
535                     data: "vendor_id",
536                     searchable: true,
537                     orderable: true,
538                     render: function (data, type, row, meta) {
539                         return row.vendor_id != undefined
540                             ? escape_str(vendors_map[row.vendor_id].name)
541                             : ""
542                     },
543                 },
544                 {
545                     title: __("Description"),
546                     data: "description",
547                     searchable: true,
548                     orderable: true,
549                 },
550                 {
551                     title: __("Status"),
552                     data: "status",
553                     searchable: true,
554                     orderable: true,
555                     render: function (data, type, row, meta) {
556                         return escape_str(
557                             get_lib_from_av("av_agreement_statuses", row.status)
558                         )
559                     },
560                 },
561                 {
562                     title: __("Closure reason"),
563                     data: "closure_reason",
564                     searchable: true,
565                     orderable: true,
566                     render: function (data, type, row, meta) {
567                         return escape_str(
568                             get_lib_from_av(
569                                 "av_agreement_closure_reasons",
570                                 row.closure_reason
571                             )
572                         )
573                     },
574                 },
575                 {
576                     title: __("Is perpetual"),
577                     data: "is_perpetual",
578                     searchable: true,
579                     orderable: true,
580                     render: function (data, type, row, meta) {
581                         return escape_str(row.is_perpetual ? _("Yes") : _("No"))
582                     },
583                 },
584                 {
585                     title: __("Renewal priority"),
586                     data: "renewal_priority",
587                     searchable: true,
588                     orderable: true,
589                     render: function (data, type, row, meta) {
590                         return escape_str(
591                             get_lib_from_av(
592                                 "av_agreement_renewal_priorities",
593                                 row.renewal_priority
594                             )
595                         )
596                     },
597                 },
598                 {
599                     title: __("Actions"),
600                     data: function (row, type, val, meta) {
601                         return '<div class="actions"></div>'
602                     },
603                     className: "actions noExport",
604                     searchable: false,
605                     orderable: false,
606                 },
607             ]
608         },
609     },
610     mounted() {
611         if (!this.building_table) {
612             this.building_table = true
613             this.getAgreementCount()
614         }
615     },
616     components: { flatPickr, Toolbar, KohaTable },
617     name: "AgreementsList",
618     emits: ["select-agreement", "close"],
619 }
620 </script>
621
622 <style scoped>
623 #agreement_list {
624     display: table;
625 }
626 .filters > label[for="by_mine_filter"],
627 .filters > input[type="checkbox"],
628 .filters > input[type="button"] {
629     margin-left: 1rem;
630 }
631 </style>