View | Details | Raw Unified | Return to bug 15522
Collapse All | Expand All

(-)a/koha-tmpl/intranet-tmpl/prog/js/src/admin/components/datepicker.vue (+33 lines)
Line 0 Link Here
1
<template>
2
    <input type="text" :class="{[random]: true}" :value="date" style="width: calc(100% - 16px); display: inline-block"/>
3
</template>
4
<script lang="ts">
5
import {defineComponent} from 'vue'
6
export default defineComponent({
7
    props: [
8
        'date'
9
    ],
10
    name: 'DatePicker',
11
    computed: {
12
        random() {
13
            return 'rnd_'+Math.round(Math.random() * 1000)
14
        }
15
    },
16
    mounted() {
17
        const self = this;
18
        this.$nextTick(()=>{
19
            $('.'+self.random).datepicker({
20
                onClose: (dateText, inst) => {
21
                    validate_date(dateText, inst)
22
                }
23
            }).on("change", function () {
24
                if (!is_valid_date($(this).val())) {
25
                    $(this).val("")
26
                }
27
                self.$emit('update:date', $(this).val())
28
                self.$emit('change');
29
            });
30
        })
31
    }
32
})
33
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/admin/policy/components/policyMain.vue (+351 lines)
Line 0 Link Here
1
<template>
2
    <div>
3
        <div class="group" v-for="group in groups" :key="group.key">
4
            <div  class="row">
5
                <h2 class="col-md-9">{{group.title}}</h2>
6
                <div class="col-md-3">
7
                    <button @click="group.expand = !group.expand">{{group.expand ? collapseText : expandText}}</button>
8
                </div>
9
            </div>
10
            <div class="text-muted">
11
                {{group.description}}
12
            </div>
13
            <form v-if="group.expand" class="form-horizontal">
14
                <template v-for="panel in group.panels" :key="panel.key">
15
                    <div class="panel panel-default" v-if="has_kinds(group.key, panel.key)">
16
                        <div class="panel-heading">
17
                            <div class="row">
18
                                <div class="col-md-11">{{self[panel.key+'_panel_heading']}}</div>
19
                                <div class="col-md-1">
20
                                    <span class="fa fa-pencil" @click="panel.edit = !panel.edit"></span>
21
                                </div>
22
                            </div>
23
                        </div>
24
                        <div class="panel-body">
25
                            <div v-for="(rule, key) in effective_rules[group.key][panel.key]" :key="key" class="form-group">
26
                                <label for="{{key}}" class="col-md-3 control-label">{{rule.kind.description}}</label>
27
                                <div class="col-md-9" v-if="!panel.edit">
28
                                    <div class="form-control-static">{{get_rule_value(rule)}}</div>
29
                                    <div class="text-muted" v-if="rule.rule._use_default && !rule.is_default">Using default value</div>
30
                                </div>
31
                                <div class="col-md-9" v-if="panel.edit">
32
                                    <template v-if="!rule.is_default">
33
                                        <div>
34
                                            <input type="checkbox" :id="key+'_default'" v-model="rule.rule._use_default" />Use default value ({{get_default_value(rule)}})
35
                                        </div>
36
                                        <div>
37
                                            <input v-if="!rule.kind.choices && rule.kind.type != 'date'" :readonly="rule.rule._use_default" :disabled="rule.rule._use_default" type="text" :id="key" v-model="rule.rule.rule_value" @change="edit(rule.rule)" class="form-control" :class="{datepicker: rule.kind.type == 'date'}"/>
38
                                            <date-picker v-if="!rule.kind.choices && rule.kind.type == 'date'" :readonly="rule.rule._use_default" :disabled="rule.rule._use_default" :id="key" v-model:date="rule.rule.rule_value" @change="edit(rule.rule)" class="form-control"/>
39
                                            <select v-if="rule.kind.choices" :readonly="rule.rule._use_default" :disabled="rule.rule._use_default" :id="key" v-model="rule.rule.rule_value" @change="edit(rule.rule)" class="form-control">
40
                                                <option v-for="choice in rule.kind.choices" :key="choice[0]" :value="choice[0]">{{choice[1]}}</option>
41
                                            </select>
42
                                        </div>
43
                                    </template>
44
                                    <template v-if="rule.is_default">
45
                                        <input v-if="!rule.kind.choices && rule.kind.type != 'date'" type="text" :id="key" v-model="rule.default.rule_value" @change="edit(rule.default)" class="form-control" :class="{datepicker: rule.kind.type == 'date'}"/>
