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

(-)a/admin/curbside_pickup.pl (+95 lines)
Line 0 Link Here
1
#! /usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
use CGI qw ( -utf8 );
20
use C4::Context;
21
use C4::Auth qw( get_template_and_user );
22
use C4::Output qw( output_html_with_http_headers );
23
24
use Koha::CurbsidePickupPolicies;
25
use Koha::Libraries;
26
27
my $input       = CGI->new;
28
my $op          = $input->param('op') || 'list';
29
my @messages;
30
31
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
32
    {
33
        template_name => "admin/curbside_pickup.tt",
34
        query         => $input,
35
        type          => "intranet",
36
        flagsrequired => { parameters => 'manage_curbside_pickups' },
37
    }
38
);
39
40
my $libraries = Koha::Libraries->search( {}, { order_by => ['branchname'] } );
41
if ( $op eq 'save' ) {
42
    foreach my $library ( $libraries->as_list ) {
43
        my $branchcode = $library->branchcode;
44
45
        my $params = {
46
47
            branchcode      => $branchcode,
48
            enabled         => scalar $input->param("enable-$branchcode") || 0,
49
            pickup_interval => scalar $input->param("interval-$branchcode"),
50
            patrons_per_interval    => scalar $input->param("max-per-interval-$branchcode"),
51
            patron_scheduled_pickup => scalar $input->param("patron-scheduled-$branchcode") || 0,
52
        };
53
54
        for my $day (
55
            qw( sunday monday tuesday wednesday thursday friday saturday ))
56
        {
57
            for my $start_end (qw( start end )) {
58
                for my $hour_min (qw( hour minute )) {
59
60
                    my $value = $input->param(
61
                        "pickup-$start_end-$hour_min-$day-$branchcode");
62
                    $value = undef if $value eq q{};
63
64
                    my $key = $day . '_' . $start_end . '_' . $hour_min;
65
66
                    $params->{$key} = $value;
67
                }
68
            }
69
        }
70
71
        my $CurbsidePickupPolicy =
72
          Koha::CurbsidePickupPolicies->find( { branchcode => $branchcode } );
73
        $CurbsidePickupPolicy->delete if $CurbsidePickupPolicy;
74
75
        Koha::CurbsidePickupPolicy->new($params)->store();
76
    }
77
    $op = 'list';
78
}
79
80
if ( $op eq 'list' ) {
81
    $template->param(
82
        policies => {
83
            map { $_->branchcode => $_ }
84
              Koha::CurbsidePickupPolicies->search->as_list
85
        },
86
        libraries => $libraries,
87
    );
88
}
89
90
$template->param(
91
    messages => \@messages,
92
    op       => $op,
93
);
94
95
output_html_with_http_headers $input, $cookie, $template->output;
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/curbside_pickup.tt (-1 / +316 lines)
Line 0 Link Here
0
- 
1
[% USE raw %]
2
[% USE Asset %]
3
[% SET footerjs = 1 %]
4
[% USE TablesSettings %]
5
[% INCLUDE 'doc-head-open.inc' %]
6
<title>
7
    Curbside pickup &rsaquo; Administration &rsaquo; Koha
8
</title>
9
[% INCLUDE 'doc-head-close.inc' %]
10
<style>
11
    .pickup-slot {
12
        border: solid;
13
        padding: 1em;
14
    }
15
</style>
16
</head>
17
18
<body id="admin_curbside_pickup" class="admin">
19
[% INCLUDE 'header.inc' %]
20
[% INCLUDE 'cities-admin-search.inc' %]
21
22
[% USE format_minutes = format('%02d') %]
23
[% MACRO minutes_format BLOCK %][% IF m != "" %][% format_minutes(m) | html %][% END %][% END %]
24
<nav id="breadcrumbs" aria-label="Breadcrumb" class="breadcrumb">
25
    <ol>
26
        <li>
27
            <a href="/cgi-bin/koha/mainpage.pl">Home</a>
28
        </li>
29
        <li>
30
            <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
31
        </li>
32
33
        <li>
34
            <a href="#" aria-current="page">
35
                Curbside pickup
36
            </a>
37
        </li>
38
    </ol>
