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

(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/KohaTable.vue (+6 lines)
Lines 103-108 export default { Link Here
103
                                        this.$__("Delete") +
103
                                        this.$__("Delete") +
104
                                        "</a>"
104
                                        "</a>"
105
                                )
105
                                )
106
                            } else if (action == "remove") {
107
                                content.push(
108
                                    '<a class="remove btn btn-default btn-xs" role="button"><i class="fa fa-remove"></i> ' +
109
                                        this.$__("Remove") +
110
                                        "</a>"
111
                                )
106
                            }
112
                            }
107
                        })
113
                        })
108
                        return content.join(" ")
114
                        return content.join(" ")
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/Home.vue (+11 lines)
Line 0 Link Here
1
<template></template>
2
3
<script>
4
export default {
5
    data() {
6
        return {}
7
    },
8
    methods: {},
9
    components: {},
10
}
11
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/Main.vue (+194 lines)
Line 0 Link Here
1
<template>
2
    <div v-if="initialized && PreservationModule == 1">
3
        <Breadcrumb />
4
        <div class="main container-fluid">
5
            <div class="row">
6
                <div class="col-sm-10 col-sm-push-2">
7
                    <main>
8
                        <Dialog />
9
                        <router-view />
10
                    </main>
11
                </div>
12
13
                <div class="col-sm-2 col-sm-pull-10">
14
                    <aside>
15
                        <div id="navmenu">
16
                            <div id="navmenulist">
17
                                <h5>{{ $__("Preservation") }}</h5>
18
                                <ul>
19
                                    <li>
20
                                        <router-link
21
                                            to="/cgi-bin/koha/preservation/home.pl"
22
                                        >
23
                                            <i class="fa fa-home"></i>
24
                                            {{ $__("Home") }}</router-link
25
                                        >
26
                                    </li>
27
                                    <li>
28
                                        <router-link
29
                                            to="/cgi-bin/koha/preservation/trains"
30
                                        >
31
                                            <i class="fa fa-train"></i>
32
                                            {{ $__("Trains") }}</router-link
33
                                        >
34
                                    </li>
35
                                    <li>
36
                                        <router-link
37
                                            to="/cgi-bin/koha/preservation/waiting-list"
38
                                        >
39
                                            <i class="fa fa-recycle"></i>
40
                                            {{ $__("Waiting list") }}
41
                                        </router-link>
42
                                    </li>
43
                                    <li>
44
                                        <router-link
45
                                            to="/cgi-bin/koha/preservation/settings"
46
                                        >
47
                                            <i class="fa fa-cog"></i>
48
                                            {{ $__("Settings") }}
49
                                        </router-link>
50
                                    </li>
51
                                </ul>
52
                            </div>
53
                        </div>
54
                    </aside>
55
                </div>
56
            </div>
57
        </div>
58
    </div>
59
    <div class="main container-fluid" v-else>
60
        <Dialog />
61
    </div>
62
</template>
63
64
<script>
65
import { inject } from "vue"
66
import Breadcrumb from "../Breadcrumb.vue"
67
import Dialog from "../Dialog.vue"
68
import { APIClient } from "../../fetch/api-client.js"
69
import "vue-select/dist/vue-select.css"
70
71
export default {
72
    setup() {
73
        const AVStore = inject("AVStore")
74
75
        const mainStore = inject("mainStore")
76
77
        const { loading, loaded, setError } = mainStore
78
79
        const PreservationStore = inject("PreservationStore")
80
        return {
81
            AVStore,
82
            loading,
83
            loaded,
84
            setError,
85
            PreservationStore,
86
        }
87
    },
88
    data() {
89
        return {
90
            initialized: false,
91
            PreservationModule: null,
92
        }
93
    },
94
    beforeCreate() {
95
        this.loading()
96
97
        const fetch_config = () => {
98
            const sysprefs_client = APIClient.sysprefs
99
            const av_client = APIClient.authorised_values
100
            let promises = [
101
                sysprefs_client.sysprefs
102
                    .get("PreservationNotForLoanWaitingListIn")
103
                    .then(
104
                        value => {
105
                            this.PreservationStore.settings.not_for_loan_waiting_list_in =
106
                                value.value
107
                        },
108
                        error => {}
109
                    ),
110
                sysprefs_client.sysprefs
111
                    .get("PreservationNotForLoanDefaultTrainIn")
112
                    .then(
113
                        value => {
114
                            this.PreservationStore.settings.not_for_loan_default_train_in =
115
                                value.value
116
                        },
117
                        error => {}
118
                    ),
119
                av_client.values.get("NOT_LOAN").then(
120
                    values => {
121
                        this.AVStore.av_notforloan = values
122
                    },
123
                    error => {}
124
                ),
125
            ]
126
127
            return Promise.all(promises)
128
        }
129
130
        const sysprefs_client = APIClient.sysprefs
131
        sysprefs_client.sysprefs
132
            .get("PreservationModule")
133
            .then(value => {
134
                this.PreservationModule = value.value
135
                if (this.PreservationModule != 1) {
136
                    return this.setError(
137
                        this.$__(
138
                            'The preservation module is disabled, turn on <a href="/cgi-bin/koha/admin/preferences.pl?tab=&op=search&searchfield=PreservationModule">PreservationModule</a> to use it'
139
                        ),
140
                        false
141
                    )
142
                }
143
                return fetch_config()
144
            })
145
            .then(() => {
146
                this.loaded()
147
                this.initialized = true
148
            })
149
    },
150
151
    components: {
152
        Breadcrumb,
153
        Dialog,
154
    },
155
}
156
</script>
157
158
<style>
159
#navmenulist a.router-link-active {
160
    font-weight: 700;
161
}
162
#menu ul ul,
163
#navmenulist ul ul {
164
    padding-left: 2em;
165
    font-size: 100%;
166
}
167
168
form .v-select {
169
    display: inline-block;
170
    background-color: white;
171
    width: 30%;
172
}
173
174
.v-select,
175
input:not([type="submit"]):not([type="search"]):not([type="button"]):not([type="checkbox"]),
176
textarea {
177
    border-color: rgba(60, 60, 60, 0.26);
178
    border-width: 1px;
179
    border-radius: 4px;
180
    min-width: 30%;
181
}
182
.flatpickr-input {
183
    width: 30%;
184
}
185
186
#navmenulist ul li a.disabled {
187
    color: #666;
188
    pointer-events: none;
189
    font-weight: 700;
190
}
191
#navmenulist ul li a.disabled.router-link-active {
192
    color: #000;
193
}
194
</style>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/Settings.vue (+130 lines)
Line 0 Link Here
1
<template>
2
    <div v-if="!initialized">{{ $__("Loading") }}</div>
3
    <div v-else id="settings">
4
        <h2>
5
            {{ $__("Edit preservation settings") }}
6
        </h2>
7
        <div>
8
            <form @submit="onSubmit($event)">
9
                <fieldset class="rows">
10
                    <legend>{{ $__("General settings") }}</legend>
11
                    <ol>
12
                        <li>
13
                            <label
14
                                class="required"
15
                                for="not_for_loan_waiting_list_in"
16
                                >{{
17
                                    $__(
18
                                        "Status for item added to waiting list"
19
                                    )
20
                                }}:</label
21
                            >
22
                            <v-select
23
                                id="not_for_loan_waiting_list_in"
24
                                v-model="settings.not_for_loan_waiting_list_in"
25
                                label="description"
26
                                :reduce="av => av.value"
27
                                :options="av_notforloan"
28
                            >
29
                                <template #search="{ attributes, events }">
30
                                    <input
31
                                        :required="
32
                                            !settings.not_for_loan_waiting_list_in
33
                                        "
34
                                        class="vs__search"
35
                                        v-bind="attributes"
36
                                        v-on="events"
37
                                    />
38
                                </template>
39
                            </v-select>
40
                        </li>
41
                        <li>
42
                            <label for="not_for_loan_default_train_in"
43
                                >{{
44
                                    $__(
45
                                        "Default status for item added to train"
46
                                    )
47
                                }}:</label
48
                            >
49
                            <v-select
50
                                id="not_for_loan_default_train_in"
51
                                v-model="settings.not_for_loan_default_train_in"
52
                                label="description"
53
                                :reduce="av => av.value"
54
                                :options="av_notforloan"
55
                            />
56
                        </li>
57
                    </ol>
58
                </fieldset>
59
                <fieldset class="action">
60
                    <input type="submit" value="Submit" />
61
                    <router-link
62
                        to="/cgi-bin/koha/preservation/home.pl"
63
                        role="button"
64
                        class="cancel"
65
                        >{{ $__("Cancel") }}</router-link
66
                    >
67
                </fieldset>
68
            </form>
69
            <SettingsProcessings />
70
        </div>
71
    </div>
72
</template>
73
74
<script>
75
import { inject } from "vue"
76
import { APIClient } from "../../fetch/api-client.js"
77
import { storeToRefs } from "pinia"
78
import SettingsProcessings from "./SettingsProcessings.vue"
79
80
export default {
81
    setup() {
82
        const AVStore = inject("AVStore")
83
        const { av_notforloan } = storeToRefs(AVStore)
84
85
        const { setMessage, setWarning } = inject("mainStore")
86
        const PreservationStore = inject("PreservationStore")
87
        const { settings } = storeToRefs(PreservationStore)
88
89
        return { av_notforloan, setMessage, setWarning, settings }
90
    },
91
    data() {
92
        return {
93
            initialized: true,
94
        }
95
    },
96
    methods: {
97
        checkForm(train) {
98
            let errors = []
99
100
            errors.forEach(function (e) {
101
                setWarning(e)
102
            })
103
            return !errors.length
104
        },
105
        onSubmit(e) {
106
            e.preventDefault()
107
            const client = APIClient.sysprefs
108
            client.sysprefs
109
                .update(
110
                    "PreservationNotForLoanWaitingListIn",
111
                    this.settings.not_for_loan_waiting_list_in
112
                )
113
                .then(
114
                    client.sysprefs.update(
115
                        "PreservationNotForLoanDefaultTrainIn",
116
                        this.settings.not_for_loan_default_train_in || 0
117
                    )
118
                )
119
                .then(
120
                    success => {
121
                        this.setMessage(this.$__("Settings updated"), true)
122
                    },
123
                    error => {}
124
                )
125
        },
126
    },
127
    components: { SettingsProcessings },
128
    name: "Settings",
129
}
130
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/SettingsProcessings.vue (+110 lines)
Line 0 Link Here
1
<template>
2
    <fieldset>
3
        <legend>{{ $__("Processings") }}</legend>
4
        <ol>
5
            <li
6
                :id="`processing_${counter}`"
7
                class="rows"
8
                v-for="(processing, counter) in processings"
9
                v-bind:key="counter"
10
            >
11
                <router-link
12
                    :to="`/cgi-bin/koha/preservation/settings/processings/${processing.processing_id}`"
13
                >
14
                    {{ processing.name }}
15
                </router-link>
16
17
                <span class="action_links">
18
                    <a @click="deleteProcessing(processing)"
19
                        ><i class="fa fa-trash"></i>
20
                        {{ $__("Remove this processing") }}</a
21
                    >
22
23
                    <router-link
24
                        :to="`/cgi-bin/koha/preservation/settings/processings/edit/${processing.processing_id}`"
25
                        ><i class="fa fa-pencil"></i>
26
                        {{ $__("Edit this processing") }}</router-link
27
                    >
28
                </span>
29
            </li>
30
        </ol>
31
        <router-link
32
            to="/cgi-bin/koha/preservation/settings/processings/add"
33
            role="button"
34
            class="btn btn-default"
35
            ><font-awesome-icon icon="plus" />
36
            {{ $__("Add new processing") }}</router-link
37
        >
38
    </fieldset>