46
                                        <date-picker v-if="!rule.kind.choices && rule.kind.type == 'date'" :id="key" v-model:date="rule.default.rule_value" @change="edit(rule.default)" class="form-control"/>
47
                                        <select v-if="rule.kind.choices" :id="key" v-model="rule.default.rule_value" @change="edit(rule.default)" class="form-control">
48
                                            <option v-for="choice in rule.kind.choices" :key="choice[0]" :value="choice[0]">{{choice[1]}}</option>
49
                                        </select>
50
                                    </template>
51
52
                                </div>
53
                            </div>
54
                        </div>
55
                    </div>
56
                </template>
57
            </form>
58
        </div>
59
    </div>
60
</template>
61
<script lang="ts">
62
    import { defineComponent, readonly, reactive } from 'vue'
63
    import DatePicker from '../../components/datepicker.vue'
64
65
    export default defineComponent({
66
        name: 'PolicyMain',
67
        components: {
68
            DatePicker
69
        },
70
        props: [
71
            'current',
72
            'libraries',
73
            'categories',
74
            'itemtypes',
75
            'doSave',
76
            'doClear'
77
        ],
78
        data() {
79
            return {
80
                self: this,
81
                expandText: _('Expand'),
82
                collapseText: _('Collapse'),
83
                kinds: {},
84
                origRules: {},
85
                rules: {},
86
                groups: [
87
                    {
88
                        key: 'circulation',
89
                        title: _("Circulation rules"),
90
                        description: _("A brief and tiny text that explains how awesome and great cirulation rules are"),
91
                        expanded: false,
92
                        panels: [
93
                            {
94
                                scope: ['branchcode'],
95
                                key: 'lib'
96
                            },
97
                            {
98
                                scope: ['branchcode', 'categorycode'],
99
                                key: 'cat'
100
                            },
101
                            {
102
                                scope: ['branchcode', 'itemtype'],
103
                                key: 'itype'
104
                            },
105
                            {
106
                                scope: ['branchcode', 'categorycode', 'itemtype'],
107
                                key: 'all'
108
                            }
109
                        ]
110
                    },
111
                    {
112
                        key: 'fines',
113
                        title: _("Fines rules"),
114
                        description: _("A long and tedious text that explains how deadly serious fines rules are"),
115
                        expanded: false,
116
                        panels: [
117
                            {
118
                                scope: ['branchcode'],
119
                                key: 'lib'
120
                            },
121
                            {
122
                                scope: ['branchcode', 'categorycode'],
123
                                key: 'cat'
124
                            },
125
                            {
126
                                scope: ['branchcode', 'itemtype'],
127
                                key: 'itype'
128
                            },
129
                            {
130
                                scope: ['branchcode', 'categorycode', 'itemtype'],
131
                                key: 'all'
132
                            }
133
                        ]
134
                    },
135
                    {
136
                        key: 'holds',
137
                        title: _("Holds rules"),
138
                        description: _("A light text that explains why you cannot live without holds rules"),
139
                        expanded: false,
140
                        panels: [
141
                            {
142
                                scope: ['branchcode'],
143
                                key: 'lib'
144
                            },
145
                            {
146
                                scope: ['branchcode', 'categorycode'],
147
                                key: 'cat'
148
                            },
149
                            {
150
                                scope: ['branchcode', 'itemtype'],
151
                                key: 'itype'
152
                            },
153
                            {
154
                                scope: ['branchcode', 'categorycode', 'itemtype'],
155
                                key: 'all'
156
                            }
157
                        ]
158
                    },
159
                ],
160
                edited: []
161
            }
162
        },
163
        async created() {
164
            this.kinds = await fetch( "/api/v1/circulation-rules/kinds" ).then( result => result.json() )
165
            this.rules = await fetch( "/api/v1/circulation-rules" ).then( result => result.json() )
166
            this.origRules = readonly(JSON.parse(JSON.stringify(this.rules)));
167
        },
168
        computed: {
169
            lib_panel_heading() {
170
                return 'For '+this.get_current('lib')
171
            },
172
            cat_panel_heading() {
173
                return 'For '+this.get_current('lib')+' and '+this.get_current('cat')
174
            },
175
            itype_panel_heading() {
176
                return 'For '+this.get_current('lib')+' and '+this.get_current('itype')
177
            },
178
            all_panel_heading() {
179
                return 'For '+this.get_current('lib')+', '+this.get_current('cat')+' and '+this.get_current('itype')
180
            },
181
            effective_rules() {
182
                const groups = {}
183
                const missing_rules = [];
184
                this.groups.forEach(group => {
185
                    groups[group.key] = {},
186
                    group.panels.forEach(panel => {
187
                        groups[group.key][panel.key] = {}
188
                        Object.keys(this.kinds).filter(key => {
189
                            const kind = this.kinds[key]
190
                            if(!kind.description) return;
191
                            if(kind.group == group.key && !this.simetric_diff(kind.scope, panel.scope).length) {
192
                                groups[group.key][panel.key][key] = {kind}
193
                                const rules = this.rules
194
                                    .filter(rule => rule.rule_name == key)
195
                                rules.forEach(rule => {
196
                                        if(rule.branchcode == null) rule.branchcode = '';
197
                                        if(rule.categorycode == null) rule.categorycode = '';
198
                                        if(rule.itemtype == null) rule.itemtype = '';
199
                                    })
200
                                groups[group.key][panel.key][key].default = rules.find(rule => rule.branchcode == '' && rule.categorycode == '' && rule.itemtype == '')
201
                                if(!groups[group.key][panel.key][key].default) {
202
                                    missing_rules.push({
203
                                        rule_value: '',
204
                                        rule_name: key,
205
                                        branchcode: '',
206
                                        categorycode: '',
207
                                        itemtype: ''
208
                                    })
209
                                }
210
                                if(panel.key == 'lib' && this.current.lib == '') {
211
                                    groups[group.key][panel.key][key].is_default = true
212
                                } else if(panel.key == 'cat' && this.current.lib == '' && this.current.cat == '') {
213
                                    groups[group.key][panel.key][key].is_default = true
214
                                } else if(panel.key == 'itype' && this.current.lib == '' && this.current.itype == '') {
215
                                    groups[group.key][panel.key][key].is_default = true
216
                                } else if(panel.key == 'all' && this.current.lib == '' && this.current.cat == '' && this.current.itype == '') {
217
                                    groups[group.key][panel.key][key].is_default = true
218
                                } else {
219
                                    groups[group.key][panel.key][key].is_default = false
220
                                }
221
222
                                groups[group.key][panel.key][key].rule = rules.find(rule => rule.branchcode == this.current.lib && rule.categorycode == this.current.cat && rule.itemtype == this.current.itype)
223
                                if(!groups[group.key][panel.key][key].rule) {
224
                                    missing_rules.push({
225
                                        rule_name: key,
226
                                        rule_value: null,
227
                                        branchcode: this.current.lib,
228
                                        categorycode: this.current.cat,
229
                                        itemtype: this.current.itype,
230
                                        _use_default: true
231
                                    });
232
                                }
233
                            }
234
                        })
235
                    })
236
                })
237
                if(missing_rules.length) {
238
                    this.$nextTick(()=>{
239
                        this.rules = this.rules.concat(missing_rules)
240
                    })
241
                    return null
242
                }
243
                return groups
244
            }
245
        },
246
        methods: {
247
            simetric_diff(left, right) {
248
                const rightSet = new Set(right)
249
                const leftSet = new Set(left)
250
                return left.filter(item => !rightSet.has(item)).concat(right.filter(item => !leftSet.has(item)))
251
            },
252
            has_kinds(group, panel) {
253
                return this.effective_rules && this.effective_rules[group] && this.effective_rules[group][panel] && Object.keys(this.effective_rules[group][panel]).length
254
            },
255
            get_default_value(rule) {
256
                if(!rule.default) {
257
                   return ''
258
                }
259
                if(rule.kind.choices && rule.kind.choices) {
260
                    const choice = rule.kind.choices.find(choice => choice[0]==rule.default.rule_value)
261
                    if(choice) return choice[1]
262
                }
263
                return rule.default.rule_value
264
            },
265
            get_rule_value(rule) {
266
                if(rule.rule._use_default) return this.get_default_value(rule);
267
                if(rule.kind.choices && rule.kind.choices) {
268
                    const choice = rule.kind.choices.find(choice => choice[0]==rule.rule.rule_value)
269
                    if(choice) return choice[1]
270
                }
271
                return rule.rule.rule_value
272
            },
273
            get_current(type) {
274
                if(type == 'lib') {
275
                    let curr_name = this.libraries.find(lib => lib.code == this.current.lib).name
276
                    return this.current[type]==''?curr_name.toLowerCase():curr_name
277
                }
278
                if(type == 'cat') {
279
                    let curr_name = this.categories.find(cat => cat.code == this.current.cat).name
280
                    return this.current[type]==''?curr_name.toLowerCase():_('%s category').format(curr_name)
281
                }
282
                if(type == 'itype') {
283
                    let curr_name = this.itemtypes.find(itype => itype.code == this.current.itype).name
284
                    return this.current[type]==''?curr_name.toLowerCase():_('%s item type').format(curr_name)
285
                }
286
            },
287
            edit(rule) {
288
                if(!this.edited.includes(rule)) this.edited.push(rule)
289
            },
290
            clear() {
291
                this.rules = reactive(JSON.parse(JSON.stringify(this.origRules)))
292
                this.edited = []
293
            },
294
            save() {
295
                const payload = this.edited
296
                    .filter(rule => {
297
                        return rule.id || !rule._use_default
298
                    })
299
                    .map(origRule => {
300
                        const rule = {
301
                            rule_name: origRule.rule_name,
302
                            rule_value: origRule.id && origRule._use_default?null:origRule.rule_value,
303
                            branchcode: origRule.branchcode == ''?null:origRule.branchcode,
304
                            categorycode: origRule.categorycode == ''?null:origRule.categorycode,
305
                            itemtype: origRule.itemtype == ''?null:origRule.itemtype
306
                        };
307
                        if(origRule.id) rule.id = origRule.id
308
309
                        return rule
310
                    })
311
                fetch( "/api/v1/circulation-rules", {
312
                    method: "POST",
313
                    credentials: "include",
314
                    body: JSON.stringify(payload),
315
                } ).then( response => {
316
                    switch( response.status ) {
317
                        case 200:
318
                            this.edited = []
319
                            this.origRules = readonly(JSON.parse(JSON.stringify(this.rules)))
320
                            humanMsg.displayMsg( '<h3>'+_("Rules saved.")+'</h3>', { className: "humanSuccess" } )
321
                            break
322
                        case 401:
323
                            humanMsg.displayAlert( '<h3>'+_("Rules not saved")+'</h3><p>'+_("Please reload the page to log in again")+'</p>', { className: "humanError" } )
324
                            break
325
                        default:
326
                            humanMsg.displayAlert( '<h3>'+_("Rules not saved")+'</h3><p>'+_("Internal error")+'</p>', { className: "humanError" } )
327
                            break
328
                    }
329
                } )
330
            }
331
        },
332
        watch: {
333
            doSave(val, oldVal) {
334
                if(val) {
335
                    this.save()
336
                }
337
            },
338
            doClear(val, oldVal) {
339
                if(val) {
340
                    this.clear()
341
                }
342
            },
343
            edited: {
344
                handler(val, oldVal) {
345
                this.$emit('edited', val)
346
                },
347
                deep: true
348
            }
349
        }
350
    })
