Refine the rendering of myopac account summary table and dashboard
[sitka/overdrive-evergreen-opac.git] / src / od_pages_myopac.coffee
CommitLineData
8ebc3fff
SC
1# Define custom jQuery extensions to rewrite content of existing pages
2# None of the extensions directly use the API, but they depend on od_action which does.
3
4define [
5 'jquery'
6 'lodash'
8ebc3fff
SC
7 'jquery-ui'
8 'od_action'
9 'od_pages_opac'
508513a1 10], ($, _) ->
8ebc3fff
SC
11
12 $.fn.extend
13
14 # Given a map between classnames and numeric values,
15 # eg, { class1: 1, class2: -1 },
16 # increment the existing values of the containers with the classnames.
17 _counters: (x) ->
18 for n, v of x
19 $x = @find ".#{n}"
20 $x.text +($x.text()) + v
21 return @
22
23 _dashboard: (x) ->
24
25 if arguments.length is 0
c34f0459 26 @append $('<div id="dashboard">')
8ebc3fff 27
c34f0459 28 else
8ebc3fff
SC
29 # Add a new dashboard for to show counts of e-items; start with
30 # zero counts
31 base = '/eg/opac/myopac'
c34f0459
SC
32 @find 'div'
33 .eq 2
34 .append """
35 <span class="dash-align">
36 <a class="dash-link" href="#{base}/circs?e_items"><span class="ncheckouts" id="dash_checked">0</span> E-items Checked Out</a>
37 </span>
38 <span class="dash_divider">|</span>
39 <span class="dash-align">
40 <a class="dash-link" href="#{base}/holds?e_items"><span class="nholds" id="dash_holds">0</span> E-items on Hold</a>
41 </span>
42 <span class="dash_divider">|</span>
43 <span class="dash-align">
44 <a class="dash-link" href="#{base}/holds?e_items&available=1"><span class="nholdsready" id="dash_pickup">0</span> E-items Ready for Checkout</a>
45 </span>
46 """
47 .end()
48 .end()
8ebc3fff
SC
49
50 # The following sequence is necessary to align the new dashboard
51 # with the existing ones, but do not know why it needs to be done
52 @find 'div'
53 .css float: 'none'
54 .end()
55
8ebc3fff 56 @_counters x # Change the values of the counters
c34f0459 57
8ebc3fff
SC
58 return @
59
60 # Replace account summary area with one that shows links to go to
c34f0459 61 # physical and e-items lists
8ebc3fff
SC
62 _account_summary: (x) ->
63
64 if arguments.length is 0
65 # Parse a list of totals of physical items from the account summary table
66 totals = ( +(v.textContent.match(/\d+?/)[0]) for v in @find('td').not '[align="right"]' )
67
c34f0459 68 tpl = _.template """
8ebc3fff
SC
69 <tbody>
70 <tr>
71 <td>
72 <a href="/eg/opac/myopac/circs">
c34f0459 73 <span><span class="ncheckouts" /> <%= ncheckouts %> Items Currently Checked out</span>
8ebc3fff
SC
74 </a>
75 </td>
76 <td align="right">
77 <a href="/eg/opac/myopac/circs?e_items"><span class="n_checkouts" /> E-items Currently Checked out</a>
78 </td>
79 </tr>
80 <tr>
81 <td>
c34f0459 82 <a href="/eg/opac/myopac/holds"><span class="nholds" /> <%= nholds %> Items Currently on Hold</a>
8ebc3fff
SC
83 </td>
84 <td align="right">
85 <a href="/eg/opac/myopac/holds?e_items"><span class="n_holds" /> E-items Currently on Hold</a>
86 </td>
87 </tr>
88 <tr>
89 <td>
c34f0459 90 <a href="/eg/opac/myopac/holds?available=1"><span class="nready" /> <%= nready %> Items ready for pickup</a>
8ebc3fff
SC
91 </td>
92 <td align="right">
93 <a href="/eg/opac/myopac/holds?e_items&available=1"><span class="n_ready" /> E-items ready for pickup</a>
94 </td>
95 </tr>
96 </tbody>
97 """
c34f0459
SC
98
99 # Build a new table consisting of two columns. The first
100 # column is for physical items with the existing totals. The
101 # second column is for e-items and is initially hidden until
102 # its total values are available.
103 @empty()
104 .append tpl
105 ncheckouts: totals[0]
106 nholds: totals[1]
107 nready: totals[2]
108 .find 'td'
109 .filter '[align="right"]'
110 .find 'a'
111 .hide()
112 .end()
113 .end()
8ebc3fff
SC
114
115 else
c34f0459
SC
116 # Change the values of the counters and reveal the e-items column
117 @_counters x
118 .find 'td'
119 .filter '[align="right"]'
120 .find 'a'
121 .show()
122 .end()
123 .end()
8ebc3fff
SC
124
125 # Relabel a history tab
126 _tab_history: ->
127 $x = $('a', @)
128 $x.text "#{ $x.text() } (Physical Items)"
129 return @
130
131 # Add a new tab for e-items and select a tab relevant for the current page name.
132 # If page name contains 'history' then select any tabs with 'history' in its ID
133 # otherwise, if search parameters has 'e_items' property then select any tabs with 'eitems' in its ID
134 #$('#acct_holds_tabs, #acct_checked_tabs')._etabs()
135 _etabs: (page_name, e_items) ->
136
137 # Tab replacement is identified by container's id
138 new_tabs =
139 acct_holds_tabs: """
140 <div id="acct_holds_tabs">
141 <div class="align" id='tab_holds'>
142 <a href="holds#">Items on Hold</a>
143 </div>
144 <div class="align" id='tab_holds_eitems'>
145 <a href="holds?e_items">E-items on Hold</a>
146 </div>
147 <div class="align" id='tab_holds_history'>
148 <a href="hold_history">Holds History</a>
149 </div>
150 </div>
151 """
152 acct_checked_tabs: """
153 <div id="acct_checked_tabs">
154 <div class="align" id='tab_circs'>
155 <a href="circs#">Current Items Checked Out</a>
156 </div>
157 <div class="align" id='tab_circs_eitems'>
158 <a href="circs?e_items">E-items Checked Out</a>
159 </div>
160 <div class="align" id='tab_circs_history'>
161 <a href="circ_history">Check Out History</a>
162 </div>
163 </div>
164 """
165 @replaceWith new_tabs[@prop 'id']
166
167 # Compute the selected tab of the current page name
168 $selected =
169 # if page name ends with '_history', select the tab with id
170 # that ends with '_history'
171 if /_history$/.test page_name
172 $('[id$=_history]')
173 # else if search parameters has 'e_items' property, select the
174 # tab with id that ends with '_eitems'
175 else if e_items
176 $('[id$=_eitems]')
177 # else select the remaining tab
178 else
179 $('[id^=tab_]').not '[id$=_history],[id$=_eitems]'
180
181 $selected.addClass 'selected'
182
183 return @
184
185
186 # Resize columns of a table, either to fixed widths, or to be equal
187 # widths, ie, 100% divided by number of columns.
188 # Also, force width of table to 100%; don't know why this is necessary.
189 _resizeCols: ->
190
191 $table = @find 'table'
192 .css 'width', '100%'
193
194 # Resize to percentage widths given in the argument list
195 if arguments.length > 0
196 $th = $table.find 'th'
197 $td = $table.find 'td'
198 for width, n in arguments
199 $th.eq(n).css 'width', width
200 $td.eq(n).css 'width', width
201
202 # Otherwise, resize to equal widths
203 else
204 ncols = @find('th').length or 1
205 width = "#{100 / ncols}%"
206
207 $table
208 .find 'th'
209 .css 'width', width
210 .end()
211 .find 'td'
212 .css 'width', width
213 .end()
214
215 return @
216
217 # Show a container having a class name from a list of candidate, and hide the rest
218 _show_from: (which, candidates...) ->
219
220 @find(x).hide() for x in candidates
221 @find candidates[which]
222 .show()
223 .end()
224
225 # Replace a title of table with new text
226 _replace_title: (x) ->
227
228 @find '.header_middle span'
229 .eq 0
230 .text x
231 .end()
232
233 # Build an empty table for showing a list of holds
234 _holds_main: ->
235
236 table = """
237 <table cellpadding="0" cellspacing="0" border="0">
238 <thead id="acct_holds_main_header"><tr>
239 <th></th>
240 <th>Title/Author</th>
241 <th>Availability</th>
242 <th>Formats</th>
243 <th>Actions</th>
244 </tr></thead>
245 <tbody id="holds_temp_parent"></tbody>
246 </table>
247 <div class="warning_box">No holds found.</div>
248 """
249 @empty().append table
250 ._resizeCols '15%', '20%', '30%', '20%', '15%'
251
252 # Build <tr> elements for showing a list of holds
253 _holds_rows: (holds) ->
254 return [] unless holds
255
256 tpl = _.template """
257 <tr id="<%= id %>" name="acct_holds_temp" class="acct_holds_temp inactive-hold">
258 <td class="thumbnail"></td>
259 <td>
260 <div class="title" /> by <div class="author" />
261 </td>
262 <td class="availability"></td>
263 <td class="formats"></td>
264 <td class="actions"></td>
265 </tr>
266 """
267
268 ids = []
269 $rows = for hold in holds
270
271 ids.push hold.reserveId
272
273 # Build an empty row element that is uniquely identified by a
274 # product ID
275 $row = $ tpl id: hold.reserveId
276
277 # Fill the row with hold values and proxy the rest of the row
278 # with progress bars
279 $row
280 ._holds_row hold # hold values
281 ._row_meta() # progress bar
282 ._holds_row_avail() # progress bar
283
284 # Add hold rows to <tbody> and remove the warning box.
285 if $rows.length > 0
286 @find 'tbody'
287 .empty().append $rows
288 .end()
289 .find '.warning_box'
290 .remove()
291 .end()
292
293 return ids
294
295 _holds_row: (hold) ->
296
297 @find 'td.availability'
298 ._holds_row_avail1 hold
299 .end()
300 .find 'td.actions'
e2ef6d2a 301 ._actions hold.actions, hold.reserveId
8ebc3fff
SC
302 .end()
303
304 # Show a title, author, or format by using the given metadata object
305 _row_meta: (meta, classnames...) ->
306
307 status = if arguments.length is 0 then value: false else 'destroy'
e07737bf 308 try @find(".#{n}").progressbar(status) for n in ['title', 'author', 'formats']
8ebc3fff
SC
309
310 return @ unless meta
311
312 $title = $ """
313 <a href="/eg/opac/results?query=#{meta.title};locg=10;qtype=title">#{meta.title}</a>
314 """
315 $thumbnail = $ """
508513a1 316 <img src="#{meta.images?.thumbnail?.href}" alt="#{meta.title}" />
8ebc3fff
SC
317 """
318 $author = $ """
319 <a href="/eg/opac/results?query=#{meta.author};locg=10;qtype=author">#{meta.author}</a>
320 """
321 for n in classnames
322 $n = @find ".#{n}"
323 switch n
324 when 'thumbnail' then $n.empty().append $thumbnail
325 when 'title' then $n.empty().append $title
326 when 'author' then $n.empty().append $author
e07737bf 327 when 'formats' then $n._show_formats meta?.formats
8ebc3fff
SC
328 return @
329
330 _holds_row_avail1: (hold) ->
331
332 hold_status = if hold.holdSuspension then 0 else if hold.actions.checkout then 1 else 2
333
334 x = if hold.holdSuspension?.suspensionType is 'limited' then 'show' else 'hide'
335
336 tpl = _.template """
337 <div class="suspended">
338 <div style="color: red">Suspended <span class="limited">until <%= activates %></span></div>
339 <ul style="padding-left: 20px">
340 <li name="acct_holds_status"><%= position %> / <%= nHolds %> holds <span class="copies" /></li>
341 <li>Email notification will be sent to <%= email %></li>
342 <li>Hold was placed <%= placed %></li>
343 </ul>
344 </div>
345 <div class="unavailable">
346 <div>Waiting for copy</div>
347 <ul style="padding-left: 20px">
348 <li name="acct_holds_status"><%= position %> / <%= nHolds %> holds <span class="copies" /></li>
349 <li>Email notification will be sent to <%= email %></li>
350 <li>Hold was placed <%= placed %></li>
351 </ul>
352 </div>
353 <div class="available">
354 <div style="color: green">Ready for checkout</div>
355 <ul style="padding-left: 20px">
356 <li name="acct_holds_status"><%= position %> / <%= nHolds %> holds <span class="copies" /></li>
357 <li>Hold will expire <%= expires %></li>
358 </ul>
359 </div>
2e685799 360 <a href="http://downloads.bclibrary.ca/ContentDetails.htm?ID=<%= id %>">Link to Overdrive Account to change preferences</a>
8ebc3fff
SC
361 """
362 @empty().append tpl
363 position: hold.holdListPosition
364 nHolds: hold.numberOfHolds
365 email: hold.emailAddress
366 expires: hold.holdExpires.fromNow()
367 placed: hold.holdPlacedDate.fromNow()
368 activates: hold.holdSuspension?.numberOfDays.calendar()
6a1128e6 369 id: hold.reserveId
8ebc3fff
SC
370
371 # Illuminate areas of this row according to the hold status
372 ._show_from hold_status, '.suspended', '.available', '.unavailable'
373 # Show the hold suspension date only if suspension type is limited
374 .find('.limited')[x]()
375 .end()
376
377 # Complete building a <tr> element for showing a hold by using the
378 # given availability object
379 _holds_row_avail: (avail) ->
380
381 status = if arguments.length is 0 then value: false else 'destroy'
382 @find '.copies'
383 .progressbar status
384 .end()
385
386 return @ unless avail
387
388 text = """
389 on #{avail.copiesOwned} copies
390 """
391 @find '.copies'
392 .text text
393 .end()
394
395 # Build an empty table for showing a list of checkouts
396 _checkouts_main: ->
397
398 table = """
399 <table cellpadding="0" cellspacing="0" border="0">
400 <thead id="acct_checked_main_header"><tr>
401 <th></th>
402 <th>Title/Author</th>
403 <th>Availability</th>
404 <th>Formats</th>
405 <th>Actions</th>
406 </tr></thead>
407 <tbody id="holds_temp_parent"></tbody>
408 </table>
409 <div class="warning_box">No checkouts found.</div>
410 """
411 @empty().append table
412 ._resizeCols()
413
414 # Build <tr> elements for showing a list of checkouts
415 _checkouts_rows: (circs) ->
416 return [] unless circs
417
418 tpl = _.template """
419 <tr id="<%= id %>" name="acct_checked_temp" class="acct_checked_temp inactive-hold">
420 <td class="thumbnail"></td>
421 <td>
422 <div class="title" /> by <div class="author" />
423 </td>
424 <td class="availability"></td>
425 <td class="formats"></td>
426 <td class="actions"></td>
427 </tr>
428 """
429
430 ids = []
431 $rows = for circ in circs
432
433 ids.push circ.reserveId
434
435 # Build an empty row element that is uniquely identified by a
436 # product ID
437 $row = $ tpl id: circ.reserveId
438
439 # Fill the row with circ values and proxy the rest of the row
440 # with progress bars
441 $row
442 ._row_checkout circ # circ values
443 ._row_meta() # progress bars
444
445 # Add checkout rows to <tbody> and remove the warning box.
603dd210 446 if $rows.length > 0
8ebc3fff
SC
447 @find 'tbody'
448 .empty().append $rows
8ebc3fff
SC
449 .end()
450 .find '.warning_box'
451 .remove()
452 .end()
453
454 return ids
455
456 _row_checkout: (circ) ->
457
458 @find 'td.availability'
459 ._checkouts_row_avail circ
460 .end()
461 .find 'td.actions'
e2ef6d2a 462 ._actions circ.actions, circ.reserveId
8ebc3fff
SC
463 .end()
464 .find 'td.formats'
e2ef6d2a 465 ._formats circ.formats, circ.reserveId
8ebc3fff
SC
466 .end()
467
468 _checkouts_row_avail: (circ) ->
469
470 tpl = _.template """
471 <div>Expires <%= expires_relatively %></div>
472 <div><%= expires_exactly %></div>
6a1128e6 473 <a href="http://downloads.bclibrary.ca/ContentDetails.htm?ID=<%= id %>">Click to access online (library card required)</a>
8ebc3fff
SC
474 """
475 @empty().append tpl
476 expires_relatively: circ.expires.fromNow()
477 expires_exactly: circ.expires.format 'YYYY MMM D, h:mm:ss a'
6a1128e6 478 id: circ.reserveId
8ebc3fff
SC
479
480 # Build a <tr> element to show the available actions of an item.
481 # If the item is available, the check out action should be possible,
482 # and if unavailable, the place hold action should be possible.
483 _holdings_row: (id) ->
484
485 tpl = _.template """
486 <tr id="<%= id %>" name="acct_holds_temp" class="acct_holds_temp inactive-hold">
487 <td class="thumbnail"></td>
488 <td>
489 <div class="title" /> by <div class="author" />
490 </td>
491 <td class="availability"></td>
492 <td class="formats"><ul></ul></td>
493 <td class="actions"></td>
494 </tr>
495 """
496 $row = $(tpl id: id)
497 ._row_meta() # progress bar
498 ._holdings_row_avail() # progress bar
499
500 @find 'tbody'
501 .empty().append $row
502 .end()
503 .find '.warning_box'
504 .remove()
505 .end()
506
507 # Complete building a <tr> element for a holding using the given availability object
508 _holdings_row_avail: (avail) ->
509
510 # Create or destroy progress bars
511 status = if arguments.length is 0 then value: false else 'destroy'
512 @find 'td.availability'
513 .progressbar status
514 .end()
515 .find 'td.actions'
516 .progressbar status
517 .end()
518
519 return @ unless avail
520
521 tpl = _.template """
522 <div class="unavailable">No copies are available for checkout</div>
523 <div class="available" style="color: green">A copy is available for checkout</div>
524 <div><%= n_avail %> of <%= n_owned %> available, <%= n_holds %> holds</div>
525 """
526 @find 'td.availability'
527 .append tpl
528 n_owned: avail.copiesOwned
529 n_avail: avail.copiesAvailable
530 n_holds: avail.numberOfHolds
531 .end()
532
533 # Build action buttons
534 .find 'td.actions'
e2ef6d2a 535 ._actions avail.actions, avail.id
8ebc3fff
SC
536 .end()
537
538 # Illuminate areas of this row according to the holdings status
539 ._show_from (if avail.available then 0 else 1), '.available', '.unavailable'
540