39
</template>
40
41
<script>
42
import { inject } from "vue"
43
import { APIClient } from "../../fetch/api-client.js"
44
45
export default {
46
    setup() {
47
        const { setConfirmationDialog, setMessage } = inject("mainStore")
48
        return { setConfirmationDialog, setMessage }
49
    },
50
    data() {
51
        return {
52
            processings: [],
53
        }
54
    },
55
    beforeCreate() {
56
        // FIXME Do we want that or a props passed from parent?
57
        const client = APIClient.preservation
58
        client.processings.getAll().then(
59
            processings => {
60
                this.processings = processings
61
            },
62
            error => {}
63
        )
64
    },
65
    methods: {
66
        deleteProcessing(processing) {
67
            this.setConfirmationDialog(
68
                {
69
                    title: this.$__(
70
                        "Are you sure you want to remove this processing?"
71
                    ),
72
                    message: processing.name,
73
                    accept_label: this.$__("Yes, delete"),
74
                    cancel_label: this.$__("No, do not delete"),
75
                },
76
                () => {
77
                    const client = APIClient.preservation
78
                    client.processings.delete(processing.processing_id).then(
79
                        success => {
80
                            this.setMessage(
81
                                this.$__("Processing %s deleted").format(
82
                                    processing.name
83
                                ),
84
                                true
85
                            )
86
                            client.processings.getAll().then(
87
                                processings => {
88
                                    this.processings = processings
89
                                },
90
                                error => {}
91
                            )
92
                        },
93
                        error => {}
94
                    )
95
                }
96
            )
97
        },
98
    },
99
    props: {},
100
    name: "SettingsProcessings",
101
}
102
</script>
103
104
<style scoped>
105
.action_links a {
106
    padding-left: 0.2em;
107
    font-size: 11px;
108
    cursor: pointer;
109
}
110
</style>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/SettingsProcessingsFormAdd.vue (+286 lines)
Line 0 Link Here
1
<template>
2
    <div v-if="!initialized">{{ $__("Loading") }}</div>
3
    <div v-else id="processings_add">
4
        <h2 v-if="processing.processing_id">
5
            {{ $__("Edit processing #%s").format(processing.processing_id) }}
6
        </h2>
7
        <h2 v-else>{{ $__("New processing") }}</h2>
8
        <div>
9
            <form @submit="onSubmit($event)">
10
                <fieldset class="rows">
11
                    <ol>
12
                        <li>
13
                            <label class="required" for="processing_name"
14
                                >{{ $__("Processing name") }}:</label
15
                            >
16
                            <input
17
                                id="processing_name"
18
                                v-model="processing.name"
19
                                :placeholder="$__('Processing name')"
20
                                required
21
                            />
22
                            <span class="required">{{ $__("Required") }}</span>
23
                        </li>
24
                    </ol>
25
                </fieldset>
26
                <fieldset class="rows">
27
                    <legend>{{ $__("Attributes") }}</legend>
28
                    <fieldset
29
                        :id="`attribute_${counter}`"
30
                        class="rows"
31
                        v-for="(attribute, counter) in processing.attributes"
32
                        v-bind:key="counter"
33
                    >
34
                        <legend>
35
                            {{ $__("Attribute %s").format(counter + 1) }}
36
                            <a
37
                                href="#"
38
                                @click.prevent="deleteAttribute(counter)"
39
                                ><i class="fa fa-trash"></i>
40
                                {{ $__("Remove this attribute") }}</a
41
                            >
42
                        </legend>
43
                        <ol>
44
                            <li>
45
                                <label
46
                                    :for="`attribute_name_${counter}`"
47
                                    class="required"
48
                                    >{{ $__("Name") }}:
49
                                </label>
50
                                <input
51
                                    :id="`attribute_name_${counter}`"
52
                                    type="text"
53
                                    :name="`attribute_name_${counter}`"
54
                                    v-model="attribute.name"
55
                                    required
56
                                />
57
                                <span class="required">{{
58
                                    $__("Required")
59
                                }}</span>
60
                            </li>
61
                            <li>
62
                                <label
63
                                    :for="`attribute_type_${counter}`"
64
                                    class="required"
65
                                    >{{ $__("Type") }}:
66
                                </label>
67
                                <v-select
68
                                    :id="`attribute_type_${counter}`"
69
                                    v-model="attribute.type"
70
                                    :options="attribute_types"
71
                                    :reduce="o => o.code"
72
                                    @option:selected="
73
                                        attribute.option_source = null
74
                                    "
75
                                >
76
                                    <template #search="{ attributes, events }">
77
                                        <input
78
                                            :required="!attribute.type"
79
                                            class="vs__search"
80
                                            v-bind="attributes"
81
                                            v-on="events"
82
                                        />
83
                                    </template>
84
                                </v-select>
85
                                <span class="required">{{
86
                                    $__("Required")
87
                                }}</span>
88
                            </li>
89
                            <li v-if="attribute.type == 'authorised_value'">
90
                                <label
91
                                    :for="`attribute_option_${counter}`"
92
                                    class="required"
93
                                    >{{ $__("Options") }}:
94
                                </label>
95
                                <v-select
96
                                    :id="`attribute_option_${counter}`"
97
                                    v-model="attribute.option_source"
98
                                    :options="authorised_value_categories"
99
                                >
100
                                    <template #search="{ attributes, events }">
101
                                        <input
102
                                            :required="!attribute.option_source"
103
                                            class="vs__search"
104
                                            v-bind="attributes"
105
                                            v-on="events"
106
                                        />
107
                                    </template>
108
                                </v-select>
109
                                <span class="required">{{
110
                                    $__("Required")
111
                                }}</span>
112
                            </li>
113
                            <li v-if="attribute.type == 'db_column'">
114
                                <label
115
                                    :for="`attribute_option_${counter}`"
116
                                    class="required"
117
                                    >{{ $__("Options") }}:
118
                                </label>
119
                                <v-select
120
                                    :id="`attribute_option_${counter}`"
121
                                    v-model="attribute.option_source"
122
                                    :options="db_column_options"
123
                                    :reduce="o => o.code"
124
                                >
125
                                    <template #search="{ attributes, events }">
126
                                        <input
127
                                            :required="!attribute.option_source"
128
                                            class="vs__search"
129
                                            v-bind="attributes"
130
                                            v-on="events"
131
                                        />
132
                                    </template>
133
                                </v-select>
134
                                <span class="required">{{
135
                                    $__("Required")
136
                                }}</span>
137
                            </li>
138
                        </ol>
139
                    </fieldset>
140
                    <a class="btn btn-default" @click="addAttribute"
141
                        ><font-awesome-icon icon="plus" />
142
                        {{ $__("Add new attribute") }}</a
143
                    >
144
                </fieldset>
145
146
                <fieldset class="action">
147
                    <input type="submit" value="Submit" />
148
                    <router-link
149
                        to="/cgi-bin/koha/preservation/settings"
150
                        role="button"
151
                        class="cancel"
152
                        >{{ $__("Cancel") }}</router-link
153
                    >
154
                </fieldset>
155
            </form>
156
        </div>
157
    </div>
158
</template>
159
160
<script>
161
import { inject } from "vue"
162
import { APIClient } from "../../fetch/api-client.js"
163
import { storeToRefs } from "pinia"
164
165
export default {
166
    setup() {
167
        const AVStore = inject("AVStore")
168
        const {} = storeToRefs(AVStore)
169
170
        const { setMessage, setWarning } = inject("mainStore")
171
172
        const db_column_options = Object.keys(db_columns).map(function (c) {
173
            return { label: "%s (%s)".format(db_columns[c], c), code: c }
174
        })
175
        return {
176
            setMessage,
177
            setWarning,
178
            authorised_value_categories,
179
            db_column_options,
180
        }
181
    },
182
    data() {
183
        return {
184
            processing: {
185
                processing_id: null,
186
                name: "",
187
                attributes: [],
188
            },
189
            attribute_types: [
190
                {
191
                    label: this.$__("Authorized value"),
192
                    code: "authorised_value",
193
                },
194
                {
195
                    label: this.$__("Free text"),
196
                    code: "free_text",
197
                },
198
                {
199
                    label: this.$__("Database column"),
200
                    code: "db_column",
201
                },
202
            ],
203
            initialized: false,
204
        }
205
    },
206
    beforeRouteEnter(to, from, next) {
207
        next(vm => {
208
            if (to.params.processing_id) {
209
                vm.processing = vm.getProcessing(to.params.processing_id)
210
            } else {
211
                vm.initialized = true
212
            }
213
        })
214
    },
215
    methods: {
216
        async getProcessing(processing_id) {
217
            const client = APIClient.preservation
218
            await client.processings.get(processing_id).then(
219
                processing => {
220
                    this.processing = processing
221
                    this.initialized = true
222
                },
223
                error => {}
224
            )
225
        },
226
        checkForm(processing) {
227
            let errors = []
228
229
            let attributes = processing.attributes
230
231
            errors.forEach(function (e) {
232
                setWarning(e)
233
            })
234
235
            return !errors.length
236
        },
237
        onSubmit(e) {
238
            e.preventDefault()
239
240
            let processing = JSON.parse(JSON.stringify(this.processing)) // copy
241
            let processing_id = processing.processing_id
242
            delete processing.processing_id
243
244
            if (!this.checkForm(processing)) {
245
                return false
246
            }
247
248
            processing.attributes = processing.attributes.map(
249
                ({ processing_id, processing_attribute_id, ...keepAttrs }) =>
250
                    keepAttrs
251
            )
252
253
            const client = APIClient.preservation
254
            if (processing_id) {
255
                client.processings.update(processing, processing_id).then(
256
                    success => {
257
                        this.setMessage(this.$__("Processing updated"))
258
                        this.$router.push("/cgi-bin/koha/preservation/settings")
259
                    },
260
                    error => {}
261
                )
262
            } else {
263
                client.processings.create(processing).then(
264
                    success => {
265
                        this.setMessage(this.$__("Processing created"))
266
                        this.$router.push("/cgi-bin/koha/preservation/settings")
267
                    },
268
                    error => {}
269
                )
270
            }
271
        },
272
        addAttribute() {
273
            this.processing.attributes.push({
274
                name: "",
275
                type: null,
276
                option_source: null,
277
            })
278
        },
279
        deleteAttribute(counter) {
280
            this.processing.attributes.splice(counter, 1)
281
        },
282
    },
283
    components: {},
284
    name: "SettingsProcessingsFormAdd",
285
}
286
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/SettingsProcessingsShow.vue (+151 lines)
Line 0 Link Here
1
<template>
2
    <div v-if="!initialized">{{ $__("Loading") }}</div>
3
    <div v-else id="processing_show">
4
        <h2>
5
            {{ $__("Processing #%s").format(processing.processing_id) }}
6
            <span class="action_links">
7
                <router-link
8
                    :to="`/cgi-bin/koha/preservation/settings/processings/edit/${processing.processing_id}`"
9
                    :title="$__('Edit')"
10
                    ><i class="fa fa-pencil"></i
11
                ></router-link>
12
                <a @click="doDelete()"><i class="fa fa-trash"></i></a>
13
            </span>
14
        </h2>
15
        <div>
16
            <fieldset class="rows">
17
                <ol>
18
                    <li>
19
                        <label>{{ $__("Processing name") }}:</label>
20
                        <span>
21
                            {{ processing.name }}
22
                        </span>
23
                    </li>
24
                </ol>
25
            </fieldset>
26
            <fieldset class="rows">
27
                <legend>{{ $__("Attributes") }}</legend>
28
                <ol v-if="processing.attributes.length">
29
                    <li
30
                        v-for="(attribute, counter) in processing.attributes"
31
                        v-bind:key="counter"
32
                    >
33
                        <label>{{ attribute.name }}</label>
34
                        <span v-if="attribute.type == 'authorised_value'">{{
35
                            $__("Authorized value")
36
                        }}</span>
37
                        <span v-else-if="attribute.type == 'free_text'">{{
38
                            $__("Free text")
39
                        }}</span>
40
                        <span v-else-if="attribute.type == 'db_column'">{{
41
                            $__("Database column")
42
                        }}</span>