39
</nav>
40
41
<div class="main container-fluid">
42
    <div class="row">
43
        <div class="col-sm-10 col-sm-push-2">
44
            <main>
45
46
[% FOR m IN messages %]
47
    <div class="dialog [% m.type | html %]">
48
        [% SWITCH m.code %]
49
        [% CASE %]
50
            <span>[% m.code | html %]</span>
51
        [% END %]
52
    </div>
53
[% END %]
54
55
[% IF op == 'list' %]
56
57
    <h1>Curbside pickup configuration</h1>
58
59
    <form method="post" class="form">
60
        <div id="curbside_pickup_tabs" class="toptabs">
61
            <ul class="nav nav-tabs" role="tablist">
62
                [% FOREACH l IN libraries %]
63
                    [% IF loop.first %]
64
                    <li role="presentation" class="active">
65
                    [% ELSE %]
66
                    <li role="presentation">
67
                    [% END %]
68
                        <a href="#conf-[% l.branchcode | uri %]" aria-controls="conf-[% l.branchcode | html %]" role="tab" data-toggle="tab">[% l.branchname | html %]</a>
69
                    </li>
70
                [% END %]
71
            </ul>
72
73
            <div class="tab-content">
74
                [% FOREACH l IN libraries %]
75
                    [% SET branchcode = l.branchcode %]
76
                    [% IF loop.first %]
77
                    <div id="conf-[% l.branchcode | html %]" role="tabpanel" class="tab-pane active">
78
                    [% ELSE %]
79
                    <div id="conf-[% l.branchcode | html %]" role="tabpanel" class="tab-pane">
80
                    [% END %]
81
                        <fieldset class="rows" style="float: none;">
82
                            <ol>
83
                                <li>
84
                                    <label for="enable-[% l.branchcode | html %]">Enable: </label>
85
                                    [% IF policies.$branchcode.enabled %]
86
                                        <input name="enable-[% l.branchcode | html %]" id="enable-[% l.branchcode | html %]" value="1" type="checkbox" checked>
87
                                    [% ELSE %]
88
                                        <input name="enable-[% l.branchcode | html %]" id="enable-[% l.branchcode | html %]" value="1" type="checkbox">
89
                                    [% END %]
90
                                    <span class="hint">Enable curbside pickup.<span>
91
                                </li>
92
93
                                <li>
94
                                    <label for="interval-[% l.branchcode | html %]">Pickup interval: </label>
95
                                    <input name="interval-[% l.branchcode | html %]" id="interval-[% l.branchcode | html %]" value="[% policies.$branchcode.pickup_interval | html %]" type="text">
96
                                    <span class="hint">Number of minutes each curbside pickup interaction will take.</span>
97
                                </li>
98
99
                                <li>
100
                                    <label for="max-per-interval-[% l.branchcode | html %]">Maximum patrons per interval: </label>
101
                                    <input name="max-per-interval-[% l.branchcode | html %]" id="max-per-interval-[% l.branchcode | html %]" value="[% policies.$branchcode.patrons_per_interval | html %]" type="text">
102
                                    <span class="hint">Maximum number of simultaneus pickups per interval.</span>
103
                                </li>
104
105
                                <li>
106
                                    <label for="patron-scheduled-[% l.branchcode | html %]">Patron-scheduled pickup: </label>
107
                                    [% IF policies.$branchcode.patron_scheduled_pickup %]
108
                                        <input name="patron-scheduled-[% l.branchcode | html %]" id="patron-scheduled-[% l.branchcode | html %]" value="1" type="checkbox" checked>
109
                                    [% ELSE %]
110
                                        <input name="patron-scheduled-[% l.branchcode | html %]" id="patron-scheduled-[% l.branchcode | html %]" value="1" type="checkbox">
111
                                    [% END %]
112
                                    <span class="hint">Enable patrons to schedule their own curbside pickups.</span>
113
                                </li>
114
                            </ol>
115
                        </fieldset>
116
117
                        <fieldset class="rows" style="float: none;">
118
                            <legend>Curbside pickup hours</legend>
119
120
                            <em>Times should be in 24-hour format ( 00:00 to 23:59 ).</em>