351
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/admin/policy/components/policySidebar.vue (+102 lines)
Line 0 Link Here
1
<template>
2
    <div>
3
        <form>
4
            <div class="form-group">
5
                <label for="choose" class="control-label">Choose list</label>
6
                <select v-model="choose" id="choose" class="form-control">
7
                    <option value="lib">Library</option>
8
                    <option value="cat">Patron category</option>
9
                    <option value="itype">Item type</option>
10
                </select>
11
            </div>
12
            <div class="form-group">
13
                <label for="filter" class="control-label">Filter list</label>
14
                <input type="text" id="filter" v-model="filter"  class="form-control"/>
15
            </div>
16
        </form>
17
        <div class="list-group">
18
            <a class="list-group-item" v-for="item in list" :key="item.code" :class="{active: is_selected(item.code)}" @click.exact="do_select(item.code)" @click.ctrl="add_select(item.code)" @click.meta="add_select(item.code)">
19
                {{item.name}}
20
            </a>
21
        </div>
22
    </div>
23
</template>
24
<script lang="ts">
25
/// <reference path="../main.d.ts" />
26
    import { defineComponent } from 'vue'
27
    export default defineComponent({
28
        name: 'PolicySidebar',
29
        props: [
30
            'current',
31
            'select',
32
            'libraries',
33
            'categories',
34
            'itemtypes'
35
        ],
36
        data() {
37
            return {
38
                choose: 'lib',
39
                filter: null,
40
                libList: [],
41
                catList: [],
42
                itypeList: [],
43
                localCurrent: {
44
                    lib: '',
45
                    cat: '',
46
                    itype: ''
47
                },
48
                localSelected: {
49
                    lib: [''],
50
                    cat: [''],
51
                    itype: ['']
52
                }
53
            }
54
        },
55
        created() {
56
            this.$emit('update:current', this.localCurrent)
57
            this.$emit('update:select', this.localSelected)
58
            this.libList = this.libraries
59
            this.catList = this.categories
60
            this.itypeList = this.itemtypes
61
        },
62
        methods: {
63
            do_select(code) {
64
                this.localCurrent[this.choose] = code
65
                this.localSelected[this.choose] = [code]
66
                this.$emit('update:current', this.localCurrent)
67
                this.$emit('update:select', this.localSelected)
68
            },
69
            add_select(code) {
70
                this.localSelected[this.choose].push(code)
71
                this.$emit('update:select', this.localSelected)
72
            },
73
            is_selected(code) {
74
                return this.localSelected[this.choose].includes(code)
75
            }
76
        },
77
        computed: {
78
            list() {
79
                if(this.choose == 'lib') return this.libList.filter(item => item.show)
80
                if(this.choose == 'cat') return this.catList.filter(item => item.show)
81
                return this.itypeList.filter(item => item.show)
82
            }
83
        },
84
        watch: {
85
            choose() {
86
                this.filter = null;
87
            },
88
            filter() {
89
                if(this.filter == null) {
90
                    this.libList.forEach(item => item.show = true)
91
                    this.catList.forEach(item => item.show = true)
92
                    this.itypeList.forEach(item => item.show = true)
93
                } else {
94
                    let reg = new RegExp(this.filter, 'i')
95
                    this.libList.forEach(item => item.show = reg.test(item.name))
96
                    this.catList.forEach(item => item.show = reg.test(item.name))
97
                    this.itypeList.forEach(item => item.show = reg.test(item.name))
98
                }
99
            }
100
        }
101
    })