43
                        <span v-else
44
                            >{{ $__("Unknown") }} - {{ attribute.type }}</span
45
                        >
46
                    </li>
47
                </ol>
48
                <span v-else>
49
                    {{
50
                        $__(
51
                            "There are no attributes defined for this processing."
52
                        )
53
                    }}
54
                </span>
55
            </fieldset>
56
            <fieldset class="action">
57
                <router-link
58
                    to="/cgi-bin/koha/preservation/settings"
59
                    role="button"
60
                    class="cancel"
61
                    >{{ $__("Close") }}</router-link
62
                >
63
            </fieldset>
64
        </div>
65
    </div>
66
</template>
67
68
<script>
69
import { inject } from "vue"
70
import { APIClient } from "../../fetch/api-client.js"
71
72
export default {
73
    setup() {
74
        const { setConfirmationDialog, setMessage } = inject("mainStore")
75
76
        return {
77
            setConfirmationDialog,
78
            setMessage,
79
        }
80
    },
81
    data() {
82
        return {
83
            processing: {
84
                processing_id: null,
85
                name: "",
86
                attributes: [],
87
            },
88
            initialized: false,
89
        }
90
    },
91
    beforeRouteEnter(to, from, next) {
92
        next(vm => {
93
            vm.getProcessing(to.params.processing_id)
94
        })
95
    },
96
    beforeRouteUpdate(to, from) {
97
        this.processing = this.getProcessing(to.params.processing_id)
98
    },
99
    methods: {
100
        async getProcessing(processing_id) {
101
            const client = APIClient.preservation
102
            await client.processings.get(processing_id).then(
103
                processing => {
104
                    this.processing = processing
105
                    this.initialized = true
106
                },
107
                error => {}
108
            )
109
        },
110
        doDelete: function () {
111
            this.setConfirmationDialog(
112
                {
113
                    title: this.$__(
114
                        "Are you sure you want to remove this processing?"
115
                    ),
116
                    message: this.processing.name,
117
                    accept_label: this.$__("Yes, delete"),
118
                    cancel_label: this.$__("No, do not delete"),
119
                },
120
                () => {
121
                    const client = APIClient.preservation
122
                    client.processings
123
                        .delete(this.processing.processing_id)
124
                        .then(
125
                            success => {
126
                                this.setMessage(
127
                                    this.$__("Processing %s deleted").format(
128
                                        this.processing.name
129
                                    ),
130
                                    true
131
                                )
132
                                this.$router.push(
133
                                    "/cgi-bin/koha/preservation/settings"
134
                                )
135
                            },
136
                            error => {}
137
                        )
138
                }
139
            )
140
        },
141
    },
142
    name: "ProcessingsShow",
143
}
144
</script>
145
<style scoped>
146
.action_links a {
147
    padding-left: 0.2em;
148
    font-size: 11px;
149
    cursor: pointer;
150
}
151
</style>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsFormAdd.vue (+196 lines)
Line 0 Link Here
1
<template>
2
    <div v-if="!initialized">{{ $__("Loading") }}</div>
3
    <div v-else id="trains_add">
4
        <h2 v-if="train.train_id">
5
            {{ $__("Edit train #%s").format(train.train_id) }}
6
        </h2>
7
        <h2 v-else>{{ $__("New train") }}</h2>
8
        <div>
9
            <form @submit="onSubmit($event)">
10
                <fieldset class="rows">
11
                    <ol>
12
                        <li>
13
                            <label class="required" for="train_name"
14
                                >{{ $__("name") }}:</label
15
                            >
16
                            <input
17
                                id="train_name"
18
                                v-model="train.name"
19
                                :placeholder="$__('Name')"
20
                                required
21
                            />
22
                            <span class="required">{{ $__("Required") }}</span>
23
                        </li>
24
                        <li>
25
                            <label class="required" for="train_description"
26
                                >{{ $__("Description") }}:
27
                            </label>
28
                            <textarea
29
                                id="train_description"
30
                                v-model="train.description"
31
                                :placeholder="$__('Description')"
32
                                required
33
                                rows="10"
34
                                cols="50"
35
                            />
36
                            <span class="required">{{ $__("Required") }}</span>
37
                        </li>
38
                        <li>
39
                            <label for="not_for_loan_waiting_list_in"
40
                                >{{
41
                                    $__("Status for item added to this train")
42
                                }}:</label
43
                            >
44
                            <v-select
45
                                id="not_for_loan"
46
                                v-model="train.not_for_loan"
47
                                label="description"
48
                                :reduce="av => av.value"
49
                                :options="av_notforloan"
50
                            />
51
                        </li>
52
                        <li>
53
                            <label
54
                                class="required"
55
                                for="train_default_processing"
56
                                >{{ $__("Default processing") }}:
57
                            </label>
58
                            <v-select
59
                                id="train_default_processing"
60
                                label="name"
61
                                v-model="train.default_processing_id"
62
                                :reduce="p => p.processing_id"
63
                                :options="processings"
64
                                :required="!train.default_processing_id"
65
                            >
66
                                <template #search="{ attributes, events }">
67
                                    <input
68
                                        :required="!train.default_processing_id"
69
                                        class="vs__search"
70
                                        v-bind="attributes"
71
                                        v-on="events"
72
                                    />
73
                                </template>
74
                            </v-select>
75
                        </li>
76
                    </ol>
77
                </fieldset>
78
                <fieldset class="action">
79
                    <input type="submit" value="Submit" />
80
                    <router-link
81
                        to="/cgi-bin/koha/preservation/trains"
82
                        role="button"
83
                        class="cancel"
84
                        >{{ $__("Cancel") }}</router-link
85
                    >
86
                </fieldset>
87
            </form>
88
        </div>
89
    </div>
90
</template>
91
92
<script>
93
import { inject } from "vue"
94
import { storeToRefs } from "pinia"
95
import { APIClient } from "../../fetch/api-client.js"
96
97
export default {
98
    setup() {
99
        const AVStore = inject("AVStore")
100
        const { av_notforloan } = storeToRefs(AVStore)
101
102
        const { setMessage, setWarning } = inject("mainStore")
103
104
        const PreservationStore = inject("PreservationStore")
105
        const { settings } = storeToRefs(PreservationStore)
106
107
        return { av_notforloan, setMessage, setWarning, settings }
108
    },
109
    data() {
110
        return {
111
            train: {
112
                train_id: null,
113
                name: "",
114
                description: "",
115
                not_for_loan: this.settings.not_for_loan_default_train_in,
116
                default_processing_id: null,
117
                created_on: null,
118
                closed_on: null,
119
                sent_on: null,
120
                received_on: null,
121
            },
122
            processings: [],
123
            initialized: false,
124
        }
125
    },
126
    beforeRouteEnter(to, from, next) {
127
        next(vm => {
128
            if (to.params.train_id) {
129
                vm.train = vm.getTrain(to.params.train_id)
130
            } else {
131
                vm.initialized = true
132
            }
133
        })
134
    },
135
    beforeCreate() {
136
        const client = APIClient.preservation
137
        client.processings.getAll().then(
138
            processings => {
139
                this.processings = processings
140
            },
141
            error => {}
142
        )
143
    },
144
    methods: {
145
        async getTrain(train_id) {
146
            const client = APIClient.preservation
147
            client.trains.get(train_id).then(train => {
148
                this.train = train
149
                this.initialized = true
150
            })
151
        },
152
        checkForm(train) {
153
            let errors = []
154
155
            errors.forEach(function (e) {
156
                setWarning(e)
157
            })
158
            return !errors.length
159
        },
160
        onSubmit(e) {
161
            e.preventDefault()
162
163
            let train = JSON.parse(JSON.stringify(this.train)) // copy
164
            let train_id = train.train_id
165
            if (!this.checkForm(train)) {
166
                return false
167
            }
168
169
            delete train.train_id
170
            delete train.default_processing
171
            delete train.items
172
173
            const client = APIClient.preservation
174
            if (train_id) {
175
                client.trains.update(train, train_id).then(
176
                    success => {
177
                        this.setMessage(this.$__("Train updated"))
178
                        this.$router.push("/cgi-bin/koha/preservation/trains")
179
                    },
180
                    error => {}
181
                )
182
            } else {
183
                client.trains.create(train).then(
184
                    success => {
185
                        this.setMessage(this.$__("Train created"))
186
                        this.$router.push("/cgi-bin/koha/preservation/trains")
187
                    },
188
                    error => {}
189
                )
190
            }
191
        },
192
    },
193
    components: {},
194
    name: "TrainsFormAdd",
195
}
196
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsFormAddItem.vue (+327 lines)
Line 0 Link Here
1
<template>
2
    <div v-if="!initialized">{{ $__("Loading") }}</div>
3
    <div v-else id="trains_add_item">
4
        <h2 v-if="train_item && train_item.train_item_id">
5
            {{ $__("Edit item #%s").format(train_item.item_id) }}
6
        </h2>
7
        <h2 v-else>{{ $__("Add new item to %s").format(train.name) }}</h2>
8
        <div v-if="train_item">
9
            <form @submit="onSubmit($event)">
10
                <fieldset class="rows">
11
                    <ol>
12
                        <li>
13
                            <label for="itemnumber"
14
                                >{{ $__("Itemnumber") }}:</label
15
                            >
16
                            <span>{{ train_item.item_id }}</span>
17
                        </li>
18
                        <li>
19
                            <label for="processing"
20
                                >{{ $__("Processing") }}:
21
                            </label>
22
                            <v-select
23
                                id="processing"
24
                                label="name"
25
                                v-model="train_item.processing_id"
26
                                @option:selected="refreshAttributes(1)"
27
                                :reduce="p => p.processing_id"
28
                                :options="processings"
29
                                :clearable="false"
30
                            />
31
                        </li>
32
                        <li
33
                            class="attribute"
34
                            v-for="(attribute, counter) in attributes"
35
                            v-bind:key="counter"
36
                        >
37
                            <label :for="`attribute_${counter}`"
38
                                >{{ attribute.name }}:
39
                            </label>
40
                            <span v-if="attribute.type == 'authorised_value'">
41
                                <v-select
42
                                    :id="`attribute_${counter}`"
43
                                    v-model="attribute.value"
44
                                    label="description"
45
                                    :reduce="av => av.value"
46
                                    :options="
47
                                        av_options[attribute.option_source]
48
                                    "
49
                                />
50
                            </span>
51
                            <span v-else-if="attribute.type == 'free_text'">
52
                                <input
53
                                    :id="`attribute_${counter}`"
54
                                    v-model="attribute.value"
55
                                />
56
                            </span>
57
                            <span v-else-if="attribute.type == 'db_column'">
58
                                <input
59
                                    :id="`attribute_${counter}`"
60
                                    v-model="attribute.value"
61
                                />
62
                            </span>
63
                        </li>
64
                    </ol>
65
                </fieldset>
66
                <fieldset class="action">
67
                    <input type="submit" value="Submit" />
68
                    <router-link
69
                        to="/cgi-bin/koha/preservation/trains"
70
                        role="button"
71
                        class="cancel"
72
                        >{{ $__("Cancel") }}</router-link
73
                    >
74
                </fieldset>
75
            </form>
76
        </div>
77
        <div v-else>
78
            <form @submit="getItemFromWaitingList($event)">
79
                <fieldset class="rows">
80
                    <ol>
81
                        <li>
82
                            <label class="required" for="barcode"
83
                                >{{ $__("barcode") }}:</label
84
                            >
85
                            <input
86
                                id="barcode"
87
                                v-model="barcode"
88
                                :placeholder="$__('barcode')"
89
                                required
90
                            />
91
                            <span class="required">{{ $__("Required") }}</span>
92
                        </li>
93
                    </ol>
94
                </fieldset>
95
                <fieldset class="action">
96
                    <input type="submit" value="Submit" />
97
                    <router-link
98
                        :to="`/cgi-bin/koha/preservation/trains/${train.train_id}`"
99
                        role="button"