121
122
                            <ol class="pickup_hours">
123
                                [% BLOCK pickup_hours_day %]
124
                                    [% SET day_start_hour = d _ '_start_hour' %]
125
                                    [% SET day_start_minute = d _ '_start_minute' %]
126
                                    [% SET day_end_hour = d _ '_end_hour' %]
127
                                    [% SET day_end_minute = d _ '_end_minute' %]
128
                                    [% SET p = policies.$branchcode %]
129
                                    [% IF p.$day_start_hour && p.$day_start_minute && p.$day_end_hour && p.$day_end_minute %]
130
                                        <li>
131
                                            <span class="slot">
132
                                                <input type="hidden" class="pickup-slot" name="pickup-slot-[% l.branchcode | html %]" value="[% d | html %]-[% p.$day_start_hour %]:[% p.$day_start_minute %]-[% p.$day_end_hour %]:[% p.$day_end_minute %]" />
133
                                            </span>
134
                                        </li>
135
                                    [% END %]
136
                                [% END %]
137
                                [% PROCESS pickup_hours_day d => 'sunday' %]
138
                                [% PROCESS pickup_hours_day d => 'monday' %]
139
                                [% PROCESS pickup_hours_day d => 'tuesday' %]
140
                                [% PROCESS pickup_hours_day d => 'wednesday' %]
141
                                [% PROCESS pickup_hours_day d => 'thursday' %]
142
                                [% PROCESS pickup_hours_day d => 'friday' %]
143
                                [% PROCESS pickup_hours_day d => 'saturday' %]
144
                            </ol>
145
146
                            <ol>
147
                                <li>
148
                                    <label>New slot:</label>
149
                                    <div>
150
                                        <select id="day-[% l.branchcode | html %]">
151
                                            <option value="0">Sunday</option>
152
                                            <option value="1">Monday</option>
153
                                            <option value="2">Tuesday</option>
154
                                            <option value="3">Wednesday</option>
155
                                            <option value="4">Thursday</option>
156
                                            <option value="5">Friday</option>
157
                                            <option value="6">Saturday</option>
158
                                            <span>
159
                                                From <input type="text" size="5" class="noEnterSubmit" id="new-start-[% l.branchcode | html %]" placeholder="00:00" />
160
                                                to <input type="text" size="5" class="noEnterSubmit" id="new-end-[% l.branchcode | html %]" placeholder="23:55" />
161
                                            </span>
162
                                        </select>
163
                                        <input type="button" class="add-new-slot" data-branchcode="[% l.branchcode | html %]" value="Add" />
164
                                    </div>
165
                                </li>
166
167
                            </ol>
168
                        </fieldset>
169
                    </div>
170
                [% END %]
171
            </div>
172
        </div>
173
174
        <input type="hidden" name="op" value="save" />
175
176
        <fieldset class="action">
177
            <input type="submit" value="Save configuration" />
178
        </fieldset>
179
    </form>
180
[% END %]
181
182
            </main>
183
        </div> <!-- /.col-sm-10.col-sm-push-2 -->
184
185
        <div class="col-sm-2 col-sm-pull-10">
186
            <aside>
187
                [% INCLUDE 'admin-menu.inc' %]
188
            </aside>
189
        </div> <!-- /.col-sm-2.col-sm-pull-10 -->
190
     </div> <!-- /.row -->
191
192
[% MACRO jsinclude BLOCK %]
193
    [% Asset.js("js/admin-menu.js") | $raw %]
194
    <script>
195
196
        let opening_slots = {};
197
        let slots;
198
        [% FOR l IN libraries %]
199
            [% SET branchcode = l.branchcode %]
200
            slots = [];
201
            [% FOR p IN policies.$branchcode.opening_slots %]
202
                slots.push('%s-%s-%s'.format("[% p.day | html %]", format_hhmm("[% p.start_hour | html %]:[% p.start_minute | html %]"), format_hhmm("[% p.end_hour | html %]:[% p.end_minute | html %]")));
203
            [% END %]
204
            opening_slots["[% l.branchcode | html %]"] = slots;
205
        [% END %]