102
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/admin/policy/components/policyTags.vue (+68 lines)
Line 0 Link Here
1
<template>
2
    <div>
3
        <div class="panel panel-default">
4
            <div class="panel-heading">
5
                <h3 class="panel-title">Libraries</h3>
6
            </div>
7
            <div class="panel-body">
8
                <div class="container-fluid">
9
                    <div class="row">
10
                        <span class="label col-md-6" v-for="lib in select.lib" :key="lib" :class="{'label-primary': is_current('lib', lib), 'label-default': !is_current('lib', lib)}" @click="set_current('lib', lib)">{{get_name('lib', lib)}}</span>
11
                    </div>
12
                </div>
13
            </div>
14
        </div>
15
        <div class="panel panel-default">
16
            <div class="panel-heading">
17
                <h3 class="panel-title">Patron categories</h3>
18
            </div>
19
            <div class="panel-body">
20
                <div class="container-fluid">
21
                    <div class="row">
22
                        <span class="label col-md-6" v-for="cat in select.cat" :key="cat" :class="{'label-primary': is_current('cat', cat), 'label-default': !is_current('cat', cat)}" @click="set_current('cat', cat)">{{get_name('cat', cat)}}</span>
23
                    </div>
24
                </div>
25
            </div>
26
        </div>
27
        <div class="panel panel-default">