100
                        class="cancel"
101
                        >{{ $__("Cancel") }}</router-link
102
                    >
103
                </fieldset>
104
            </form>
105
        </div>
106
    </div>
107
</template>
108
109
<script>
110
import { inject } from "vue"
111
import { APIClient } from "../../fetch/api-client"
112
113
export default {
114
    setup() {
115
        const { setMessage, setWarning, loading, loaded } = inject("mainStore")
116
        return {
117
            setMessage,
118
            setWarning,
119
            loading,
120
            loaded,
121
            api_mappings,
122
        }
123
    },
124
    data() {
125
        return {
126
            train: {
127
                train_id: null,
128
                name: "",
129
                description: "",
130
            },
131
            item: { item_id: null },
132
            train_item: null,
133
            barcode: "",
134
            processings: [],
135
            processing: null,
136
            initialized: false,
137
            av_options: {},
138
            default_values: {},
139
            attributes: [],
140
        }
141
    },
142
    beforeCreate() {
143
        const client = APIClient.preservation
144
        client.processings
145
            .getAll()
146
            .then(processings => (this.processings = processings))
147
    },
148
    beforeRouteEnter(to, from, next) {
149
        next(vm => {
150
            if (to.params.train_item_id) {
151
                vm.train = vm
152
                    .getTrain(to.params.train_id)
153
                    .then(() =>
154
                        vm
155
                            .getTrainItem(
156
                                to.params.train_id,
157
                                to.params.train_item_id
158
                            )
159
                            .then(() =>
160
                                vm
161
                                    .refreshAttributes()
162
                                    .then(() => (vm.initialized = true))
163
                            )
164
                    )
165
            } else {
166
                vm.train = vm
167
                    .getTrain(to.params.train_id)
168
                    .then(() => (vm.initialized = true))
169
            }
170
        })
171
    },
172
    methods: {
173
        async getTrain(train_id) {
174
            const client = APIClient.preservation
175
            await client.trains.get(train_id).then(
176
                train => {
177
                    this.train = train
178
                },
179
                error => {}
180
            )
181
        },
182
        async getTrainItem(train_id, train_item_id) {
183
            const client = APIClient.preservation
184
            await client.train_items.get(train_id, train_item_id).then(
185
                train_item => {
186
                    this.train_item = train_item
187
                    this.item = train_item.catalogue_item
188
                },
189
                error => {}
190
            )
191
        },
192
        async getItemFromWaitingList(e) {
193
            e.preventDefault()
194
            const client = APIClient.preservation
195
            client.waiting_list_items.get_from_barcode(this.barcode).then(
196
                item => {
197
                    if (!item) {
198
                        this.setWarning(
199
                            this.$__(
200
                                "Cannot find item with this barcode. It must be in the waiting list."
201
                            )
202
                        )
203
                        return
204
                    }
205
                    this.item = item
206
                    this.train_item = {
207
                        item_id: item.item_id,
208
                        processing_id: this.train.default_processing_id,
209
                    }
210
                    this.refreshAttributes(1)
211
                },
212
                error => {}
213
            )
214
        },
215
        columnApiMapping(db_column) {
216
            let table_col = db_column.split(".")
217
            let table = table_col[0]
218
            let col = table_col[1]
219
            let api_attribute = this.api_mappings[table][col] || col
220
            return table == "biblio" || table == "biblioitems"
221
                ? this.item.biblio[api_attribute]
222
                : this.item[api_attribute]
223
        },
224
        updateDefaultValues() {
225
            this.processing.attributes
226
                .filter(attribute => attribute.type == "db_column")
227
                .forEach(attribute => {
228
                    this.default_values[attribute.processing_attribute_id] =
229
                        this.columnApiMapping(attribute.option_source)
230
                })
231
        },
232
        async refreshAttributes(apply_default_value) {
233
            this.loading()
234
235
            const client = APIClient.preservation
236
            await client.processings.get(this.train_item.processing_id).then(
237
                processing => (this.processing = processing),
238
                error => {}
239
            )
240
            this.updateDefaultValues()
241
            this.attributes = this.processing.attributes.map(attribute => {
242
                let value = ""
243
                if (!apply_default_value) {
244
                    let existing_attribute = this.train_item.attributes.find(
245
                        a =>
246
                            a.processing_attribute_id ==
247
                            attribute.processing_attribute_id
248
                    )
249
                    if (existing_attribute) {
250
                        value = existing_attribute.value
251
                    }
252
                } else if (attribute.type == "db_column") {
253
                    value =
254
                        this.default_values[attribute.processing_attribute_id]
255
                }
256
                return {
257
                    processing_attribute_id: attribute.processing_attribute_id,
258
                    name: attribute.name,
259
                    type: attribute.type,
260
                    option_source: attribute.option_source,
261
                    value,
262
                }
263
            })
264
            const client_av = APIClient.authorised_values
265
            let av_cat_array = this.processing.attributes
266
                .filter(attribute => attribute.type == "authorised_value")
267
                .map(attribute => attribute.option_source)
268
269
            client_av.values
270
                .getCategoriesWithValues([
271
                    ...new Set(av_cat_array.map(av_cat => '"' + av_cat + '"')),
272
                ]) // unique
273
                .then(av_categories => {
274
                    av_cat_array.forEach(av_cat => {
275
                        let av_match = av_categories.find(
276
                            element => element.category_name == av_cat
277
                        )
278
                        this.av_options[av_cat] = av_match.authorised_values
279
                    })
280
                })
281
                .then(() => this.loaded())
282
        },
283
        onSubmit(e) {
284
            e.preventDefault()
285
286
            let train_item_id = this.train_item.train_item_id
287
            let train_item = {
288
                item_id: this.train_item.item_id,
289
                processing_id: this.train_item.processing_id,
290
                attributes: this.attributes.map(a => ({
291
                    processing_attribute_id: a.processing_attribute_id,
292
                    value: a.value,
293
                })),
294
            }
295
296
            const client = APIClient.preservation
297
            if (train_item_id) {
298
                client.train_items
299
                    .update(train_item, this.train.train_id, train_item_id)
300
                    .then(
301
                        success => {
302
                            this.setMessage(this.$__("Item updated"))
303
                            this.$router.push(
304
                                "/cgi-bin/koha/preservation/trains/" +
305
                                    this.train.train_id
306
                            )
307
                        },
308
                        error => {}
309
                    )
310
            } else {
311
                client.train_items.create(train_item, this.train.train_id).then(
312
                    success => {
313
                        this.setMessage(this.$__("Item added to train"))
314
                        this.$router.push(
315
                            "/cgi-bin/koha/preservation/trains/" +
316
                                this.train.train_id
317
                        )
318
                    },
319
                    error => {}
320
                )
321
            }
322
        },
323
    },
324
    components: {},
325
    name: "TrainsFormAdd",
326
}
327
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsList.vue (+270 lines)
Line 0 Link Here
1
<template>
2
    <div v-if="!initialized">{{ $__("Loading") }}</div>
3
    <div v-else id="trains_list">
4
        <Toolbar />
5
        <fieldset v-if="count_trains > 0" class="filters">
6
            <label>{{ $__("Filter by") }}:</label>
7
            <input
8
                type="radio"
9
                id="all_status_filter"
10
                v-model="filters.status"
11
                value=""
12
            /><label for="all_status_filter">{{ $__("All") }}</label>
13
            <input
14
                type="radio"
15
                id="closed_status_filter"
16
                v-model="filters.status"
17
                value="closed"
18
            /><label for="closed_status_filter">{{ $__("Closed") }}</label>
19
            <input
20
                type="radio"
21
                id="sent_status_filter"
22
                v-model="filters.status"
23
                value="sent"
24
            /><label for="sent_status_filter">{{ $__("Sent") }}</label>
25
            <input
26
                type="radio"
27
                id="received_status_filter"
28
                v-model="filters.status"
29
                value="received"
30
            /><label for="received_status_filter">{{ $__("Received") }}</label>
31
            <input
32
                @click="filter_table"
33
                id="filter_table"
34
                type="button"
35
                :value="$__('Filter')"
36
            />
37
        </fieldset>
38
        <div v-if="count_trains > 0" class="page-section">
39
            <KohaTable
40
                ref="table"
41
                v-bind="tableOptions"
42
                @show="doShow"
43
                @edit="doEdit"
44
                @delete="doDelete"
45
                @addItems="doAddItems"
46
            ></KohaTable>
47
        </div>
48
49
        <div v-else class="dialog message">
50
            {{ $__("There are no trains defined") }}
51
        </div>
52
    </div>
53
</template>
54
55
<script>
56
import flatPickr from "vue-flatpickr-component"
57
import Toolbar from "./TrainsToolbar.vue"
58
import { inject, ref, reactive } from "vue"
59
import { APIClient } from "../../fetch/api-client"
60
import { build_url } from "../../composables/datatables"
61
import KohaTable from "../KohaTable.vue"
62
63
export default {
64
    setup() {
65
        const AVStore = inject("AVStore")
66
        const { get_lib_from_av, map_av_dt_filter } = AVStore
67
        const { setConfirmationDialog, setMessage } = inject("mainStore")
68
        const table = ref()
69
        const filters = reactive({ status: "" })
70
        return {
71
            get_lib_from_av,
72
            map_av_dt_filter,
73
            setConfirmationDialog,
74
            setMessage,
75
            table,
76
            filters,
77
        }
78
    },
79
    data: function () {
80
        this.filters.status = this.$route.query.status || ""
81
        return {
82
            fp_config: flatpickr_defaults,
83
            count_trains: 0,
84
            initialized: false,
85
            tableOptions: {
86
                columns: this.getTableColumns(),
87
                url: this.table_url,
88
                add_filters: true,
89
                actions: {
90
                    0: ["show"],
91
                    "-1": [
92
                        "edit",
93
                        "delete",
94
                        {
95
                            addItems: {
96
                                text: this.$__("Add items"),
97
                                icon: "fa fa-plus",
98
                            },
99
                        },
100
                    ],
101
                },
102
            },
103
        }
104
    },
105
    beforeRouteEnter(to, from, next) {
106
        next(vm => {
107
            vm.getCountTrains()
108
        })
109
    },
110
    computed: {},
111
    methods: {
112
        async getCountTrains() {
113
            const client = APIClient.preservation
114
            client.trains.count().then(
115
                count => {
116
                    this.count_trains = count
117
                    this.initialized = true
118
                },
119
                error => {}
120
            )
121
        },
122
        doShow: function (train, dt, event) {
123
            event.preventDefault()
124
            this.$router.push(
125
                "/cgi-bin/koha/preservation/trains/" + train.train_id
126
            )
127
        },
128
        doEdit: function (train, dt, event) {
129
            this.$router.push(
130
                "/cgi-bin/koha/preservation/trains/edit/" + train.train_id
131
            )
132
        },
133
        doDelete: function (train, dt, event) {
134
            this.setConfirmationDialog(
135
                {
136
                    title: this.$__(
137
                        "Are you sure you want to remove this train?"
138
                    ),
139
                    message: train.name,
140
                    accept_label: this.$__("Yes, delete"),
141
                    cancel_label: this.$__("No, do not delete"),
142
                },
143
                () => {
144
                    const client = APIClient.preservation
145
                    client.trains.delete(train.train_id).then(
146
                        success => {
147
                            this.setMessage(
148
                                this.$__("Train %s deleted").format(train.name),
149
                                true
150
                            )
151
                            dt.draw()
152
                        },
153
                        error => {}
154
                    )
155
                }
156
            )
157
        },
158
        doAddItems: function (train, dt, event) {
159
            this.$router.push(
160
                "/cgi-bin/koha/preservation/trains/" +
161
                    train.train_id +
162
                    "/items/add"
163
            )
164
        },
165
        table_url() {
166
            let url = "/api/v1/preservation/trains"
167
            let q
168
            if (this.filters.status == "closed") {
169
                q = {
170
                    "me.closed_on": { "!=": null },
171
                    "me.sent_on": null,
172
                    "me.received_on": null,
173
                }
174
            } else if (this.filters.status == "sent") {
175
                q = {
176
                    "me.closed_on": { "!=": null },
177
                    "me.sent_on": { "!=": null },
178
                    "me.received_on": null,
179
                }
180
            } else if (this.filters.status == "received") {
181
                q = {
182
                    "me.closed_on": { "!=": null },
183
                    "me.sent_on": { "!=": null },
184
                    "me.received_on": { "!=": null },
185
                }
186
            }
187
            if (q) {
188
                url += "?" + new URLSearchParams({ q: JSON.stringify(q) })
189
            }
190
191
            return url
192
        },
193
        filter_table: async function () {
194
            let new_route = build_url(
195
                "/cgi-bin/koha/preservation/trains",
196
                this.filters
197
            )
198
            this.$router.push(new_route)
199
            if (this.$refs.table) {
200
                this.$refs.table.redraw(this.table_url())
201
            }
202
        },
203
        getTableColumns: function () {
204
            let escape_str = this.escape_str
205
            return [
206
                {
207
                    title: __("Name"),
208
                    data: "me.train_id:me.name",
209
                    searchable: true,
210
                    orderable: true,
211
                    render: function (data, type, row, meta) {
212
                        return `<a href="/cgi-bin/koha/preservation/trains/${row.train_id}" class="show">${row.name} (#${row.train_id})</a>`
213
                    },
214
                },
215
                {
216
                    title: __("Created on"),
217
                    data: "created_on",
218
                    searchable: true,
219
                    orderable: true,
220
                    render: function (data, type, row, meta) {
221
                        return $date(row.created_on)
222
                    },
223
                },
224
                {
225
                    title: __("Closed on"),
226
                    data: "closed_on",
227
                    searchable: true,
228
                    orderable: true,
229
                    render: function (data, type, row, meta) {
230
                        return $date(row.closed_on)
231
                    },
232
                },
233
                {
234
                    title: __("Sent on"),
235
                    data: "sent_on",
236
                    searchable: true,
237
                    orderable: true,
238
                    render: function (data, type, row, meta) {
239
                        return $date(row.sent_on)
240
                    },
241
                },
242
                {
243
                    title: __("Received on"),
244
                    data: "received_on",
245
                    searchable: true,
246
                    orderable: true,
247
                    render: function (data, type, row, meta) {
248
                        return $date(row.received_on)
249
                    },
250
                },
251
            ]
252
        },
253
    },
254
    components: { flatPickr, Toolbar, KohaTable },
255
    name: "trainsList",
256
    emits: ["select-train", "close"],
257
}
258
</script>
259
260
<style scoped>
261
#train_list {
262
    display: table;
