Line 0
Link Here
|
|
|
1 |
<template> |
2 |
<form |
3 |
class="modal-content" |
4 |
id="circulation-trigger-form-add" |
5 |
@submit="addCircRule($event)" |
6 |
> |
7 |
<div class="modal-header"> |
8 |
<h1 class="modal-title"> |
9 |
{{ $__("Circulation Trigger Configuration") }} |
10 |
</h1> |
11 |
<router-link |
12 |
class="btn-close" |
13 |
type="button" |
14 |
:to="{ |
15 |
name: 'CirculationTriggersList', |
16 |
}" |
17 |
></router-link> |
18 |
</div> |
19 |
<div class="modal-body"> |
20 |
<div class="page-section bg-info" v-if="ruleSetInitialized"> |
21 |
<h2>{{ $__("Circulation context") }}</h2> |
22 |
<TriggerContext :ruleSetInfo="ruleSetInfo" /> |
23 |
</div> |
24 |
<div v-else-if="editMode !== 'confirmContext'"> |
25 |
<p>{{ $__("Loading rule set information...") }}</p> |
26 |
</div> |
27 |
<fieldset class="rows" v-if="contextInitialized"> |
28 |
<legend>{{ $__("Confirm trigger context") }}</legend> |
29 |
<ol> |
30 |
<li> |
31 |
<label for="library_id" class="required" |
32 |
>{{ $__("Library") }}:</label |
33 |
> |
34 |
<v-select |
35 |
id="library_id" |
36 |
v-model="context.library_id" |
37 |
label="name" |
38 |
:reduce="lib => lib.library_id" |
39 |
:options="libraries" |
40 |
:disabled="editMode !== 'confirmContext'" |
41 |
> |
42 |
<template #search="{ attributes, events }"> |
43 |
<input |
44 |
:required="!context.library_id" |
45 |
class="vs__search" |
46 |
v-bind="attributes" |
47 |
v-on="events" |
48 |
/> |
49 |
</template> |
50 |
</v-select> |
51 |
<span class="required">{{ $__("Required") }}</span> |
52 |
</li> |
53 |
<li> |
54 |
<label for="patron_category_id" class="required" |
55 |
>{{ $__("Patron category") }}:</label |
56 |
> |
57 |
<v-select |
58 |
id="patron_category_id" |
59 |
v-model="context.patron_category_id" |
60 |
label="name" |
61 |
:reduce="cat => cat.patron_category_id" |
62 |
:options="patronCategories" |
63 |
:disabled="editMode !== 'confirmContext'" |
64 |
> |
65 |
<template #search="{ attributes, events }"> |
66 |
<input |
67 |
:required="!context.patron_category_id" |
68 |
class="vs__search" |
69 |
v-bind="attributes" |
70 |
v-on="events" |
71 |
/> |
72 |
</template> |
73 |
</v-select> |
74 |
<span class="required">{{ $__("Required") }}</span> |
75 |
</li> |
76 |
<li> |
77 |
<label for="item_type_id" class="required" |
78 |
>{{ $__("Item type") }}:</label |
79 |
> |
80 |
<v-select |
81 |
id="item_type_id" |
82 |
v-model="context.item_type_id" |
83 |
label="description" |
84 |
:reduce="type => type.item_type_id" |
85 |
:options="itemTypes" |
86 |
:disabled="editMode !== 'confirmContext'" |
87 |
> |
88 |
<template #search="{ attributes, events }"> |
89 |
<input |
90 |
:required="!context.item_type_id" |
91 |
class="vs__search" |
92 |
v-bind="attributes" |
93 |
v-on="events" |
94 |
/> |
95 |
</template> |
96 |
</v-select> |
97 |
<span class="required">{{ $__("Required") }}</span> |
98 |
</li> |
99 |
</ol> |
100 |
<div v-if="editMode === 'confirmContext'"> |
101 |
<router-link |
102 |
:to="{ |
103 |
name: 'CirculationTriggersSelectOrAdd', |
104 |
query: context, |
105 |
}" |
106 |
class="btn btn-default btn-xs" |
107 |
><i class="fa-solid fa-pencil"></i> |
108 |
{{ $__("Confirm context") }}</router-link |
109 |
> |
110 |
</div> |
111 |
<div |
112 |
class="page-section" |
113 |
v-if="ruleSetInitialized && editMode !== 'confirmContext'" |
114 |
> |
115 |
<TriggersTable |
116 |
:triggerNumber="triggerNumber" |
117 |
:modal="true" |
118 |
:actions="true" |
119 |
:ruleSets="effectiveTriggerFilteredRuleSets" |
120 |
:ruleSetBeingEdited="currentRuleSet" |
121 |
:triggerBeingEdited="triggerBeingEdited" |
122 |
/> |
123 |
</div> |
124 |
</fieldset> |
125 |
<div v-else> |
126 |
<p>{{ $__("Loading context...") }}</p> |
127 |
</div> |
128 |
<fieldset class="rows" v-if="alertMessage"> |
129 |
<div class="alert alert-info">{{ alertMessage }}</div> |
130 |
</fieldset> |
131 |
<fieldset |
132 |
class="rows" |
133 |
v-if=" |
134 |
(ruleSetInitialized && editMode === 'edit') || |
135 |
editMode === 'add' |
136 |
" |
137 |
> |
138 |
<legend v-if="editMode === 'add'"> |
139 |
{{ $__("Add new trigger") }} |
140 |
{{ " " + triggerNumber }} |
141 |
</legend> |
142 |
<legend v-else> |
143 |
{{ $__("Edit trigger") }} {{ " " + triggerNumber }} |
144 |
</legend> |
145 |
<div class="page-section bg-info"> |
146 |
<p> |
147 |
{{ |
148 |
$__( |
149 |
"NOTE: Delay for a given trigger can be pushed forward or backwards only within the bounds of what its two neighbouring triggers allows." |
150 |
) |
151 |
}} |
152 |
</p> |
153 |
</div> |
154 |
<ol> |
155 |
<li> |
156 |
<label for="overdue_delay">{{ $__("Delay") }}: </label> |
157 |
<div class="numeric-input-wrapper"> |
158 |
<div class="input-with-clear"> |
159 |
<input |
160 |
@input="setAllowSubmission" |
161 |
id="overdue_delay" |
162 |
v-model=" |
163 |
ruleSetToSubmit[ |
164 |
`overdue_${triggerNumber}_delay` |
165 |
] |
166 |
" |
167 |
type="number" |
168 |
:placeholder=" |
169 |
fallbackRuleSet?.[ |
170 |
`overdue_${triggerNumber}_delay` |
171 |
] |
172 |
" |
173 |
:min="minDelay" |
174 |
:max="maxDelay" |
175 |
class="numeric-input" |
176 |
/> |
177 |
<button |
178 |
v-if=" |
179 |
ruleSetToSubmit[ |
180 |
`overdue_${triggerNumber}_delay` |
181 |
] !== null && |
182 |
ruleSetToSubmit[ |
183 |
`overdue_${triggerNumber}_delay` |
184 |
] !== undefined |
185 |
" |
186 |
type="button" |
187 |
class="clear-btn" |
188 |
@click="handleSetDelayToNull" |
189 |
> |
190 |
<svg |
191 |
xmlns="http://www.w3.org/2000/svg" |
192 |
width="10" |
193 |
height="10" |
194 |
> |
195 |
<path |
196 |
d="M6.895455 5l2.842897-2.842898c.348864-.348863.348864-.914488 0-1.263636L9.106534.261648c-.348864-.348864-.914489-.348864-1.263636 0L5 3.104545 2.157102.261648c-.348863-.348864-.914488-.348864-1.263636 0L.261648.893466c-.348864.348864-.348864.914489 0 1.263636L3.104545 5 .261648 7.842898c-.348864.348863-.348864.914488 0 1.263636l.631818.631818c.348864.348864.914773.348864 1.263636 0L5 6.895455l2.842898 2.842897c.348863.348864.914772.348864 1.263636 0l.631818-.631818c.348864-.348864.348864-.914489 0-1.263636L6.895455 5z" |
197 |
></path> |
198 |
</svg> |
199 |
</button> |
200 |
<div class="chevron-buttons"> |
201 |
<button |
202 |
type="button" |
203 |
class="increment-btn" |
204 |
@click="incrementDelay" |
205 |
> |
206 |
â–´ |
207 |
</button> |
208 |
<button |
209 |
type="button" |
210 |
class="decrement-btn" |
211 |
@click="decrementDelay" |
212 |
> |
213 |
â–¾ |
214 |
</button> |
215 |
</div> |
216 |
</div> |
217 |
</div> |
218 |
</li> |
219 |
<li> |
220 |
<label for="restricts" |
221 |
>{{ $__("Restricts checkouts") }}:</label |
222 |
> |
223 |
<div> |
224 |
<input |
225 |
@click="setAllowSubmission" |
226 |
type="radio" |
227 |
id="restricts-yes" |
228 |
v-model=" |
229 |
ruleSetToSubmit[ |
230 |
`overdue_${triggerNumber}_restrict` |
231 |
] |
232 |
" |
233 |
:value="1" |
234 |
/> |
235 |
{{ $__("Yes") }} |
236 |
<input |
237 |
@click="setAllowSubmission" |
238 |
type="radio" |
239 |
id="restricts-no" |
240 |
v-model=" |
241 |
ruleSetToSubmit[ |
242 |
`overdue_${triggerNumber}_restrict` |
243 |
] |
244 |
" |
245 |
:value="0" |
246 |
/> |
247 |
{{ $__("No") }} |
248 |
<input |
249 |
@click="setAllowSubmission" |
250 |
type="radio" |
251 |
id="restricts-fallback" |
252 |
v-model=" |
253 |
ruleSetToSubmit[ |
254 |
`overdue_${triggerNumber}_restrict` |
255 |
] |
256 |
" |
257 |
:value="null" |
258 |
/> |
259 |
{{ $__("Fallback to default") }} |
260 |
<span |
261 |
v-if=" |
262 |
fallbackRuleSet?.[ |
263 |
`overdue_${triggerNumber}_restrict` |
264 |
] !== null |
265 |
" |
266 |
> |
267 |
({{ |
268 |
fallbackRuleSet?.[ |
269 |
`overdue_${triggerNumber}_restrict` |
270 |
] === "1" |
271 |
? $__("Yes") |
272 |
: $__("No") |
273 |
}}) |
274 |
</span> |
275 |
</div> |
276 |
</li> |
277 |
</ol> |
278 |
</fieldset> |
279 |
<div v-else-if="editMode === 'add' || editMode === 'edit'"> |
280 |
<p>{{ $__("Loading circulation rules...") }}</p> |
281 |
</div> |
282 |
<fieldset |
283 |
class="rows" |
284 |
v-if=" |
285 |
(ruleSetInitialized && editMode === 'edit') || |
286 |
editMode === 'add' |
287 |
" |
288 |
> |
289 |
<legend v-if="ruleSetInfo.triggerCount < triggerNumber"> |
290 |
{{ $__("Notice for trigger") }} |
291 |
{{ " " + triggerNumber }} |
292 |
</legend> |
293 |
<legend v-else> |
294 |
{{ $__("Edit notice for trigger") }} |
295 |
{{ " " + triggerNumber }} |
296 |
</legend> |
297 |
<ol> |
298 |
<li> |
299 |
<label for="letter_code">{{ $__("Letter") }}:</label> |
300 |
<v-select |
301 |
id="letter_code" |
302 |
v-model=" |
303 |
ruleSetToSubmit[ |
304 |
`overdue_${triggerNumber}_notice` |
305 |
] |
306 |
" |
307 |
label="name" |
308 |
:reduce="type => type.code" |
309 |
:options="filteredLetters" |
310 |
@update:modelValue="setAllowSubmission" |
311 |
> |
312 |
<template #search="{ attributes, events }"> |
313 |
<input |
314 |
class="vs__search" |
315 |
v-bind="attributes" |
316 |
v-on="events" |
317 |
:placeholder=" |
318 |
ruleSetToSubmit[ |
319 |
`overdue_${triggerNumber}_notice` |
320 |
] === null || |
321 |
ruleSetToSubmit[ |
322 |
`overdue_${triggerNumber}_notice` |
323 |
] === undefined |
324 |
? letters.find( |
325 |
letter => |
326 |
letter.code === |
327 |
fallbackRuleSet?.[ |
328 |
`overdue_${triggerNumber}_notice` |
329 |
] |
330 |
)?.name || |
331 |
fallbackRuleSet?.[ |
332 |
`overdue_${triggerNumber}_notice` |
333 |
] |
334 |
: '' |
335 |
" |
336 |
/> |
337 |
</template> |
338 |
</v-select> |
339 |
</li> |
340 |
<li |
341 |
v-if=" |
342 |
ruleSetToSubmit[ |
343 |
`overdue_${triggerNumber}_notice` |
344 |
] !== '' || |
345 |
((ruleSetToSubmit[ |
346 |
`overdue_${triggerNumber}_notice` |
347 |
] === null || |
348 |
ruleSetToSubmit[ |
349 |
`overdue_${triggerNumber}_notice` |
350 |
] === undefined) && |
351 |
fallbackRuleSet?.[ |
352 |
`overdue_${triggerNumber}_notice` |
353 |
] !== '') |
354 |
" |
355 |
> |
356 |
<label for="mtt">{{ $__("Transport type(s)") }}:</label> |
357 |
<v-select |
358 |
id="mtt" |
359 |
v-model=" |
360 |
ruleSetToSubmit[`overdue_${triggerNumber}_mtt`] |
361 |
" |
362 |
label="name" |
363 |
:reduce="type => type.code" |
364 |
:options="transportTypes" |
365 |
multiple |
366 |
:required="true" |
367 |
@update:modelValue="setAllowSubmission" |
368 |
> |
369 |
<template #search="{ attributes, events }"> |
370 |
<input |
371 |
class="vs__search" |
372 |
v-bind="attributes" |
373 |
v-on="events" |
374 |
:placeholder=" |
375 |
ruleSetToSubmit[ |
376 |
`overdue_${triggerNumber}_mtt` |
377 |
] === null || |
378 |
ruleSetToSubmit[ |
379 |
`overdue_${triggerNumber}_mtt` |
380 |
] === undefined || |
381 |
ruleSetToSubmit[ |
382 |
`overdue_${triggerNumber}_mtt` |
383 |
].length === 0 |
384 |
? fallbackRuleSet?.[ |
385 |
`overdue_${triggerNumber}_mtt` |
386 |
] |
387 |
: '' |
388 |
" |
389 |
/> |
390 |
</template> |
391 |
</v-select> |
392 |
</li> |
393 |
</ol> |
394 |
</fieldset> |
395 |
<div v-else-if="editMode === 'add' || editMode === 'edit'"> |
396 |
<p>{{ $__("Loading circulation rules...") }}</p> |
397 |
</div> |
398 |
</div> |
399 |
<div class="modal-footer"> |
400 |
<ButtonSubmit |
401 |
v-if="editMode === 'edit' || editMode === 'add'" |
402 |
:disabled="!allowSubmission" |
403 |
/> |
404 |
<router-link |
405 |
:to="{ |
406 |
name: 'CirculationTriggersList', |
407 |
}" |
408 |
>{{ $__("Cancel") }}</router-link |
409 |
> |
410 |
</div> |
411 |
</form> |
412 |
</template> |
413 |
|
414 |
<script> |
415 |
import TriggersTable from "./TriggersTable.vue"; |
416 |
import { inject } from "vue"; |
417 |
import { storeToRefs } from "pinia"; |
418 |
import ButtonSubmit from "../../ButtonSubmit.vue"; |
419 |
import TriggerContext from "./TriggerContext.vue"; |
420 |
import { isEqual, cloneDeep } from "lodash"; |
421 |
|
422 |
export default { |
423 |
setup() { |
424 |
const circRulesStore = inject("circRulesStore"); |
425 |
const { |
426 |
updateTriggerCount, |
427 |
findEffectiveRule, |
428 |
getSelectedRuleSet, |
429 |
setEffectiveTriggerFilteredRuleSet, |
430 |
updateCircRuleSets, |
431 |
hasConflict, |
432 |
} = circRulesStore; |
433 |
const { |
434 |
letters, |
435 |
libraries, |
436 |
itemTypes, |
437 |
transportTypes, |
438 |
patronCategories, |
439 |
triggerCounts, |
440 |
storeInitialized, |
441 |
} = storeToRefs(circRulesStore); |
442 |
|
443 |
return { |
444 |
letters, |
445 |
itemTypes, |
446 |
libraries, |
447 |
transportTypes, |
448 |
triggerCounts, |
449 |
patronCategories, |
450 |
getSelectedRuleSet, |
451 |
updateTriggerCount, |
452 |
findEffectiveRule, |
453 |
setEffectiveTriggerFilteredRuleSet, |
454 |
updateCircRuleSets, |
455 |
hasConflict, |
456 |
storeInitialized, |
457 |
}; |
458 |
}, |
459 |
data() { |
460 |
return { |
461 |
triggerNumber: 1, |
462 |
context: { |
463 |
library_id: "*", |
464 |
item_type_id: "*", |
465 |
patron_category_id: "*", |
466 |
}, |
467 |
fallbackRuleSet: null, |
468 |
ruleSetInfo: { |
469 |
issuelength: null, |
470 |
decreaseloanholds: null, |
471 |
fine: null, |
472 |
chargeperiod: null, |
473 |
lengthunit: null, |
474 |
triggerCount: null, |
475 |
}, |
476 |
editMode: false, |
477 |
ruleSetToSubmit: null, |
478 |
currentRuleSet: null, |
479 |
triggerBeingEdited: null, |
480 |
minDelay: 0, |
481 |
maxDelay: Infinity, |
482 |
filteredLetters: [], |
483 |
alertMessage: null, |
484 |
allowSubmission: false, |
485 |
effectiveTriggerFilteredRuleSets: [], |
486 |
contextInitialized: false, |
487 |
ruleSetInitialized: false, |
488 |
}; |
489 |
}, |
490 |
beforeMount() { |
491 |
// handle hard refresh mid-stepper workflow by ensuring store is initialized |
492 |
if (!this.storeInitialized) { |
493 |
this.$watch("storeInitialized", newVal => { |
494 |
if (newVal) { |
495 |
this.initializeComponent(); |
496 |
} |
497 |
}); |
498 |
return; |
499 |
} |
500 |
this.initializeComponent(); |
501 |
}, |
502 |
beforeRouteEnter(to, from, next) { |
503 |
if (!from.name) { |
504 |
next(); |
505 |
return; |
506 |
} |
507 |
next(vm => vm.initializeComponent()); |
508 |
}, |
509 |
methods: { |
510 |
initializeComponent() { |
511 |
const { query } = this.$route; |
512 |
this.setEditMode(); |
513 |
this.setContext(query); |
514 |
this.contextInitialized = true; |
515 |
this.setTriggerNumber(query.triggerNumber, this.context.library_id); |
516 |
if ( |
517 |
["selectOrAdd", "add", "edit"].some(str => |
518 |
this.$route.fullPath.includes(str) |
519 |
) |
520 |
) { |
521 |
this.setRuleSets(); |
522 |
} |
523 |
}, |
524 |
async addCircRule(e) { |
525 |
e.preventDefault(); |
526 |
|
527 |
const ruleSetToSubmit = { |
528 |
context: this.context, |
529 |
}; |
530 |
|
531 |
// ensure that no property is set to null as this would delete the rule! |
532 |
if ( |
533 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] !== |
534 |
null && |
535 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] !== |
536 |
this.fallbackRuleSet[`overdue_${this.triggerNumber}_delay`] |
537 |
) { |
538 |
ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] = |
539 |
cloneDeep( |
540 |
this.ruleSetToSubmit[ |
541 |
`overdue_${this.triggerNumber}_delay` |
542 |
] |
543 |
); |
544 |
} |
545 |
|
546 |
if ( |
547 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_notice`] !== |
548 |
null && |
549 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_notice`] !== |
550 |
this.fallbackRuleSet[`overdue_${this.triggerNumber}_notice`] |
551 |
) { |
552 |
ruleSetToSubmit[`overdue_${this.triggerNumber}_notice`] = |
553 |
cloneDeep( |
554 |
this.ruleSetToSubmit[ |
555 |
`overdue_${this.triggerNumber}_notice` |
556 |
] |
557 |
); |
558 |
} |
559 |
|
560 |
if ( |
561 |
this.ruleSetToSubmit[ |
562 |
`overdue_${this.triggerNumber}_restrict` |
563 |
] !== null && |
564 |
this.ruleSetToSubmit[ |
565 |
`overdue_${this.triggerNumber}_restrict` |
566 |
] !== |
567 |
this.fallbackRuleSet[ |
568 |
`overdue_${this.triggerNumber}_restrict` |
569 |
] |
570 |
) { |
571 |
ruleSetToSubmit[`overdue_${this.triggerNumber}_restrict`] = |
572 |
cloneDeep( |
573 |
this.ruleSetToSubmit[ |
574 |
`overdue_${this.triggerNumber}_restrict` |
575 |
] |
576 |
); |
577 |
} |
578 |
|
579 |
if ( |
580 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_mtt`] !== |
581 |
null && |
582 |
Array.isArray( |
583 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_mtt`] |
584 |
) && |
585 |
!isEqual( |
586 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_mtt`], |
587 |
this.fallbackRuleSet[`overdue_${this.triggerNumber}_mtt`] |
588 |
) |
589 |
) { |
590 |
ruleSetToSubmit[`overdue_${this.triggerNumber}_mtt`] = |
591 |
cloneDeep( |
592 |
this.ruleSetToSubmit[ |
593 |
`overdue_${this.triggerNumber}_mtt` |
594 |
].join(",") |
595 |
); |
596 |
} |
597 |
|
598 |
ruleSetToSubmit[`overdue_${this.triggerNumber}_has_rules`] = true; |
599 |
|
600 |
// in edit mode, check for changes from elsewhere to the rule set being edited |
601 |
if (this.editMode === "edit") { |
602 |
const ruleSetInDb = await this.getSelectedRuleSet(this.context); |
603 |
if ( |
604 |
this.hasConflict( |
605 |
ruleSetInDb, |
606 |
this.currentRuleSet, |
607 |
this.triggerNumber |
608 |
) |
609 |
) { |
610 |
this.alertMessage = |
611 |
"Your changes could not be saved as this circulation trigger was updated elsewhere. Please see the updated trigger below."; |
612 |
this.$router.push({ |
613 |
path: "/cgi-bin/koha/admin/circulation_triggers/edit", |
614 |
query: { |
615 |
library_id: this.context.library_id, |
616 |
patron_category_id: this.context.patron_category_id, |
617 |
item_type_id: this.context.item_type_id, |
618 |
triggerNumber: this.triggerNumber, |
619 |
}, |
620 |
}); |
621 |
return; |
622 |
} |
623 |
} |
624 |
|
625 |
await this.updateCircRuleSets(ruleSetToSubmit, this.triggerNumber); |
626 |
await this.$router.push({ |
627 |
name: "CirculationTriggersList", |
628 |
query: { refresh: Date.now() }, |
629 |
}); |
630 |
}, |
631 |
async setRuleSets() { |
632 |
this.updateTriggerCount(); |
633 |
await this.setCurrentRuleSet(); |
634 |
|
635 |
this.ruleSetToSubmit = cloneDeep(this.currentRuleSet); |
636 |
|
637 |
this.setMinDelay(); |
638 |
if (this.ruleSetToSubmit === null) { |
639 |
this.ruleSetToSubmit = { |
640 |
context: this.context, |
641 |
[`overdue_${this.triggerNumber}_delay`]: this.minDelay, |
642 |
[`overdue_${this.triggerNumber}_notice`]: null, |
643 |
[`overdue_${this.triggerNumber}_mtt`]: null, |
644 |
[`overdue_${this.triggerNumber}_restrict`]: null, |
645 |
}; |
646 |
} |
647 |
this.setMaxDelay(); |
648 |
this.setRuleSetInfo(); |
649 |
this.effectiveTriggerFilteredRuleSets = |
650 |
this.setEffectiveTriggerFilteredRuleSet(this.context); |
651 |
this.setFallbackRuleSet(); |
652 |
this.setFilteredLetters(); |
653 |
this.setAllowSubmission(); |
654 |
this.ruleSetInitialized = true; |
655 |
}, |
656 |
setContext(query) { |
657 |
this.context.library_id = query.library_id ?? "*"; |
658 |
this.context.item_type_id = query.item_type_id ?? "*"; |
659 |
this.context.patron_category_id = query.patron_category_id ?? "*"; |
660 |
}, |
661 |
setEditMode() { |
662 |
this.editMode = this.$route.path.substring( |
663 |
this.$route.path.lastIndexOf("/") + 1 |
664 |
); |
665 |
}, |
666 |
setRuleSetInfo() { |
667 |
this.ruleSetInfo = { |
668 |
issuelength: this.currentRuleSet.issuelength, |
669 |
decreaseloanholds: this.currentRuleSet.decreaseloanholds, |
670 |
fine: this.currentRuleSet.fine, |
671 |
chargeperiod: this.currentRuleSet.chargeperiod, |
672 |
lengthunit: this.currentRuleSet.lengthunit, |
673 |
triggerCount: this.triggerCounts[this.context.library_id], |
674 |
}; |
675 |
}, |
676 |
async setCurrentRuleSet() { |
677 |
this.currentRuleSet = await this.getSelectedRuleSet( |
678 |
this.context, |
679 |
true |
680 |
); |
681 |
// override the context as fetched from the DBto ensure it matches the selected context |
682 |
// FIXME: consider passing the context to trigger table separately |
683 |
this.currentRuleSet.context = this.context; |
684 |
}, |
685 |
setTriggerNumber(triggerNumber, library_id) { |
686 |
this.triggerNumber = |
687 |
this.editMode === "edit" |
688 |
? triggerNumber |
689 |
: this.triggerCounts[library_id] + 1; |
690 |
}, |
691 |
setFallbackRuleSet() { |
692 |
this.fallbackRuleSet = { |
693 |
[`overdue_${this.triggerNumber}_delay`]: this.findEffectiveRule( |
694 |
this.context, |
695 |
"delay", |
696 |
i |
697 |
).value, |
698 |
}; |
699 |
|
700 |
if (this.editMode === "add") { |
701 |
return; |
702 |
} |
703 |
this.fallbackRuleSet = { |
704 |
[`overdue_${this.triggerNumber}_delay`]: this.findEffectiveRule( |
705 |
this.context, |
706 |
"delay", |
707 |
this.triggerNumber |
708 |
).value, |
709 |
[`overdue_${this.triggerNumber}_notice`]: |
710 |
this.findEffectiveRule( |
711 |
this.context, |
712 |
"notice", |
713 |
this.triggerNumber |
714 |
).value, |
715 |
[`overdue_${this.triggerNumber}_mtt`]: this.findEffectiveRule( |
716 |
this.context, |
717 |
"mtt", |
718 |
this.triggerNumber |
719 |
).value, |
720 |
[`overdue_${this.triggerNumber}_restrict`]: |
721 |
this.findEffectiveRule( |
722 |
this.context, |
723 |
"restrict", |
724 |
this.triggerNumber |
725 |
).value, |
726 |
}; |
727 |
}, |
728 |
setMinDelay() { |
729 |
if ( |
730 |
this.ruleSetToSubmit === null || |
731 |
this.triggerNumber === 0 || |
732 |
this.triggerNumber === 1 |
733 |
) { |
734 |
this.minDelay = 0; |
735 |
return; |
736 |
} |
737 |
const priorTriggerNumber = this.triggerNumber - 1; |
738 |
this.minDelay = this.currentRuleSet[ |
739 |
`overdue_${priorTriggerNumber}_delay` |
740 |
] |
741 |
? parseInt( |
742 |
this.currentRuleSet[`overdue_${priorTriggerNumber}_delay`] |
743 |
) + 1 |
744 |
: 0; |
745 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] = |
746 |
this.minDelay; |
747 |
}, |
748 |
setMaxDelay() { |
749 |
const nextTriggerNumber = parseInt(this.triggerNumber) + 1; |
750 |
this.maxDelay = this.currentRuleSet?.[ |
751 |
`overdue_${nextTriggerNumber}_delay` |
752 |
] |
753 |
? parseInt( |
754 |
this.currentRuleSet[`overdue_${nextTriggerNumber}_delay`] |
755 |
) - 1 |
756 |
: Infinity; |
757 |
}, |
758 |
setFilteredLetters() { |
759 |
let library = this.context.library_id; |
760 |
const branchcodeMatches = letters.filter( |
761 |
letter => letter.branchcode === library |
762 |
); |
763 |
const emptyBranchcodeMatches = letters.filter( |
764 |
letter => letter.branchcode === "" |
765 |
); |
766 |
|
767 |
const uniqueCodes = [ |
768 |
...new Set( |
769 |
[...branchcodeMatches, ...emptyBranchcodeMatches].map( |
770 |
letter => letter.code |
771 |
) |
772 |
), |
773 |
]; |
774 |
|
775 |
this.filteredLetters = letters.filter( |
776 |
letter => |
777 |
uniqueCodes.includes(letter.code) && |
778 |
(letter.branchcode === library || letter.branchcode === "") |
779 |
); |
780 |
}, |
781 |
incrementDelay() { |
782 |
// Check for minDelay and maxDelay |
783 |
const min = this.minDelay !== undefined ? this.minDelay : 1; |
784 |
const max = this.maxDelay !== undefined ? this.maxDelay : Infinity; |
785 |
|
786 |
// Set to minDelay if it's null or undefined |
787 |
if ( |
788 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] === |
789 |
undefined || |
790 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] === |
791 |
null |
792 |
) { |
793 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] = |
794 |
min; |
795 |
} |
796 |
|
797 |
// Increment within the valid range |
798 |
else { |
799 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] = |
800 |
Math.min( |
801 |
this.ruleSetToSubmit[ |
802 |
`overdue_${this.triggerNumber}_delay` |
803 |
] + 1, |
804 |
max |
805 |
); |
806 |
} |
807 |
this.setAllowSubmission(); |
808 |
}, |
809 |
decrementDelay() { |
810 |
// Check for minDelay |
811 |
const min = this.minDelay !== undefined ? this.minDelay : 1; |
812 |
// Decrement only if greater than minDelay |
813 |
if ( |
814 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] > |
815 |
min |
816 |
) { |
817 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`]--; |
818 |
} |
819 |
this.setAllowSubmission(); |
820 |
}, |
821 |
setAllowSubmission() { |
822 |
// if notice is set to "", this translates to 'No letter', for which submisison is allowed, and mtt is redundant |
823 |
const noticeAllowSubmissionAndBypassMtt = |
824 |
this.ruleSetToSubmit?.[ |
825 |
`overdue_${this.triggerNumber}_notice` |
826 |
] === ""; |
827 |
if (noticeAllowSubmissionAndBypassMtt) { |
828 |
this.allowSubmission = true; |
829 |
return; |
830 |
} |
831 |
|
832 |
// check specificity required as 0 is a value that submission must be allowed for |
833 |
const delayAllowsSubmission = |
834 |
this.ruleSetToSubmit?.[`overdue_${this.triggerNumber}_delay`] != |
835 |
null; |
836 |
if (delayAllowsSubmission) { |
837 |
this.allowSubmission = true; |
838 |
return; |
839 |
} |
840 |
|
841 |
// if notice is set, then ensure at least one transport has been set |
842 |
const noticeAllowSubmisison = |
843 |
this.ruleSetToSubmit?.[ |
844 |
`overdue_${this.triggerNumber}_notice` |
845 |
] != null; |
846 |
const mttHasItems = |
847 |
this.ruleSetToSubmit?.[`overdue_${this.triggerNumber}_mtt`] |
848 |
?.length; |
849 |
if (noticeAllowSubmisison && mttHasItems) { |
850 |
this.allowSubmission = true; |
851 |
return; |
852 |
} |
853 |
|
854 |
const restrictAllowsSubmission = |
855 |
this.ruleSetToSubmit?.[ |
856 |
`overdue_${this.triggerNumber}_restrict` |
857 |
] != null; |
858 |
if (restrictAllowsSubmission) { |
859 |
this.allowSubmission = true; |
860 |
return; |
861 |
} |
862 |
this.allowSubmission = false; |
863 |
}, |
864 |
handleSetDelayToNull() { |
865 |
this.ruleSetToSubmit[`overdue_${this.triggerNumber}_delay`] = null; |
866 |
this.setAllowSubmission(); |
867 |
}, |
868 |
}, |
869 |
watch: { |
870 |
$route: { |
871 |
immediate: true, |
872 |
handler: function (newVal, oldVal) { |
873 |
if ( |
874 |
oldVal && |
875 |
oldVal.query.triggerNumber && |
876 |
newVal.query.triggerNumber !== oldVal.query.triggerNumber |
877 |
) { |
878 |
this.$router.go(0); |
879 |
} |
880 |
}, |
881 |
}, |
882 |
}, |
883 |
components: { TriggersTable, ButtonSubmit, TriggerContext }, |
884 |
}; |
885 |
</script> |
886 |
|
887 |
<style scoped> |
888 |
#circulation-trigger-form-add { |
889 |
max-height: 90vh; |
890 |
} |
891 |
|
892 |
form li { |
893 |
display: flex; |
894 |
align-items: center; |
895 |
} |
896 |
|
897 |
.numeric-input-wrapper { |
898 |
position: relative; |
899 |
display: inline-block; |
900 |
width: 30%; |
901 |
} |
902 |
|
903 |
.input-with-clear { |
904 |
position: relative; |
905 |
display: flex; |
906 |
align-items: center; |
907 |
width: 100%; |
908 |
} |
909 |
|
910 |
.numeric-input { |
911 |
padding-right: 40px; /* Adjust to leave space for clear button */ |
912 |
padding-left: 0.25em; |
913 |
padding-top: 2px; |
914 |
padding-bottom: 2px; |
915 |
width: 100%; |
916 |
border-radius: 4px; |
917 |
border: 1px solid #ccc; |
918 |
font-size: 16px; |
919 |
box-sizing: border-box; |
920 |
transition: border-color 0.2s ease; |
921 |
} |
922 |
|
923 |
.clear-btn { |
924 |
position: absolute; |
925 |
right: 22px; /* Adjust positioning */ |
926 |
fill: var(--vs-controls-color); |
927 |
background-color: transparent; |
928 |
border: 0; |
929 |
font-size: 1.2em; |
930 |
color: #333; |
931 |
cursor: pointer; |
932 |
z-index: 2; /* Ensure it is above the input */ |
933 |
} |
934 |
|
935 |
.button:active:hover, |
936 |
.clear-btn:active:hover { |
937 |
background-color: #d4d4d4; |
938 |
border-color: #8c8c8c; |
939 |
} |
940 |
|
941 |
/* Chevron buttons container */ |
942 |
.chevron-buttons { |
943 |
display: flex; |
944 |
flex-direction: column; |
945 |
position: absolute; |
946 |
right: 0px; |
947 |
top: 0; |
948 |
bottom: 0; |
949 |
width: 16px; |
950 |
padding: 0px 5px 0px 2px; |
951 |
justify-content: center; |
952 |
z-index: 2; |
953 |
} |
954 |
|
955 |
/* Chevron button styles */ |
956 |
.increment-btn, |
957 |
.decrement-btn { |
958 |
background-color: transparent; |
959 |
border: 0px solid #ccc; |
960 |
font-size: 10px; |
961 |
padding: 0px; |
962 |
cursor: pointer; |
963 |
color: rgba(60, 60, 60, 0.5); |
964 |
border-radius: 2px; |
965 |
} |
966 |
|
967 |
.increment-btn:hover, |
968 |
.decrement-btn:hover { |
969 |
background-color: #ddd; |
970 |
} |
971 |
|
972 |
/* Hide the native increment/decrement buttons */ |
973 |
input[type="number"]::-webkit-inner-spin-button, |
974 |
input[type="number"]::-webkit-outer-spin-button { |
975 |
-webkit-appearance: none; |
976 |
margin: 0; |
977 |
} |
978 |
|
979 |
input[type="number"] { |
980 |
-moz-appearance: textfield; /* For Firefox */ |
981 |
} |
982 |
|
983 |
.numeric-input:focus, |
984 |
.numeric-input:hover { |
985 |
border-color: #007bff; /* Match focus color of v-select */ |
986 |
outline: none; |
987 |
} |
988 |
|
989 |
.dialog.alert |
990 |
fieldset:not(.bg-danger):not(.bg-warning):not(.bg-info):not( |
991 |
.bg-success |
992 |
):not(.bg-primary):not(.action), |
993 |
.dialog.error |
994 |
fieldset:not(.bg-danger):not(.bg-warning):not(.bg-info):not( |
995 |
.bg-success |
996 |
):not(.bg-primary):not(.action) { |
997 |
margin: 0; |
998 |
background-color: rgba(255, 255, 255, 1); |
999 |
} |
1000 |
|
1001 |
.router-link-active { |
1002 |
margin-left: 10px; |
1003 |
} |
1004 |
.modal-header { |
1005 |
display: flex; |
1006 |
justify-content: space-between; |
1007 |
} |
1008 |
</style> |