28
            <div class="panel-heading">
29
                <h3 class="panel-title">Item types</h3>
30
            </div>
31
            <div class="panel-body">
32
                <div class="container-fluid">
33
                    <div class="row">
34
                        <span class="label col-md-6" v-for="itype in select.itype" :key="itype" :class="{'label-primary': is_current('itype', itype), 'label-default': !is_current('itype', itype)}" @click="set_current('itype', itype)">{{get_name('itype', itype)}}</span>
35
                    </div>
36
                </div>
37
            </div>
38
        </div>
39
    </div>
40
</template>
41
<script lang="ts">
42
/// <reference path="../main.d.ts" />
43
    import { defineComponent } from 'vue'
44
    export default defineComponent({
45
        name: 'PolicyTags',
46
        props: [
47
            'current',
48
            'select',
49
            'libraries',
50
            'categories',
51
            'itemtypes'
52
        ],
53
        methods: {
54
            is_current(type, code) {
55
                return this.current[type] == code;
56
            },
57
            set_current(type, code) {
58
                this.current[type] = code
59
                this.$emit('update:current', this.current)
60
            },
61
            get_name(type, code) {
62
                if(type == 'lib') return this.libraries.find(item => item.code == code).name
63
                if(type == 'cat') return this.categories.find(item => item.code == code).name
64
                if(type == 'itype') return this.itemtypes.find(item => item.code == code).name
65
            }
66
        }
67
    })