206
207
        function format_hhmm(hhmm){
208
            let hh, mm;
209
            [ hh, mm ] = hhmm.split(':');
210
            return String(hh).padStart(2, '0') + ':' + String(mm).padStart(2, '0');
211
        }
212
        function format_slot(slot){
213
            let start = slot[0];
214
            let end = slot[1];
215
            return format_hhmm(start) + _(" to ") + format_hhmm(end);
216
        }
217
        function refresh_pickup_hours(branchcode) {
218
            let slots = {};
219
            $("#conf-"+branchcode).find("input.pickup-slot").each(function(){
220
                let splitted  = $(this).val().split("-");
221
                let day = splitted[0];
222
                let start = splitted[1];
223
                let end = splitted[2];
224
225
                if(!slots[day]) slots[day] = [];
226
227
                slots[day].push([start, end]);
228
            });
229
230
            let new_slot_node = $("<span class='slot'><input type='hidden' class='pickup-slot' name='pickup-slot-"+branchcode+"' value='"+day+"-"+start+"-"+end+"'/></span>");
231
            $("#conf-"+branchcode).find(".pickup_hours").append(new_slot_node);
232
233
            $("#conf-"+branchcode).find(".pickup_hours li").remove();
234
235
            Object.keys(slots).forEach(function(day){
236
                let li_node = $('<li><label>'+get_day_lib(day)+'<label></li>');
237
                slots[day].forEach(function(slot) {
238
                    li_node.append('<span>'+format_slot(slot)+'</span>');
239
                });
240
                li_node.appendTo($("#conf-"+branchcode).find(".pickup_hours"));
241
            });
242
        }
243
        function get_day_lib(day){
244
            let lib;
245
            switch(day){
246
            case 'sunday':
247
                lib = _("Sunday");
248
                break;
249
            case 'monday':
250
                lib = _("Monday");
251
                break;
252
            case 'tuesday':
253
                lib = _("Tuesday");
254
                break;
255
            case 'wednesday':
256
                lib = _("Wednesday");
257
                break;
258
            case 'thursday':
259
                lib = _("Thursday");
260
                break;
261
            case 'friday':
262
                lib = _("Friday");
263
                break;
264
            case 'saturday':
265
                lib = _("Saturday");
266
                break;
267
            }
268
            return lib;
269
        }
270
271
        let opening_slots = {};
272
        [% FOR l IN libraries %]
273
            opening_slots["[% l.branchcode | html %]"] = [];
274
        [% END %]
275
        $(document).ready(function(){
276
            [% FOR l IN libraries %]
277
                refresh_pickup_hours("[% l.branchcode | html %]");
278
            [% END %]
279
280
            $(".add-new-slot").on("click", function(){
281
                let branchcode = $(this).data('branchcode');
282
                let day = $("#day-" + branchcode).val();
283
                let start = $("#new-start-" + branchcode).val();
284
                let end = $("#new-end-" + branchcode).val();
285
                // FIXME confirm selection
286
287
                let start_hour, start_minute, end_hour, end_minute;
288
                [ start_hour, start_minute ] = start.split(":");
289
                [ end_hour, end_minute ] = end.split(":");
290
                if ( start_hour === undefined
291
                  || start_minute === undefined
292
                  || end_hour === undefined
293
                  || end_minute === undefined
294
                  || isNaN(parseInt(start_hour))
295
                  || isNaN(parseInt(end_hour))
296
                  || isNaN(parseInt(start_minute))
297
                  || isNaN(parseInt(end_minute))
298
                  || start_hour > 23 || start_minute > 59
299
                  || end_hour > 23 || end_minute > 59
300
                  ) {
301
                    $("#invalid_slot_warning").show();
302
                    return;
303
                } else {
304
                    $("#invalid_slot_warning").hide();
305
                }
306
307
                let new_slot = day + '-' + start + '-' + end;
308
                if ( opening_slots[branchcode].indexOf(new_slot) < 0 )
309
                    opening_slots[branchcode].push(new_slot);
310
311
                refresh_pickup_hours(branchcode);
312
            })
313
        });
314
    </script>
315
[% END %]
316
[% INCLUDE 'intranet-bottom.inc' %]

Return to bug 30650