LP1895737: Add Curbside Appointments to Bootstrap OPAC
authorJason Boyer <JBoyer@EquinoxInitiative.org>
Wed, 10 Mar 2021 23:48:12 +0000 (18:48 -0500)
committerGalen Charlton <gmc@equinoxOLI.org>
Tue, 13 Jul 2021 14:16:44 +0000 (10:16 -0400)
Add support for requesting / editing / canceling curbside
appointments to the bootstrap opac.

Signed-off-by: Jason Boyer <JBoyer@EquinoxInitiative.org>
Signed-off-by: Terran McCanna <tmccanna@georgialibraries.org>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>

Open-ILS/src/templates-bootstrap/opac/css/style.css.tt2
Open-ILS/src/templates-bootstrap/opac/myopac/holds_curbside.tt2 [new file with mode: 0755]
Open-ILS/src/templates-bootstrap/opac/parts/myopac/base.tt2

index 3bf6f2b..0317abd 100755 (executable)
@@ -135,6 +135,12 @@ only screen and (max-width: 650px)  {
     .holdHistoryTable td:nth-of-type(4):before { content: "Pickup Location"; display: flex; }
     .holdHistoryTable td:nth-of-type(5):before { content: "Status"; display: flex; }
 
+    .curbsideApptTable td:nth-of-type(1):before { content: "Pickup Location"; display: flex; }
+    .curbsideApptTable td:nth-of-type(2):before { content: "Date"; display: flex; }
+    .curbsideApptTable td:nth-of-type(3):before { content: "Time"; display: flex;}
+    .curbsideApptTable td:nth-of-type(4):before { content: "Arrival Notes"; display: flex;}
+    .curbsideApptTable td:nth-of-type(5):before { content: "Action"; display: flex;}
+
     .paymentTable td:nth-of-type(1):before { content: "Payment Date"; display: flex; }
     .paymentTable td:nth-of-type(2):before { content: "Payment For"; display: flex; }
     .paymentTable td:nth-of-type(3):before { content: "Amount"; display: flex; }
diff --git a/Open-ILS/src/templates-bootstrap/opac/myopac/holds_curbside.tt2 b/Open-ILS/src/templates-bootstrap/opac/myopac/holds_curbside.tt2
new file mode 100755 (executable)
index 0000000..e464b6c
--- /dev/null
@@ -0,0 +1,191 @@
+[%  PROCESS "opac/parts/header.tt2";
+    PROCESS "opac/parts/misc_util.tt2";
+    PROCESS "opac/parts/hold_status.tt2";
+    PROCESS "opac/parts/hold_notify.tt2";
+    PROCESS "opac/parts/myopac/column_sort_support.tt2";
+    WRAPPER "opac/parts/myopac/base.tt2";
+
+    myopac_page = "holds_curbside";
+    parent="holds";
+    limit = (ctx.holds_limit.defined) ? ctx.holds_limit : 0;
+    offset = (ctx.holds_offset.defined) ? ctx.holds_offset : 0;
+    count = (ctx.holds_ids.size.defined) ? ctx.holds_ids.size : 0;
+%]
+<h3> [% l("Curbside Pickup Appointments") %]</h3>
+<div>
+    <div id='holds_main'>
+          <div class="row">
+            <div class="col-8">
+             [% IF offset != 0 %]<a href='[% mkurl('holds', {limit => limit,offset => (offset - limit)}) %]'
+              ><span class="np_nav_link classic_link btn btn-action">&#9668;[% l('Previous') %]</span></a> [% END %]
+
+             [% IF offset > 0 || count > limit; curpage = 0; WHILE curpage * limit < count; IF curpage * limit == offset; %]
+                  <span class="np_nav_link classic_link btn btn-action disabled">[% curpage + 1 %]</span>
+
+                    [%- ELSE %]
+                <a href='[% mkurl('holds', {limit => limit, offset => (curpage * limit)}) %]' class="np_nav_link classic_link btn btn-action">[% curpage + 1 %]</a>
+                    [%- END; curpage = curpage + 1; END; END %]
+                <span style="padding-left:5px;" class='error'>
+                    [%  IF ctx.hold_suspend_post_capture; l('One or more holds could not be suspended because the item is at (or en route to) the pickup library.'); END; %]
+                </span>
+
+                [% IF count > limit + offset %]<a href='[% mkurl('holds', {limit => limit, offset => (offset + limit)}) %]'
+                  ><span class="np_nav_link classic_link btn btn-action">[% l('Next') %]&#9658;</span></a>[% END %]
+            </div>
+          </div>
+
+        [% IF ctx.holds.size && ctx.holds.size < 1 %]
+        <div class="warning_box">[% l('No holds found.') %]</div>
+        [% ELSE %]
+    <div class="w-100">
+    [% FOR lib IN ctx.curbside_pickup_libs;
+
+        cs_slot = '';
+        cs_slot_id = '';
+        cs_date = '';
+        cs_time = '';
+        cs_notes = '';
+
+       cs_org = ctx.cs_org;
+        IF cs_org == lib;
+            cs_slot = ctx.cs_slot;
+            cs_slot_id = ctx.cs_slot_id;
+            cs_date = ctx.cs_date;
+            cs_time = ctx.cs_time;
+            cs_notes = ctx.cs_notes;
+        END;
+
+        appointment = ctx.curbside_appointments.$lib;
+        IF appointment;
+            cs_slot = appointment;
+            cs_slot_id = appointment.id;
+            IF appointment.slot; cs_date = date.format(ctx.parse_datetime(appointment.slot),'%F'); END;
+            IF appointment.slot; cs_time = date.format(ctx.parse_datetime(appointment.slot),'%T'); END;
+            cs_notes = appointment.notes;
+        END %]
+        <form class="egtr" method="POST">
+            <input type="hidden" name="action" value="curbside"/>
+            <input type="hidden" name="cs_slot_id" value="[% appointment.id %]"/>
+            <table class="table table-bordered miniTable curbsideApptTable w-100 my-3">
+                <thead>
+                    <th>[% l('Pickup Location') %]</th>
+                    <th>[% l('Date') %]</th>
+                    <th>[% l('Time') %]</th>
+                    <th>[% l('Arrival Notes') %]
+                        <a href="#" title="" data-toggle="tooltip" data-original-title="Vehicle Description, etc.">
+                            <i class="fas fa-question-circle" aria-hidden="true"></i>
+                        </a>
+                    </th>
+                    <th>[% l('Action') %]</th>
+                </thead>
+                <tbody><tr>
+            [% disable_me = 0 %]
+            [% no_patron_input = 0 %]
+            [% date_started_null = 0 %]
+            [% IF appointment && appointment.arrival; disable_me = 1; END %]
+            [% IF ctx.get_org_setting(lib, 'circ.curbside.disable_patron_input'); no_patron_input = 1; END %]
+            <td>
+                [% ctx.get_aou(lib).name | html %]<br/>
+                [% l('Phone:')%] [% ctx.get_aou(lib).phone | html %]
+                <input type="hidden" name="cs_org" value="[% lib %]"/>
+            </td>
+            <td>
+                [% IF !cs_date || ( date.format(cs_date _ ' 12:00:00', '%Y%m%d') < date.format(date.now, '%Y%m%d') ); cs_date = ''; tmp_cs_date = date.format(date.now, '%F'); date_started_null = 1; %]
+                [% ELSE; tmp_cs_date = cs_date; END %]
+                [% IF cs_date %]<input type="hidden" name="cs_date" value="[% cs_date | html %]"/>[% END %]
+                [% IF no_patron_input && date_started_null; %]&nbsp;
+                [% ELSE; %]<input type="date" name="cs_date" min="[% date.format(date.now, '%F') %]" value="[% tmp_cs_date | html %]" [% IF cs_date || no_patron_input %]disabled="disabled"[% END %]/>[% END %]
+            </td>
+            <td>
+                [% IF appointment || cs_date; # checking times %]
+                  [% current_date = cs_date %]
+                  [% IF date_started_null && no_patron_input %]&nbsp;
+                  [% ELSIF appointment || ctx.cs_times.$lib.$current_date.size; # show a select %]
+                    <select name="cs_time" [% IF disable_me || no_patron_input %]disabled="disabled"[% END %]>
+                      [% found_time = 0 %]
+                      [% FOR t IN ctx.cs_times.$lib.$current_date %]
+                        <option value="[% t.0 | html %]"
+                          [% IF cs_time == t.0; found_time=1 %] selected="selected"[% END %]
+                          [% IF t.1 <= 0 && cs_time != t.0 %] disabled="disabled"[% END %]>
+                            [% date.format(current_date _ ' ' _ t.0,'%l:%M %p') | html %]
+                        </option>
+                      [% END %]
+                      [% IF cs_time && !found_time %]
+                        <option value="[% cs_time | html %]" selected="selected">
+                          [% date.format(current_date _ ' ' _ cs_time,'%l:%M %p') | html %]
+                        </option>
+                      [% END %]
+                    </select>
+                  [% ELSE %] 
+                    [% l('No times available for selected date') %]
+                  [% END %]
+                [% ELSE %] 
+                  [% IF !no_patron_input; %][% l('Select a date') %][% END %]
+                [% END %]
+            </td>
+            <td>
+                [% IF cs_date; # show the notes box %]
+                <input type="text" name="cs_notes" value="[% cs_notes | html %]" [% IF no_patron_input || disable_me %]disabled="disabled"[% END %]/>
+                [% ELSE %] &nbsp;
+                [% END %]
+            </td>
+            <td>[%
+                disable_arrival_button = 1; # assume arrival is not yet allowed
+                IF appointment && appointment.slot;
+                    stime = date.format(ctx.parse_datetime(appointment.slot), '%s');
+                    now_time = date.format(date.now, '%s');
+                    IF now_time >= stime;
+                        disable_arrival_button = 0; # if 'now' is after the slot time, allow arrival
+                    END;
+                END %]
+                [% IF no_patron_input %]
+                    [% l('Please contact the library to schedule, change, or cancel your appointment.') %]
+                [% ELSIF appointment.staged && !appointment.arrival; # relevant submit action %]
+                    <button type="submit" name="cs_action" value="arrive" class="btn btn-sm btn-action" style="margin: .25em;" [% IF disable_arrival_button %]disabled="disabled"[% END %]>
+                       <i class="fas fa-bell" aria-hidden="true"></i> [% l('Alert staff of your arrival') %]
+                    </button><br/>
+                    <button type="submit" name="cs_action" value="cancel" class="btn btn-sm btn-danger" style="margin: .25em;">
+                       <i class="fas fa-ban" aria-hidden="true"></i> [% l('Cancel') %]
+                    </button>
+                [% ELSIF appointment.arrival %]
+                    [% l('Staff have been notified of your arrival.') %]<br/>
+                    <!--
+                    <button type="submit" name="cs_action" value="deliver" class="btn btn-" style="margin: .25em;">
+                        [% l('Confirm delivery of items') %]
+                    </button><br/>
+                    -->
+                    <button type="submit" name="cs_action" value="cancel" class="btn btn-sm btn-danger" style="margin: .25em;">
+                       <i class="fas fa-ban" aria-hidden="true"></i> [% l('Cancel') %]
+                    </button>
+                [% ELSIF appointment.slot %]
+                    <button type="submit" name="cs_action" value="arrive" class="btn btn-sm btn-action" style="margin: .25em;" [% IF disable_arrival_button %]disabled="disabled"[% END %]>
+                       <i class="fas fa-bell" aria-hidden="true"></i> [% l('Alert staff of your arrival') %]
+                    </button><br/>
+                    <button type="submit" name="cs_action" value="save" class="btn btn-sm btn-action" style="margin: .25em;">
+                       <i class="fas fa-edit" aria-hidden="true"></i> [% l('Update') %]
+                    </button><br/>
+                    <button type="submit" name="cs_action" value="cancel" class="btn btn-sm btn-danger" style="margin: .25em;">
+                       <i class="fas fa-ban" aria-hidden="true"></i> [% l('Cancel') %]
+                    </button>
+                [% ELSIF cs_date %]
+                    <button type="submit" name="cs_action" value="save" class="btn btn-sm btn-action" style="margin: .25em;">
+                       <i class="fas fa-check" aria-hidden="true"></i> [% l('Request appointment') %]
+                    </button>
+                    <button type="submit" name="cs_action" value="reset" class="btn btn-sm btn-opac" style="margin: .25em;">
+                       <i class="fas fa-calendar" aria-hidden="true"></i> [% l('Select another date') %]
+                    </button><br/>
+                [% ELSE %]
+                    <button type="submit" name="cs_action" value="next" class="btn btn-sm btn-opac" style="margin: .25em;">
+                       <i class="fas fa-clock" aria-hidden="true"></i> [% l('Check times') %]
+                    </button>
+                [% END %]
+            </td>
+        </tr></tbody></table>
+        </form>
+    [% END %]
+    </div>
+
+        [% END %]
+    </div>
+</div>
+[% END %]
index 14e425a..646440d 100755 (executable)
@@ -14,7 +14,7 @@
         {children => 0, parent => "circs", url => "ebook_circs", text => l("<i class='fas fa-desktop' aria-hidden='true'></i> E-Items Currently Checked Out"), name => l("E-Items Currently Checked Out")},
         {children => 0, parent => "circs", url => "circ_history", text => l("<i class='fas fa-history' aria-hidden='true'></i> Check Out History"), name => l("Check Out History")},
 
-        {children => 4, parent => "parent",url => "holds", text => l("<i class='fas fa-clock' aria-hidden='true'></i> <span class='sumNum'>[_1]</span>Holds / <span class='sumNum'>[_2]</span> Ready", ctx.user_stats.holds.total, ctx.user_stats.holds.ready), name => l("Holds")},
+        {children => 5, parent => "parent",url => "holds", text => l("<i class='fas fa-clock' aria-hidden='true'></i> <span class='sumNum'>[_1]</span>Holds / <span class='sumNum'>[_2]</span> Ready", ctx.user_stats.holds.total, ctx.user_stats.holds.ready), name => l("Holds")},
         {children => 0, parent => "holds", url => "holds", text => l("<i class='fas fa-hands' aria-hidden='true'></i> Items on Hold"), name => l("Holds")},
         {children => 0, parent => "holds", url => "ebook_holds", text => l("<i class='fas fa-desktop' aria-hidden='true'></i> E-Items on Hold"), name => l("E-Items on Hold")},
         {children => 0, parent => "holds", url => "ebook_holds_ready", text => l("<i class='fas fa-download' aria-hidden='true'></i>  E-Items Ready for Checkout"), name => l("E-Items Ready for Checkout")},
 
         {children => 0, parent => "parent", url => "lists", text => l("<i class='fas fa-list' aria-hidden='true'></i> My Lists"), name => l("My Lists")}
     ];
-     IF (ctx.show_reservations_tab == 'true');
+    IF (ctx.curbside_pickup_libs.size > 0 );
+        myopac_pages.push({children => 0, parent => "parent", url => "holds_curbside", text => l("<i class='fas fa-road' aria-hidden='true'></i> Curbside Pickup"), name => l("Curbside Pickup")});
+        myopac_pages.push({children => 0, parent => "holds", url => "holds_curbside", text => l("<i class='fas fa-road' aria-hidden='true'></i> Curbside Pickup"), name => l("Curbside Pickup")});
+    END;
+    IF (ctx.show_reservations_tab == 'true');
         myopac_pages.push({children => 0, parent => "parent", url => "reservations", text => l("<i class='fas fa-splotch' aria-hidden='true'></i>  Reservations"), name => l("Reservations")});
     END;
+
     IF ctx.my_hold_subscriptions.size > 0;
         myopac_pages.push({children => 0, parent => "holds", url => "hold_subscriptions", text => l("<i class='fas fa-redo' aria-hidden='true'></i> Hold Groups"), name => l("Hold Groups")});
     END;