263
}
264
.filters > input[type="radio"] {
265
    min-width: 0 !important;
266
}
267
.filters > input[type="button"] {
268
    margin-left: 1rem;
269
}
270
</style>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsShow.vue (+548 lines)
Line 0 Link Here
1
<template>
2
    <transition name="modal">
3
        <div v-if="show_modal" class="modal">
4
            <h2>{{ $__("Copy item to the following train") }}</h2>
5
            <form @submit="copyItem($event)">
6
                <div class="page-section">
7
                    <fieldset class="rows">
8
                        <ol>
9
                            <li>
10
                                <label class="required" for="train_list"
11
                                    >{{ $__("Select a train") }}:</label
12
                                >
13
                                <v-select
14
                                    v-model="train_id_selected_for_copy"
15
                                    label="name"
16
                                    :options="train_list"
17
                                    :reduce="t => t.train_id"
18
                                >
19
                                    <template #search="{ attributes, events }">
20
                                        <input
21
                                            :required="
22
                                                !train_id_selected_for_copy
23
                                            "
24
                                            class="vs__search"
25
                                            v-bind="attributes"
26
                                            v-on="events"
27
                                        />
28
                                    </template>
29
                                </v-select>
30
                                <span class="required">{{
31
                                    $__("Required")
32
                                }}</span>
33
                            </li>
34
                        </ol>
35
                    </fieldset>
36
                    <fieldset class="action">
37
                        <input type="submit" value="Copy" />
38
                        <input
39
                            type="button"
40
                            @click="show_modal = false"
41
                            :value="$__('Close')"
42
                        />
43
                    </fieldset>
44
                </div>
45
            </form>
46
        </div>
47
    </transition>
48
    <div v-if="!initialized">{{ $__("Loading") }}</div>
49
    <div v-else id="trains_show">
50
        <div id="toolbar" class="btn-toolbar">
51
            <router-link
52
                :to="`/cgi-bin/koha/preservation/trains/${train.train_id}/items/add`"
53
                class="btn btn-default"
54
                ><font-awesome-icon icon="plus" />
55
                {{ $__("Add items") }}</router-link
56
            >
57
            <router-link
58
                :to="`/cgi-bin/koha/preservation/trains/edit/${train.train_id}`"
59
                class="btn btn-default"
60
                ><font-awesome-icon icon="pencil" />
61
                {{ $__("Edit") }}</router-link
62
            >
63
            <a @click="deleteTrain(train)" class="btn btn-default"
64
                ><font-awesome-icon icon="trash" /> {{ $__("Delete") }}</a
65
            >
66
            <a
67
                v-if="!train.closed_on"
68
                class="btn btn-default"
69
                @click="closeTrain"
70
                ><font-awesome-icon icon="remove" /> {{ $__("Close") }}</a
71
            >
72
            <a
73
                v-else-if="!train.sent_on"
74
                class="btn btn-default"
75
                @click="sendTrain"
76
                ><font-awesome-icon icon="paper-plane" /> {{ $__("Send") }}</a
77
            >
78
            <a
79
                v-else-if="!train.received_on"
80
                class="btn btn-default"
81
                @click="receiveTrain"
82
                ><font-awesome-icon icon="inbox" /> {{ $__("Receive") }}</a
83
            >
84
        </div>
85
        <h2>
86
            {{ $__("Train #%s").format(train.train_id) }}
87
        </h2>
88
        <div>
89
            <fieldset class="rows">
90
                <ol>
91
                    <li>
92
                        <label>{{ $__("Name") }}:</label>
93
                        <span>
94
                            {{ train.name }}
95
                        </span>
96
                    </li>
97
                    <li>
98
                        <label>{{ $__("Description") }}:</label>
99
                        <span>
100
                            {{ train.description }}
101
                        </span>
102
                    </li>
103
                    <li v-if="train.closed_on">
104
                        <label>{{ $__("Closed on") }}:</label>
105
                        <span>
106
                            {{ format_date(train.closed_on) }}
107
                        </span>
108
                    </li>
109
                    <li v-if="train.sent_on">
110
                        <label>{{ $__("Sent on") }}:</label>
111
                        <span>
112
                            {{ format_date(train.sent_on) }}
113
                        </span>
114
                    </li>
115
                    <li v-if="train.received_on">
116
                        <label>{{ $__("Received on") }}:</label>
117
                        <span>
118
                            {{ format_date(train.received_on) }}
119
                        </span>
120
                    </li>
121
                    <li>
122
                        <label
123
                            >{{
124
                                $__("Status for item added to this train")
125
                            }}:</label
126
                        >
127
                        <span>{{
128
                            get_lib_from_av("av_notforloan", train.not_for_loan)
129
                        }}</span>
130
                    </li>
131
                    <li>
132
                        <label>{{ $__("Default processing") }}:</label>
133
                        <span>
134
                            {{ train.default_processing.name }}
135
                        </span>
136
                    </li>
137
                </ol>
138
            </fieldset>
139
            <fieldset v-if="train.items.length" class="rows">
140
                <legend>{{ $__("Items") }}</legend>
141
                <table v-if="item_table.display" :id="table_id"></table>
142
                <ol v-else>
143
                    <li
144
                        :id="`item_${counter}`"
145
                        class="rows"
146
                        v-for="(item, counter) in train.items"
147
                        v-bind:key="counter"
148
                    >
149
                        <!-- FIXME Counter here may change, we should pass an order by clause when retrieving the items -->
150
                        <label
151
                            >{{ counter + 1 }}
152
                            <span class="action_links">
153
                                <router-link
154
                                    :to="`/cgi-bin/koha/preservation/trains/${train.train_id}/items/edit/${item.train_item_id}`"
155
                                    :title="$__('Edit')"
156
                                    ><i class="fa fa-pencil"></i></router-link
157
                            ></span>
158
                        </label>
159
                        <div class="attributes_values">
160
                            <span
161
                                :id="`attribute_${counter_attribute}`"
162
                                class="attribute_value"
163
                                v-for="(
164
                                    attribute, counter_attribute
165
                                ) in item.attributes"
166
                                v-bind:key="counter_attribute"
167
                            >
168
                                <!-- FIXME We need to display the description of the AV here -->
169
                                {{ attribute.processing_attribute.name }}={{
170
                                    attribute.value
171
                                }}
172
                            </span>
173
                        </div>
174
                    </li>
175
                </ol>
176
            </fieldset>
177
            <fieldset class="action">
178
                <router-link
179
                    to="/cgi-bin/koha/preservation/trains"
180
                    role="button"
181
                    class="cancel"
182
                    >{{ $__("Close") }}</router-link
183
                >
184
            </fieldset>
185
        </div>
186
    </div>