68
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/admin/policy/main.d.ts (+22 lines)
Line 0 Link Here
1
declare function _ (text: string, ...params: any[]): string
2
declare function $ (text: string, ...params: any[]): any
3
declare function validate_date (date: string, obj: object): void
4
declare function is_valid_date (date: string): boolean
5
6
type KOHA = {
7
    BRANCHES: object,
8
    ITEM_TYPES: object,
9
    PATRON_CATEGORIES: object
10
}
11
12
declare interface String {
13
    format(...string):string
14
}
15
16
type HUMANMSG = {
17
    displayMsg(string, any): void,
18
    displayAlert(string, any): void
19
}
20
21
declare var Koha: KOHA
22
 declare var humanMsg:HUMANMSG
(-)a/koha-tmpl/intranet-tmpl/prog/js/src/admin/policy/main.ts (-1 / +73 lines)
Line 0 Link Here
0
- 
1
/// <reference path="main.d.ts" />
2
3
import {createApp} from "vue/dist/vue.esm-bundler";
4
// @ts-ignore
5
import PolicySidebar from "./components/policySidebar.vue";
6
// @ts-ignore
7
import PolicyMain from "./components/policyMain.vue";
8
// @ts-ignore
9
import PolicyTags from "./components/policyTags.vue";
10
11
12
createApp({
13
    template: `
14
    <div class="container-fluid">
15
        <div class="row">
16
            <h1 class="col-md-10">{{title}}</h1>
17
            <div class="col-md-2">
18
                <button type="button" class="btn btn-primary" :disabled="!edited.length" @click="doSave = true">Save</button>
19
                <button type="button" class="btn btn-default" :disabled="!edited.length" @click="doClear = true">Clear</button>
20
            </div>
21
        </div>
22
        <hr/>
23
        <div class="row">
24
            <policy-sidebar class="col-md-3" v-model:current="current" v-model:select="selected" :libraries="libraries" :categories="categories" :itemtypes="itemtypes"></policy-sidebar>
25
            <policy-main class="col-md-7" :current="current" :libraries="libraries" :categories="categories" :itemtypes="itemtypes" :doSave="doSave" :doClear="doClear" @edited="setEdited"></policy-main>
26
            <policy-tags class="col-md-2" v-model:current="current" :select="selected" :libraries="libraries" :categories="categories" :itemtypes="itemtypes"></policy-tags>
27
        </div>
28
    </div>
29
    `,
30
    data() {
31
        return {
32
            title: _('Circulation, fines, and holds rules'),
33
            current: {},
34
            selected: {},
35
            doSave: false,
36
            doClear: false,
37
            edited: [],
38
            saved: false
39
        }
40
    },
41
    computed: {
42
        libraries() {
43
            const libs = Object.keys(Koha.BRANCHES).map(code => {
44
                return {code, name: Koha.BRANCHES[code].branchname, show: true}
45
            })
46
            return [{code: '', name: _('All libraries'), show: true}].concat(libs)
47
        },
48
        categories() {
49
            const cats = Object.keys(Koha.PATRON_CATEGORIES).map(code => {
50
                return {code, name: Koha.PATRON_CATEGORIES[code].description, show: true}
51
            })
52
            return [{code: '', name: _('All categories'), show: true}].concat(cats)
53
        },
54
        itemtypes() {
55
            const itypes = Object.keys(Koha.ITEM_TYPES).map(code => {
56
                return {code, name: Koha.ITEM_TYPES[code].description, show: true}
57
            })
58
            return [{code: '', name: _('All item types'), show: true}].concat(itypes)
59
        }
60
    },
61
    methods: {
62
        setEdited(edited) {
63
            this.edited = edited
64
            this.doSave = false
65
            this.doClear = false
66
        }
67
    },
68
    components: {
69
        PolicySidebar,
70
        PolicyMain,
71
        PolicyTags
72
    }
73
}).mount("#vue-base");

Return to bug 15522