187
</template>
188
189
<script>
190
import { inject, createVNode, render } from "vue"
191
import { APIClient } from "../../fetch/api-client"
192
import { useDataTable } from "../../composables/datatables"
193
194
export default {
195
    setup() {
196
        const format_date = $date
197
198
        const AVStore = inject("AVStore")
199
        const { get_lib_from_av } = AVStore
200
201
        const { setConfirmationDialog, setMessage } = inject("mainStore")
202
203
        const table_id = "item_list"
204
        useDataTable(table_id)
205
206
        return {
207
            format_date,
208
            get_lib_from_av,
209
            table_id,
210
            setConfirmationDialog,
211
            setMessage,
212
        }
213
    },
214
    data() {
215
        return {
216
            train: {
217
                train_id: null,
218
                name: "",
219
                description: "",
220
            },
221
            initialized: false,
222
            show_modal: false,
223
            item_table: {
224
                display: false,
225
                data: [],
226
                columns: [],
227
            },
228
            train_list: [],
229
            train_id_selected_for_copy: null,
230
            train_item_id_to_copy: null,
231
        }
232
    },
233
    beforeRouteEnter(to, from, next) {
234
        next(vm => {
235
            vm.getTrain(to.params.train_id).then(() => vm.build_datatable())
236
            vm.getTrainList()
237
        })
238
    },
239
    methods: {
240
        async getTrain(train_id) {
241
            const client = APIClient.preservation
242
            await client.trains.get(train_id).then(
243
                train => {
244
                    this.train = train
245
                    let display = this.train.items.every(
246
                        item =>
247
                            item.processing_id ==
248
                            this.train.default_processing_id
249
                    )
250
                    if (display) {
251
                        this.item_table.data = []
252
                        this.train.items.forEach(item => {
253
                            let item_row = {}
254
                            this.train.default_processing.attributes.forEach(
255
                                attribute => {
256
                                    if (item.attributes.length <= 0) return ""
257
                                    item_row[
258
                                        attribute.processing_attribute_id
259
                                    ] = item.attributes.find(
260
                                        a =>
261
                                            a.processing_attribute_id ==
262
                                            attribute.processing_attribute_id
263
                                    ).value
264
                                }
265
                            )
266
                            item_row.item = item
267
                            this.item_table.data.push(item_row)
268
                        })
269
                        this.item_table.columns = []
270
                        this.item_table.columns.push({
271
                            name: "id",
272
                            title: this.$__("ID"),
273
                            render: (data, type, row) => {
274
                                return 42
275
                            },
276
                        })
277
                        train.default_processing.attributes.forEach(a =>
278
                            this.item_table.columns.push({
279
                                name: a.name,
280
                                title: a.name,
281
                                data: a.processing_attribute_id,
282
                            })
283
                        )
284
                        this.item_table.columns.push({
285
                            name: "actions",
286
                            className: "actions noExport",
287
                            title: this.$__("Actions"),
288
                            searchable: false,
289
                            orderable: false,
290
                            render: (data, type, row) => {
291
                                return ""
292
                            },
293
                        })
294
                    }
295
                    this.initialized = true
296
                    this.item_table.display = display
297
                },
298
                error => {}
299
            )
300
        },
301
        getTrainList: function () {
302
            const client = APIClient.preservation
303
            let q = { "me.closed_on": null }
304
            client.trains.getAll(q).then(
305
                trains => (this.train_list = trains),
306
                error => {}
307
            )
308
        },
309
        deleteTrain: function (train) {
310
            this.setConfirmationDialog(
311
                {
312
                    title: this.$__(
313
                        "Are you sure you want to remove this train?"
314
                    ),
315
                    message: train.name,
316
                    accept_label: this.$__("Yes, delete"),
317
                    cancel_label: this.$__("No, do not delete"),
318
                },
319
                () => {
320
                    const client = APIClient.preservation
321
                    client.trains.delete(train.train_id).then(
322
                        success => {
323
                            this.setMessage(
324
                                this.$__("Train %s deleted").format(train.name),
325
                                true
326
                            )
327
                        },
328
                        error => {}
329
                    )
330
                }
331
            )
332
        },
333
        async updateTrainDate(attribute) {
334
            let train = JSON.parse(JSON.stringify(this.train))
335
            let train_id = train.train_id
336
            delete train.train_id
337
            delete train.items
338
            delete train.default_processing
339
            train[attribute] = new Date()
340
            const client = APIClient.preservation
341
            if (train_id) {
342
                client.trains
343
                    .update(train, train_id)
344
                    .then(() => this.getTrain(this.train.train_id))
345
            } else {
346
                client.trains
347
                    .create(train)
348
                    .then(() => this.getTrain(this.train.train_id))
349
            }
350
        },
351
        closeTrain() {
352
            this.updateTrainDate("closed_on")
353
        },
354
        sendTrain() {
355
            this.updateTrainDate("sent_on")
356
        },
357
        receiveTrain() {
358
            this.updateTrainDate("received_on")
359
        },
360
        editItem(train_item_id) {
361
            this.$router.push(
362
                `/cgi-bin/koha/preservation/trains/${this.train.train_id}/items/edit/${train_item_id}`
363
            )
364
        },
365
        removeItem(train_item_id) {
366
            this.setConfirmationDialog(
367
                {
368
                    title: this.$__(
369
                        "Are you sure you want to remove this item?"
370
                    ),
371
                    accept_label: this.$__("Yes, remove"),
372
                    cancel_label: this.$__("No, do not remove"),
373
                },
374
                () => {
375
                    const client = APIClient.preservation
376
                    client.train_items
377
                        .delete(this.train.train_id, train_item_id)
378
                        .then(
379
                            success => {
380
                                this.setMessage(this.$__("Item removed"), true)
381
                                this.getTrain(this.train.train_id).then(() => {
382
                                    $("#" + this.table_id)
383
                                        .DataTable()
384
                                        .destroy()
385
                                    this.build_datatable()
386
                                })
387
                            },
388
                            error => {}
389
                        )
390
                }
391
            )
392
        },
393
        selectTrainForCopy(train_item_id) {
394
            this.show_modal = true
395
            this.train_item_id_to_copy = train_item_id
396
        },
397
        copyItem(event) {
398
            event.preventDefault()
399
            const client = APIClient.preservation
400
            let new_train_item = {}
401
            client.train_items
402
                .get(this.train.train_id, this.train_item_id_to_copy)
403
                .then(train_item => {
404
                    new_train_item = {
405
                        attributes: train_item.attributes.map(attr => {
406
                            return {
407
                                processing_attribute_id:
408
                                    attr.processing_attribute_id,
409
                                value: attr.value,
410
                            }
411
                        }),
412
                        item_id: train_item.item_id,
413
                        processing_id: train_item.processing_id,
414
                    }
415
                })
416
                .then(() =>
417
                    client.train_items
418
                        .create(new_train_item, this.train_id_selected_for_copy)
419
                        .then(
420
                            success => {
421
                                this.setMessage(
422
                                    this.$__("Item copied successfully.")
423
                                )
424
                                this.show_modal = false
425
                            },
426
                            error => {}
427
                        )
428
                )
429
        },
430
        build_datatable: function () {
431
            let table_id = this.table_id
432
            let item_table = this.item_table
433
            let removeItem = this.removeItem
434
            let editItem = this.editItem
435
            let selectTrainForCopy = this.selectTrainForCopy
436
            let train = this.train
437
438
            let table = KohaTable(table_id, {
439
                data: item_table.data,
440
                ordering: false,
441
                autoWidth: false,
442
                columns: item_table.columns,
443
                drawCallback: function (settings) {
444
                    var api = new $.fn.dataTable.Api(settings)
445
                    $.each($(this).find("td.actions"), function (index, e) {
446
                        let tr = $(this).parent()
447
                        let train_item_id = api.row(tr).data()
448
                            .item.train_item_id
449
450
                        let editButton = createVNode(
451
                            "a",
452
                            {
453
                                class: "btn btn-default btn-xs",
454
                                role: "button",
455
                                onClick: () => {
456
                                    editItem(train_item_id)
457
                                },
458
                            },
459
                            [
460
                                createVNode("i", {
461
                                    class: "fa fa-pencil",
462
                                    "aria-hidden": "true",
463
                                }),
464
                                __("Edit"),
465
                            ]
466
                        )
467
468
                        let removeButton = createVNode(
469
                            "a",
470
                            {
471
                                class: "btn btn-default btn-xs",
472
                                role: "button",
473
                                onClick: () => {
474
                                    removeItem(train_item_id)
475
                                },
476
                            },
477
                            [
478
                                createVNode("i", {
479
                                    class: "fa fa-trash",
480
                                    "aria-hidden": "true",
481
                                }),
482
                                __("Remove"),
483
                            ]
484
                        )
485
                        let buttons = [editButton, "", removeButton]
486
487
                        if (train.received_on !== null) {
488
                            buttons.push("")
489
                            buttons.push(
490
                                createVNode(
491
                                    "a",
492
                                    {
493
                                        class: "btn btn-default btn-xs",
494
                                        role: "button",
495
                                        onClick: () => {
496
                                            selectTrainForCopy(train_item_id)
497
                                        },
498
                                    },
499
                                    [
500
                                        createVNode("i", {
501
                                            class: "fa fa-copy",
502
                                            "aria-hidden": "true",
503
                                        }),
504
                                        __("Copy"),
505
                                    ]
506
                                )
507
                            )
508
                        }
509
510
                        let n = createVNode("span", {}, buttons)
511
                        render(n, e)
512
                    })
513
                },
514
            })
515
        },
516
    },
517
    name: "TrainsShow",
518
}
519
</script>
520
<style scoped>
521
.action_links a {
522
    padding-left: 0.2em;
523
    font-size: 11px;
524
}
525
.attributes_values {
526
    float: left;
527
}
528
.attribute_value {
529
    display: block;
530
}
531
.modal {
532
    position: fixed;
533
    z-index: 9998;
534
    top: 0;
535
    left: 0;
536
    width: 35%;
537
    height: 30%;
538
    background-color: rgba(0, 0, 0, 0.5);
539
    display: table;
540
    transition: opacity 0.3s ease;
541
    margin: auto;
542
    padding: 20px 30px;
543
    background-color: #fff;
544
    border-radius: 2px;
545
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
546
    transition: all 0.3s ease;
547
}
548
</style>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/TrainsToolbar.vue (+16 lines)
Line 0 Link Here
1
<template>
2
    <div id="toolbar" class="btn-toolbar">
3
        <router-link
4
            to="/cgi-bin/koha/preservation/trains/add"
5
            class="btn btn-default"
6
            ><font-awesome-icon icon="plus" />
7
            {{ $__("New train") }}</router-link
8
        >
9
    </div>
10
</template>
11
12
<script>
13
export default {
14
    name: "TrainsToolbar",
15
}
16
</script>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Preservation/WaitingList.vue (+344 lines)
Line 0 Link Here
1
<template>
2
    <transition name="modal_add_to_waiting_list">
3
        <div v-if="show_modal_add_to_waiting_list" class="modal">
4
            <h2>{{ $__("Add items to waiting list") }}</h2>
5
            <form @submit="addItemsToWaitingList($event)">
6
                <div class="page-section">
7
                    <fieldset class="rows">
8
                        <ol>
9
                            <li>
10
                                <label class="required" for="barcode_list"
11
                                    >{{ $__("Barcode list") }}:</label
12
                                >
13
                                <textarea
14
                                    id="barcode_list"
15
                                    v-model="barcode_list"
16
                                    :placeholder="$__('Barcodes')"
17
                                    rows="10"
18
                                    cols="50"
19
                                    required
20
                                />
21
                            </li>
22
                        </ol>
23
                    </fieldset>
24
                    <fieldset class="action">
25
                        <input type="submit" value="Submit" />
26
                        <input
27
                            type="button"
28
                            @click="show_modal_add_to_waiting_list = false"
29
                            :value="$__('Close')"
30
                        />
31
                    </fieldset>
32
                </div>
33
            </form>
34
        </div>
35
    </transition>
36
    <transition name="modal_add_to_train">
37
        <div v-if="show_modal_add_to_train" class="modal">
38
            <h2>{{ $__("Add items to a train") }}</h2>
39
            <form @submit="addItemsToTrain($event)">
40
                <div class="page-section">
41
                    <fieldset class="rows">
42
                        <ol>
43
                            <li>
44
                                <label class="required" for="train_list"
45
                                    >{{ $__("Select a train") }}:</label
46
                                >
47
                                <v-select
48
                                    id="train_id"
49
                                    v-model="train_id_selected_for_add"
50
                                    label="name"
51
                                    :options="train_list"
52
                                    :reduce="t => t.train_id"
53
                                >
54
                                    <template #search="{ attributes, events }">
55
                                        <input
56
                                            :required="
57
                                                !train_id_selected_for_add
58
                                            "
59
                                            class="vs__search"
60
                                            v-bind="attributes"
61
                                            v-on="events"
62
                                        />
63
                                    </template>
64
                                </v-select>
65
                                <span class="required">{{
66
                                    $__("Required")
67
                                }}</span>
68
                            </li>
69
                        </ol>
70
                    </fieldset>
71
                    <fieldset class="action">
72
                        <input type="submit" value="Submit" />
73
                        <input
74
                            type="button"
75
                            @click="show_modal_add_to_train = false"
76
                            :value="$__('Close')"
77
                        />
78
                    </fieldset>
79
                </div>
80
            </form>
81
        </div>
82
    </transition>
83
    <div v-if="!initialized">{{ $__("Loading") }}</div>
84
    <div v-else-if="!settings.not_for_loan_waiting_list_in" id="waiting-list">
85
        {{ $__("You need to configure this module first.") }}
86
    </div>
87
    <div v-else id="waiting-list">
88
        <div id="toolbar" class="btn-toolbar">
89
            <a
90
                class="btn btn-default"
91
                @click="show_modal_add_to_waiting_list = true"
92
                ><font-awesome-icon icon="plus" />
93
                {{ $__("Add to waiting list") }}</a
94
            >
95
            <a
96
                v-if="last_items.length > 0"
97
                class="btn btn-default"
98
                @click="show_modal_add_to_train = true"
99
                ><font-awesome-icon icon="plus" />
100
                {{
101
                    $__("Add last %s items to a train").format(
102
                        last_items.length
103
                    )
104
                }}</a
105
            >
106
        </div>
107
        <div v-if="count_waiting_list_items > 0" class="page-section">
108
            <KohaTable
109
                ref="table"
110
                v-bind="tableOptions"
111
                @remove="doRemoveItem"
112
            ></KohaTable>
113
        </div>
114
        <div v-else class="dialog message">
115
            {{ $__("There are no items in the waiting list") }}
116
        </div>
117
    </div>
118
</template>
119
120
<script>
121
import flatPickr from "vue-flatpickr-component"
122
import { inject, ref } from "vue"
123
import { storeToRefs } from "pinia"
124
import { APIClient } from "../../fetch/api-client"
125
import KohaTable from "../KohaTable.vue"
126
127
export default {
128
    setup() {
129
        const table = ref()
130
131
        const PreservationStore = inject("PreservationStore")
132
        const { settings } = storeToRefs(PreservationStore)
133
134
        const { setMessage, setConfirmationDialog, loading, loaded } =
135
            inject("mainStore")
136
137
        return {
138
            table,
139
            settings,
140
            setMessage,
141
            setConfirmationDialog,
142
            loading,
143
            loaded,
144
        }
145
    },
146
    data: function () {
147
        return {
148
            fp_config: flatpickr_defaults,
149
            count_waiting_list_items: 0,
150
            barcode_list: "",
151
            initialized: false,
152
            show_modal_add_to_waiting_list: false,
153
            show_modal_add_to_train: false,
154
            tableOptions: {
155
                columns: this.getTableColumns(),
156
                url: "/api/v1/preservation/waiting-list/items",
157
                options: { embed: "biblio" },
158
                add_filters: true,
159
                actions: {
160
                    0: ["show"],
161
                    "-1": ["remove"],
162
                },
163
            },
164
            last_items: [],
165
            train_list: [],
166
            train_id_selected_for_add: null,
167
        }
168
    },
169
    beforeRouteEnter(to, from, next) {
170
        next(vm => {
171
            vm.getCountWaitingListItems()
172
            vm.getTrainList()
173
        })
174
    },
175
    methods: {
176
        async getCountWaitingListItems() {
177
            const client = APIClient.preservation
178
            client.waiting_list_items.count().then(count => {
179
                this.count_waiting_list_items = count
180
                this.initialized = true
181
            })
182
        },
183
        getTrainList: function () {
184
            const client = APIClient.preservation
185
            client.trains.getAll().then(
186
                trains => (this.train_list = trains),
187
                error => {}
188
            )
189
        },
190
        addItemsToTrain: function (e) {
191
            e.preventDefault()
192
            this.loading()
193
            let item_ids = Object.values(this.last_items)
194
            const client = APIClient.preservation
195
            let promises = []
196
            this.last_items.forEach(i =>
197
                promises.push(
198
                    client.train_items.create(
199
                        { item_id: i.item_id },
200
                        this.train_id_selected_for_add
201
                    )
202
                )
203
            )
204
            Promise.all(promises)
205
                .then(() => {
206
                    this.setMessage(
207
                        this.$__(
208
                            "The items have been added to train %s."
209
                        ).format(this.train_id_selected_for_add),
210
                        true
211
                    )
212
213
                    this.$refs.table.redraw(
214
                        "/api/v1/preservation/waiting-list/items"
215
                    )
216
                    this.show_modal_add_to_train = false
217
                    this.last_items = []
218
                })
219
                .then(() => this.loaded())
220
        },
221
        addItemsToWaitingList: function (e) {
222
            e.preventDefault()
223
            this.show_modal_add_to_waiting_list = false
224
            let items = []
225
            this.barcode_list
226
                .split("\n")
227
                .forEach(barcode => items.push({ barcode }))
228
            const client = APIClient.preservation
229
            client.waiting_list_items.createAll(items).then(
230
                result => {
231
                    if (result.length) {
232
                        this.setMessage(
233
                            this.$__("%s new items added.").format(
234
                                result.length
235
                            ),
236
                            true
237
                        )
238
                        this.last_items = result
239
                        if (this.$refs.table) {
240
                            this.$refs.table.redraw(
241
                                "/api/v1/preservation/waiting-list/items"
242
                            )
243
                        } else {
244
                            this.getCountWaitingListItems()
245
                        }
246
                    } else {
247
                        this.setMessage(this.$__("No items added"))
248
                    }
249
                },
250
                error => {}
251
            )
252
            this.barcode_list = ""
253
        },
254
        doShow: function (biblio, dt, event) {
255
            event.preventDefault()
256
            location.href =
257
                "/cgi-bin/koha/catalogue/detail.pl?biblionumber=" +
258
                biblio.biblio_id
259
        },
260
        doRemoveItem: function (item, dt, event) {
261
            this.setConfirmationDialog(
262
                {
263
                    title: this.$__(
264
                        "Are you sure you want to remove this item from the waiting list?"
265
                    ),
266
                    message: item.barcode,
267
                    accept_label: this.$__("Yes, remove"),
268
                    cancel_label: this.$__("No, do not remove"),
269
                },
270
                () => {
271
                    const client = APIClient.preservation
272
                    client.waiting_list_items.delete(item.item_id).then(
273
                        success => {
274
                            this.setMessage(
275
                                this.$__("Item removed from the waiting list"),
276
                                true
277
                            )
278
                            dt.draw()
279
                        },
280
                        error => {}
281
                    )
282
                }
283
            )
284
        },
285
        getTableColumns: function () {
286
            let escape_str = this.escape_str
287
            return [
288
                {
289
                    data: "biblio.title",
290
                    title: __("Title"),
291
                    searchable: true,
292
                    orderable: true,
293
                    render: function (data, type, row, meta) {
294
                        return `<a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=${row.biblio.biblio_id}">${row.biblio.title}</a>`
295
                    },
296
                },
297
                {
298
                    data: "biblio.author",
299
                    title: __("Author"),
300
                    searchable: true,
301
                    orderable: true,
302
                },
303
                {
304
                    data: "callnumber",
305
                    title: __("Callnumber"),
306
                    searchable: true,
307
                    orderable: true,
308
                },
309
                {
310
                    data: "external_id",
311
                    title: __("Barcode"),
312
                    searchable: true,
313
                    orderable: true,
314
                },
315
            ]
316
        },
317
    },
318
    components: { flatPickr, KohaTable },
319
    name: "WaitingList",
320
}
321
</script>
322
323
<style scoped>
324
#waiting_list {
325
    display: table;
326
}
327
.modal {
328
    position: fixed;
329
    z-index: 9990;
330
    top: 0;
331
    left: 0;
332
    width: 35%;
333
    height: 30%;
334
    background-color: rgba(0, 0, 0, 0.5);
335
    display: table;
336
    transition: opacity 0.3s ease;
337
    margin: auto;
338
    padding: 20px 30px;
339
    background-color: #fff;
340
    border-radius: 2px;
341
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
342
    transition: all 0.3s ease;
343
}
344
</style>
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/api-client.js (+2 lines)
Lines 4-9 import AcquisitionAPIClient from "./acquisition-api-client"; Link Here
4
import AVAPIClient from "./authorised-values-api-client";
4
import AVAPIClient from "./authorised-values-api-client";
5
import ItemAPIClient from "./item-api-client";
5
import ItemAPIClient from "./item-api-client";
6
import SysprefAPIClient from "./system-preferences-api-client";
6
import SysprefAPIClient from "./system-preferences-api-client";
7
import PreservationAPIClient from "./preservation-api-client";
7
8
8
export const APIClient = {
9
export const APIClient = {
9
    erm: new ERMAPIClient(),
10
    erm: new ERMAPIClient(),
Lines 12-15 export const APIClient = { Link Here
12
    authorised_values: new AVAPIClient(),
13
    authorised_values: new AVAPIClient(),
13
    item: new ItemAPIClient(),
14
    item: new ItemAPIClient(),
14
    sysprefs: new SysprefAPIClient(),
15
    sysprefs: new SysprefAPIClient(),
16
    preservation: new PreservationAPIClient(),
15
};
17
};
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/authorised-values-api-client.js (+4 lines)
Lines 9-14 export class AVAPIClient extends HttpClient { Link Here
9
9
10
    get values() {
10
    get values() {
11
        return {
11
        return {
12
            get: category =>
13
                this.get({
14
                    endpoint: `/${category}/authorised_values`,
15
                }),
12
            getCategoriesWithValues: cat_array =>
16
            getCategoriesWithValues: cat_array =>
13
                this.get({
17
                this.get({
14
                    endpoint:
18
                    endpoint:
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/fetch/preservation-api-client.js (+184 lines)
Line 0 Link Here
1
import HttpClient from "./http-client";
2
3
export class PreservationAPIClient extends HttpClient {
4
    constructor() {
5
        super({
6
            baseURL: "/api/v1/preservation/",
7
        });
8
    }
9
10
    get trains() {
11
        return {
12
            get: (id) =>
13
                this.get({
14
                    endpoint: "trains/" + id,
15
                    headers: {
16
                        "x-koha-embed":
17
                            "default_processing,default_processing.attributes,items,items.attributes,items.attributes.processing_attribute",
18
                    },
19
                }),
20
            getAll: (query = {}) =>
21
                this.get({
22
                    endpoint: "trains?" +
23
                        new URLSearchParams({
24
                            _per_page: -1,
25
                            ...(query && { q: JSON.stringify(query) }),
26
                        }),
27
                }),
28
            delete: (id) =>
29
                this.delete({
30
                    endpoint: "trains/" + id,
31
                }),
32
            create: (train) =>
33
                this.post({
34
                    endpoint: "trains",
35
                    body: train,
36
                }),
37
            update: (train, id) =>
38
                this.put({
39
                    endpoint: "trains/" + id,
40
                    body: train,
41
                }),
42
            count: (query = {}) =>
43
                this.count({
44
                    endpoint:
45
                        "trains?" +
46
                        new URLSearchParams({
47
                            _page: 1,
48
                            _per_page: 1,
49
                            ...(query && { q: JSON.stringify(query) }),
50
                        }),
51
                }),
52
        };
53
    }
54
55
    get processings() {
56
        return {
57
            get: (id) =>
58
                this.get({
59
                    endpoint: "processings/" + id,
60
                    headers: {
61
                        "x-koha-embed": "attributes",
62
                    },
63
                }),
64
            getAll: (query) =>
65
                this.get({
66
                    endpoint: "processings?" +
67
                        new URLSearchParams({
68
                            _per_page: -1,
69
                            ...(query && { q: JSON.stringify(query) }),
70
                        }),
71
                }),
72
73
            delete: (id) =>
74
                this.delete({
75
                    endpoint: "processings/" + id,
76
                }),
77
            create: (processing) =>
78
                this.post({
79
                    endpoint: "processings",
80
                    body: processing,
81
                }),
82
            update: (processing, id) =>
83
                this.put({
84
                    endpoint: "processings/" + id,
85
                    body: processing,
86
                }),
87
            count: (query = {}) =>
88
                this.count({
89
                    endpoint:
90
                        "processings?" +
91
                        new URLSearchParams({
92
                            _page: 1,
93
                            _per_page: 1,
94
                            ...(query && { q: JSON.stringify(query) }),
95
                        }),
96
                }),
97
        };
98
    }
99
100
    get train_items() {
101
        return {
102
            get: (train_id, id) =>
103
                this.get({
104
                    endpoint: "trains/" + train_id + "/items/" + id,
105
                    headers: {
106
                        "x-koha-embed":
107
                            "attributes,catalogue_item,catalogue_item.biblio",
108
                    },
109
                }),
110
            delete: (train_id, id) =>
111
                this.delete({
112
                    endpoint: "trains/" + train_id + "/items/" + id,
113
                }),
114
            create: (train_item, train_id) =>
115
                this.post({
116
                    endpoint: "trains/" + train_id + "/items",
117
                    body: train_item,
118
                }),
119
            update: (train_item, train_id, id) =>
120
                this.put({
121
                    endpoint: "trains/" + train_id + "/items/" + id,
122
                    body: train_item,
123
                }),
124
            count: (train_id, query = {}) =>
125
                this.count({
126
                    endpoint:
127
                        "trains/" +
128
                        train_id +
129
                        "/items?" +
130
                        new URLSearchParams({
131
                            _page: 1,
132
                            _per_page: 1,
133
                            ...(query && { q: JSON.stringify(query) }),
134
                        }),
135
                }),
136
        };
137
    }
138
139
    get waiting_list_items() {
140
        return {
141
            get_from_barcode: (barcode) => {
142
                const q = {
143
                    "me.barcode": barcode,
144
                };
145
146
                const params = {
147
                    _page: 1,
148
                    _per_page: 1,
149
                    q: JSON.stringify(q),
150
                };
151
                return this.get({
152
                    endpoint:
153
                        "waiting-list/items?" + new URLSearchParams(params),
154
                    headers: {
155
                        "x-koha-embed": "biblio",
156
                    },
157
                }).then((response) => {
158
                    return response.length ? response[0] : undefined;
159
                });
160
            },
161
            delete: (id) =>
162
                this.delete({
163
                    endpoint: "waiting-list/items/" + id,
164
                }),
165
            createAll: (items) =>
166
                this.post({
167
                    endpoint: "waiting-list/items",
168
                    body: items,
169
                }),
170
            count: (query = {}) =>
171
                this.count({
172
                    endpoint:
173
                        "waiting-list/items?" +
174
                        new URLSearchParams({
175
                            _page: 1,
176
                            _per_page: 1,
177
                            ...(query && { q: JSON.stringify(query) }),
178
                        }),
179
                }),
180
        };
181
    }
182
}
183
184
export default PreservationAPIClient;
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/modules/preservation.ts (+74 lines)
Line 0 Link Here
1
import { createApp } from "vue";
2
import { createWebHistory, createRouter } from "vue-router";
3
import { createPinia } from "pinia";
4
5
import { library } from "@fortawesome/fontawesome-svg-core";
6
import {
7
    faPlus,
8
    faMinus,
9
    faPencil,
10
    faTrash,
11
    faSpinner,
12
    faClose,
13
    faPaperPlane,
14
    faInbox,
15
} from "@fortawesome/free-solid-svg-icons";
16
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
17
import vSelect from "vue-select";
18
19
library.add(
20
    faPlus,
21
    faMinus,
22
    faPencil,
23
    faTrash,
24
    faSpinner,
25
    faClose,
26
    faPaperPlane,
27
    faInbox
28
);
29
30
import App from "../components/Preservation/Main.vue";
31
32
import { routes } from "../routes/preservation";
33
34
const router = createRouter({
35
    history: createWebHistory(),
36
    linkActiveClass: "current",
37
    routes,
38
});
39
40
import { useMainStore } from "../stores/main";
41
import { useAVStore } from "../stores/authorised-values";
42
import { usePreservationStore } from "../stores/preservation";
43
44
const pinia = createPinia();
45
46
const i18n = {
47
    install: (app, options) => {
48
        app.config.globalProperties.$__ = key => {
49
            return window["__"](key);
50
        };
51
    },
52
};
53
54
const app = createApp(App);
55
56
const rootComponent = app
57
    .use(i18n)
58
    .use(pinia)
59
    .use(router)
60
    .component("font-awesome-icon", FontAwesomeIcon)
61
    .component("v-select", vSelect);
62
63
app.config.unwrapInjectedRef = true;
64
const mainStore = useMainStore(pinia);
65
app.provide("mainStore", mainStore);
66
app.provide("AVStore", useAVStore(pinia));
67
app.provide("PreservationStore", usePreservationStore(pinia));
68
69
app.mount("#preservation");
70
71
const { removeMessages } = mainStore;
72
router.beforeEach((to, from) => {
73
    removeMessages(); // This will actually flag the messages as displayed already
74
});
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/routes/preservation.js (+220 lines)
Line 0 Link Here
1
import Home from "../components/Preservation/Home.vue";
2
import TrainsList from "../components/Preservation/TrainsList.vue";
3
import TrainsShow from "../components/Preservation/TrainsShow.vue";
4
import TrainsFormAdd from "../components/Preservation/TrainsFormAdd.vue";
5
import TrainsFormAddItem from "../components/Preservation/TrainsFormAddItem.vue";
6
import WaitingList from "../components/Preservation/WaitingList.vue";
7
import Settings from "../components/Preservation/Settings.vue";
8
import SettingsProcessingsShow from "../components/Preservation/SettingsProcessingsShow.vue";
9
import SettingsProcessingsFormAdd from "../components/Preservation/SettingsProcessingsFormAdd.vue";
10
11
const breadcrumbs = {
12
    home: {
13
        text: "Home", // $t("Home")
14
        path: "/cgi-bin/koha/mainpage.pl",
15
    },
16
    preservation_home: {
17
        text: "Preservation", //$t("Preservation")
18
        path: "/cgi-bin/koha/preservation/home.pl",
19
    },
20
    trains: {
21
        text: "Trains", // $t("Trains")
22
        path: "/cgi-bin/koha/preservation/trains",
23
    },
24
    waiting_list: {
25
        text: "Waiting list", // $t("Waiting list")
26
        path: "/cgi-bin/koha/preservation/waiting-list",
27
    },
28
    settings: {
29
        home: {
30
            text: "Settings", // $t("Settings")
31
            path: "/cgi-bin/koha/preservation/settings",
32
        },
33
        processings: {
34
            home: {
35
                text: "Processings", //$t("Processings")
36
            },
37
        },
38
    },
39
};
40
const breadcrumb_paths = {
41
    trains: [
42
        breadcrumbs.home,
43
        breadcrumbs.preservation_home,
44
        breadcrumbs.trains,
45
    ],
46
    settings: [
47
        breadcrumbs.home,
48
        breadcrumbs.preservation_home,
49
        breadcrumbs.settings.home,
50
    ],
51
    settings_processings: [
52
        breadcrumbs.home,
53
        breadcrumbs.preservation_home,
54
        breadcrumbs.settings.home,
55
    ],
56
};
57
58
function build_breadcrumb(parent_breadcrumb, current) {
59
    let breadcrumb = parent_breadcrumb.flat(Infinity);
60
    if (current) {
61
        breadcrumb.push({
62
            text: current,
63
        });
64
    }
65
    return breadcrumb;
66
}
67
68
export const routes = [
69
    {
70
        path: "/cgi-bin/koha/mainpage.pl",
71
        beforeEnter(to, from, next) {
72
            window.location.href = "/cgi-bin/koha/mainpage.pl";
73
        },
74
    },
75
    {
76
        path: "/cgi-bin/koha/preservation/home.pl",
77
        component: Home,
78
        meta: {
79
            breadcrumb: () => [breadcrumbs.home, breadcrumbs.preservation_home],
80
        },
81
    },
82
    {
83
        path: "/cgi-bin/koha/preservation/trains",
84
        children: [
85
            {
86
                path: "",
87
                component: TrainsList,
88
                meta: {
89
                    breadcrumb: () => breadcrumb_paths.trains,
90
                },
91
            },
92
            {
93
                path: ":train_id",
94
                children: [
95
                    {
96
                        path: "",
97
                        component: TrainsShow,
98
                        meta: {
99
                            breadcrumb: () =>
100
                                build_breadcrumb(
101
                                    breadcrumb_paths.trains,
102
                                    "Show train" // $t("Show train")
103
                                ),
104
                        },
105
                    },
106
                    {
107
                        path: "items",
108
                        children: [
109
                            {
110
                                path: "add",
111
                                component: TrainsFormAddItem,
112
                                meta: {
113
                                    breadcrumb: () =>
114
                                        build_breadcrumb(
115
                                            breadcrumb_paths.trains,
116
                                            "Add item to train" // $t("Add item to train")
117
                                        ),
118
                                },
119
                            },
120
                            {
121
                                path: "edit/:train_item_id",
122
                                component: TrainsFormAddItem,
123
                                meta: {
124
                                    breadcrumb: () =>
125
                                        build_breadcrumb(
126
                                            breadcrumb_paths.trains,
127
                                            "Edit item in train" // $t("Edit item in train")
128
                                        ),
129
                                },
130
                            },
131
                        ],
132
                    },
133
                ],
134
            },
135
            {
136
                path: "add",
137
                component: TrainsFormAdd,
138
                meta: {
139
                    breadcrumb: () =>
140
                        build_breadcrumb(
141
                            breadcrumb_paths.trains,
142
                            "Add train" // $t("Add train")
143
                        ),
144
                },
145
            },
146
            {
147
                path: "edit/:train_id",
148
                component: TrainsFormAdd,
149
                meta: {
150
                    breadcrumb: () =>
151
                        build_breadcrumb(
152
                            breadcrumb_paths.trains,
153
                            "Edit train" // $t("Edit train")
154
                        ),
155
                },
156
            },
157
        ],
158
    },
159
    {
160
        path: "/cgi-bin/koha/preservation/waiting-list",
161
        component: WaitingList,
162
        meta: {
163
            breadcrumb: () => [
164
                breadcrumbs.home,
165
                breadcrumbs.preservation_home,
166
                breadcrumbs.waiting_list,
167
            ],
168
        },
169
    },
170
    {
171
        path: "/cgi-bin/koha/preservation/settings",
172
        children: [
173
            {
174
                path: "",
175
                component: Settings,
176
                meta: {
177
                    breadcrumb: () => breadcrumb_paths.settings,
178
                },
179
            },
180
            {
181
                path: "processings",
182
                children: [
183
                    {
184
                        path: ":processing_id",
185
                        component: SettingsProcessingsShow,
186
                        meta: {
187
                            breadcrumb: () =>
188
                                build_breadcrumb(
189
                                    breadcrumb_paths.settings_processings,
190
                                    "Show processing" // $t("Show processing")
191
                                ),
192
                        },
193
                    },
194
                    {
195
                        path: "add",
196
                        component: SettingsProcessingsFormAdd,
197
                        meta: {
198
                            breadcrumb: () =>
199
                                build_breadcrumb(
200
                                    breadcrumb_paths.settings_processings,
201
                                    "Add processing" // $t("Add processing")
202
                                ),
203
                        },
204
                    },
205
                    {
206
                        path: "edit/:processing_id",
207
                        component: SettingsProcessingsFormAdd,
208
                        meta: {
209
                            breadcrumb: () =>
210
                                build_breadcrumb(
211
                                    breadcrumb_paths.settings_processings,
212
                                    "Edit processing" // $t("Edit processing")
213
                                ),
214
                        },
215
                    },
216
                ],
217
            },
218
        ],
219
    },
220
];
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/stores/authorised-values.js (+1 lines)
Lines 36-41 export const useAVStore = defineStore("authorised_values", { Link Here
36
        av_package_types: [],
36
        av_package_types: [],
37
        av_package_content_types: [],
37
        av_package_content_types: [],
38
        av_title_publication_types: [],
38
        av_title_publication_types: [],
39
        av_notforloan: [],
39
    }),
40
    }),
40
    actions: {
41
    actions: {
41
        get_lib_from_av(arr_name, av) {
42
        get_lib_from_av(arr_name, av) {
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/stores/preservation.js (+10 lines)
Line 0 Link Here
1
import { defineStore } from "pinia";
2
3
export const usePreservationStore = defineStore("preservation", {
4
    state: () => ({
5
        settings: {
6
            not_for_loan_waiting_list_in: null,
7
            not_for_loan_default_train_in: 0,
8
        },
9
    }),
10
});
(-)a/webpack.config.js (-1 / +1 lines)
Lines 6-11 const webpack = require('webpack'); Link Here
6
module.exports = {
6
module.exports = {
7
  entry: {
7
  entry: {
8
    erm: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/erm.ts",
8
    erm: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/erm.ts",
9
    preservation: "./koha-tmpl/intranet-tmpl/prog/js/vue/modules/preservation.ts",
9
  },
10
  },
10
  output: {
11
  output: {
11
    filename: "[name].js",
12
    filename: "[name].js",
12
- 

Return to bug 30708