formhandler.template.html 17.7 KB
Newer Older
S Anand's avatar
S Anand committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--
Each "var ..." template is embedded into formhandler.js as a minified HTML string variable.
This uses rollup-plugin-htmlparts.js: our custom rollup plugin.

Each template receives these variables:

- data: JSON data from FormHandler
- meta: Meta HTTP headers from FormHandler
- options: Options passed to $().formhandler() (including defaults)
- args: URL query parameters used to retrieve data
-->

<!-- This is the root template that renders all other components on this page -->
<!-- var template_ -->
15
16
<div class="position-relative">
  <div class="formhandler">
17
    <div class="note"></div>
18
19
    <div class="formhandler-table-header d-flex justify-content-between mb-2">
      <div class="d-flex flex-wrap">
20
21
22
23
24
        <div class="edit"></div>
        <div class="add"></div>
        <div class="count"></div>
        <div class="page"></div>
        <div class="size"></div>
25
26
27
28
      </div>
      <div class="d-flex">
        <div class="filters"></div>
        <div class="export"></div>
S Anand's avatar
S Anand committed
29
      </div>
30
    </div>
S Anand's avatar
S Anand committed
31
      <div class="<%- (options.table == 'grid') ? 'table_grid' : 'table' %>"></div>
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  </div>
  <div class="loader pos-cc d-none">
    <div class="fa fa-spinner fa-spin fa-3x fa-fw"></div>
    <span class="sr-only">Loading...</span>
  </div>
</div>
<div class="modal formhandler-table-modal" id="fh-modal-<%- idcount %>" tabindex="-1" role="dialog" aria-labelledby="fh-label-<%- idcount %>"
  aria-hidden="true">
  <div class="modal-dialog modal-sm" role="document">
    <div class="modal-content">
      <form class="formhandler-table-modal-form modal-body">
        <label id="fh-label-<%- idcount %>" for="formhandler-table-modal-value">Value</label>
        <p>
          <input class="form-control" name="filter_input">
        </p>
        <div>
          <button type="button" class="btn btn-sm btn-secondary mr-1" data-dismiss="modal">Cancel</button>
          <button type="submit" class="btn btn-sm btn-primary mr-1">Apply filter</button>
          <a class="btn btn-sm btn-danger remove-action urlfilter" data-dismiss="modal" data-target="#" href="#">Remove filter</button>
        </div>
      </form>
53
54
55
    </div><!-- .modal-content -->
  </div><!-- .modal-dialog -->
</div><!-- .modal -->
S Anand's avatar
S Anand committed
56
57
58
<!-- end -->

<!-- var template_table -->
59
60
61
62
<%
  var filtered_cols = args['_c'] && args['_c'].length != options.columns.length ?
                      options.columns.filter(function(col) { return args['_c'].indexOf('-' + col.name) < 0 }) :
                      options.columns
63
64
  var cols = options.columns.length ? filtered_cols : meta.columns;
  cols = cols.filter(function(col) { return col.hide !== true})
65
  var form_id = idcount
Bharat R's avatar
Bharat R committed
66
  var namespace = options.namespace ? options.namespace + ':' : ''
67
%>
68

S Anand's avatar
S Anand committed
69
70
<table class="table table-sm table-striped">
  <thead>
71
    <% _.each(cols, function(colinfo) {
72
73
74
        col_defaults(colinfo, data)
        var menu_item = false
        var col_id = idcount++
75
76
        var qsort = parse('?')
        var isSorted = _.includes(args['_sort'], colinfo.name) ? {op: '', cls: 'table-primary'} : _.includes(args['_sort'], '-' + colinfo.name) ? {op: '-', cls: 'table-danger'} : {}
77
      %>
78
      <th class="<%- isSorted.cls %>" data-col="<%- colinfo.name %>">
S Anand's avatar
S Anand committed
79
        <div class="dropdown">
80
          <a href="#" class="dropdown-toggle text-nowrap" id="fh-dd-<%- col_id %>" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
S Anand's avatar
S Anand committed
81
82
            <%- colinfo.title || colinfo.name %>
          </a>
83
84
          <div class="dropdown-menu" aria-labelledby="fh-dd-<%- col_id %>">
            <% _.each(colinfo.sort, function(title, op) { menu_item = true
85
86
87
88
              qsort = qsort.update({_sort: args['_sort'] || []})
              if (!_.isEmpty(isSorted))
                qsort = qsort.update({_sort: [colinfo.name, '-' + colinfo.name]}, 'del')
              var active = _.includes(args['_sort'], op + colinfo.name) %>
Bharat R's avatar
Bharat R committed
89
              <a class="dropdown-item urlfilter <%- active ? 'active': '' %>" href="<%- qsort.update({[namespace + '_sort']: [op + colinfo.name]}, active ? 'del': 'add').toString() %>">
90
91
                <%- title %>
              </a>
92
93
94
            <% }) %>
            <% if (menu_item) { %>
              <div class="dropdown-divider"></div>
S Anand's avatar
S Anand committed
95
            <% menu_item = false } %>
96
97
98
99
100
101
102
103
104
105
            <% _.each(colinfo.filters, function(title, op) { menu_item = true %>
              <a class="dropdown-item <%= colinfo.name + op in args ? 'active' : '' %>" href="#" data-op="<%- op %>" data-toggle="modal" data-target="#fh-modal-<%- form_id %>">
                <%- title %>
              </a>
            <% }) %>
            <% if (menu_item) {
              menu_item = false %>
              <div class="dropdown-divider"></div>
            <% } %>
            <% if (colinfo.hideable) { %>
Bharat R's avatar
Bharat R committed
106
              <a class="dropdown-item urlfilter" href="?<%- namespace + '_c=-' + encodeURIComponent(colinfo.name) %>" data-mode="add">Hide</a>
107
108
109
            <% } %>
          </div><!-- .dropdown-menu -->
        </div><!-- .dropdown -->
S Anand's avatar
S Anand committed
110
      </th>
111
    <% }) %>
S Anand's avatar
S Anand committed
112
113
  </thead>
  <tbody>
114
115
    <% if (isAdd) { %>
      <tr class="new-row">
116
117
        <% _.each(cols, function(colinfo) {
          if (!colinfo.template) { %>
118
119
120
            <td data-key="<%- colinfo.name %>">
              <% var isEditable = colinfo.editable === undefined ? true : colinfo.editable %>
              <%= _.template(templates['template_editable'])({isEditable: isEditable, val: undefined}) %>
121
            </td>
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
          <% } else { %>
            <td></td>
          <% } %>
        <% }) %>
      </tr>
    <% } %>
    <% if (options.rowTemplate) { %>
      <% _.each(data, function(row, rowIndex) { %>
        <%= typeof options.rowTemplate == 'function' ? options.rowTemplate({row: row, data: data, index: rowIndex}) : _.template(options.rowTemplate)({row: row, data: data, index: rowIndex}) %>
      <% }) %>
    <% } else {%>
    <% _.each(data, function(row, rowIndex) { %>
      <tr data-val="<%- JSON.stringify(row) %>" data-row="<%- rowIndex %>">
        <% _.each(cols, function(colinfo) { %>
          <% var fmt = typeof(colinfo.format),
            val = row[colinfo.name],
            isEditable = colinfo.editable === undefined ? true : colinfo.editable,
            disp = fmt == "function" ?
              colinfo.format({name: colinfo.name, value: val, index: rowIndex, row: row, data:data }) :
            fmt === "string" && colinfo.type === "number" ?
              numeral(val).format(colinfo.format) :
            fmt === "string" && colinfo.type === "date" ?
144
              moment.utc(val).format(colinfo.format):
145
146
147
              val,
            col_link
          %>
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
          <% if (!isEdit && colinfo.template) { %>
            <%= typeof colinfo.template == 'function' ?
              colinfo.template({name: colinfo.name, value: val, format: disp, link: col_link, index: rowIndex, row: row, data: data}) :
              _.template(colinfo.template)(({name: colinfo.name, value: val, format: disp, link: col_link, index: rowIndex, row: row, data: data})) %>
          <% } else if (!isEdit && 'link' in colinfo) { %>
            <% col_link = typeof colinfo.link == 'function' ?
              colinfo.link({name: colinfo.name, value: val, format: disp, index: rowIndex, row: row, data: data}) :
              (typeof colinfo.link == 'string' ?
                _.template(colinfo.link)({name: colinfo.name, value: val, format: disp, index: rowIndex, row: row, data: data})
                : colinfo.link)
                %>
            <% if (col_link === false) { %>
                <td><%= disp %></td>
            <% } else if (col_link && col_link[0] == '?') { %>
                <td><a class="urlfilter" href="<%- col_link %>"><%= disp %></a></td>
            <% } else { %>
                <td><a href="<%- col_link %>" target="_blank"><%= disp %></a></td>
            <% } %>
S Anand's avatar
S Anand committed
166
          <% } else if (isEdit && isEditable) { %>
167
168
169
            <td data-key="<%- colinfo.name %>">
              <%= _.template(templates['template_editable'])({isEditable: isEditable, val: val}) %>
            </td>
S Anand's avatar
S Anand committed
170
          <% } else { %>
171
            <td>
Bharat R's avatar
Bharat R committed
172
              <a class="urlfilter ii" href="?<%- namespace + encodeURIComponent(colinfo.name) %>=<%- encodeURIComponent(val) %>&amp;_offset=">
173
174
175
                <%= disp %>
              </a>
            </td>
S Anand's avatar
S Anand committed
176
177
          <% } %>
        <% }) %>
178
        </tr>
179
180
      <% }) %>
    <% } %>
S Anand's avatar
S Anand committed
181
182
183
184
  </tbody>
</table>
<!-- end -->

185
186
187
188
189
190
191
192
193
<!-- var template_editable -->
<% if (isEditable.input == 'select') { %>

  <select
      <% for (key in isEditable.attrs) { %>
        <%= key + '="' + isEditable.attrs[key] + '"' %>
      <% } %>
      class="form-control form-control-sm"
    >
194
    <option value="" disabled selected>-- select --</option>
195
    <% _.each(isEditable.options, function(item) { %>
196
      <option <%- val !== undefined && val === item ? 'selected': null %> value="<%- item %>">
197
198
199
200
201
202
203
204
205
        <%- item %>
      </option>
    <% }) %>
  </select>


<% } else if (isEditable.input == 'radio') { %>

  <% _.each(isEditable.options, function(item) { %>
206
    <input type="radio" <%- val !== undefined && val === item ? 'checked': null %> value="<%- item %>"
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
      <% for (key in isEditable.attrs) { %>
        <%= key + '="' + isEditable.attrs[key] + '"' %>
      <% } %>
      class="form-control form-control-sm"
    />
    <%- item %> <br>
  <% }) %>

<% } else { %>
  <input type="<%- isEditable.input || 'text' %>" value="<%- val %>"
    <% for (key in isEditable.attrs) { %>
      <%= key + '="' + isEditable.attrs[key] + '"' %>
    <% } %>
    class="form-control form-control-sm"
  />

<% } %>
224
225
226
227
228
<% if (isEditable.validationMessage) { %>
  <div class="invalid-feedback">
    <%- isEditable.validationMessage %>
  </div>
<% } %>
229
230
<!-- end -->

S Anand's avatar
S Anand committed
231
232
<!-- var template_page -->
<% var page = 1 + Math.floor(meta.offset / meta.limit),
233
234
      last_page = 'count' in meta ? Math.floor((meta.count + meta.limit - 1) / meta.limit) : meta.rows < meta.limit ? page : null,
      lo = Math.max(page - 2, 1),
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
      hi = last_page !== null ? Math.min(last_page, page + 2) : page + 2 %>
<ul class="pagination pagination-sm mr-2">
  <li class="page-item <%- page <= 1 ? 'disabled' : '' %>">
    <a class="page-link" href="?_offset=<%- meta.offset - meta.limit %>">Previous</a>
  </li>
  <% if (lo > 1) { %>
    <li class="page-item">
      <a class="page-link" href="?_offset=">1</a>
    </li>
    <% if (lo > 2) { %>
      <li class="page-item disabled">
        <a class="page-link" href="#">...</a>
      </li>
    <% } %>
  <% } %>
  <% _.each(_.range(lo, hi + 1), function(pg) { %>
    <li class="page-item <%- pg == page ? 'active' : '' %>">
      <a class="page-link" href="?_offset=<%- meta.limit * (pg - 1) || '' %>">
        <%- pg %>
      </a>
S Anand's avatar
S Anand committed
255
    </li>
256
257
258
259
260
261
262
263
  <% }) %>
  <% if ('count' in meta) { %>
    <% if (hi + 1 < last_page) { %>
      <li class="page-item disabled">
        <a class="page-link" href="#">...</a>
      </li>
    <% } %>
    <% if (hi < last_page || lo > hi) { %>
S Anand's avatar
S Anand committed
264
      <li class="page-item">
265
266
267
        <a class="page-link" href="?_offset=<%- meta.limit * (last_page - 1) %>">
          <%- last_page %>
        </a>
S Anand's avatar
S Anand committed
268
      </li>
269
270
271
272
273
274
275
    <% } %>
  <% } %>
  <li class="page-item <%- (last_page === null) || (page < last_page) ? '' : 'disabled' %>">
    <a class="page-link" href="?_offset=<%- meta.offset + meta.limit %>">Next</a>
  </li>
</ul>
<!-- end -->
S Anand's avatar
S Anand committed
276

277
278
279
280
281
282
283
284
285
286
287
288
289
<!-- var template_size -->
<% if (meta.limit) { %>
  <div class="btn-group btn-group-sm mr-2" role="group">
    <button id="formhandler-size-<%- idcount++ %>" type="button" class="btn btn-light btn-sm dropdown-toggle" data-toggle="dropdown"
      aria-haspopup="true" aria-expanded="false">
      <%- meta.limit %> rows
    </button>
    <div class="dropdown-menu" aria-labelledby="formhandler-size-<%- idcount %>">
      <% _.each(options.sizeValues, function(size) { %>
        <a class="dropdown-item <%- meta.limit == size ? 'active' : '' %> urlfilter" href="?_limit=<%- size %>">
          <%- size %>
        </a>
      <% }) %>
290
    </div>
291
292
293
  </div>
<% } %>
<!-- end -->
294

295
296
297
298
299
300
301
<!-- var template_count -->
<% if ('count' in meta) { %>
  <span class="btn btn-sm btn-light mr-2">
    <%- meta.count %> rows
  </span>
<% } %>
<!-- end -->
S Anand's avatar
S Anand committed
302

303
<!-- var template_edit -->
S Anand's avatar
S Anand committed
304
<button type="submit" class="btn btn-success mr-2 btn-sm edit-btn">
305
306
307
  Edit
</button>
<!-- end -->
308

309
310
311
312
313
<!-- var template_add -->
<button type="button" class="btn btn-success mr-2 btn-sm add-btn">
  Add
</button>
<!-- end -->
314

315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
<!-- var template_export -->
<div class="btn-group btn-group-sm" role="group">
  <button id="formhandler-export-<%- idcount++ %>" type="button" class="btn btn-light btn-sm dropdown-toggle" data-toggle="dropdown"
    aria-haspopup="true" aria-expanded="false">
    Export as
  </button>
  <div class="dropdown-menu dropdown-menu-right" aria-labelledby="formhandler-export-<%- idcount %>">
    <% _.each(options.exportFormats, function(label, key) { %>
      <a class="dropdown-item" href="<%- parse(options.src).update(args).update({_format: key}) %>">
        <%- label %>
      </a>
    <% }) %>
  </div>
</div>
<!-- end -->
S Anand's avatar
S Anand committed
330

331
332
333
334
335
336
<!-- var template_filters -->
<div class="p-1"><%
  var qparts = parse('?')
  _.each(args['_c'], function(col_name) {
    qparts.update({_c: col_name}, 'add')
    var hide_col = col_name[0] == '-'
Bharat R's avatar
Bharat R committed
337
338
    var display_name = hide_col ? col_name.slice(1) : col_name
    display_name = (display_name.indexOf(':') > -1)? display_name.split(':')[1].trim() : display_name %>
339
    <a href="?_c=<%- encodeURIComponent(col_name) %>" data-mode="del" class="badge badge-pill <%- hide_col ? 'badge-dark' : 'badge-danger' %> urlfilter"
340
341
342
343
344
      title="<%- hide_col ? 'Show' : 'Hide' %> column <%- display_name %>">
      <%- display_name %>
    </a>
  <% })
  _.each(args, function(list_values, key) {
Bharat R's avatar
Bharat R committed
345
346
347
    var isnamespaced_key = (key.indexOf(":") > -1)
    var has_valid_namespace = (options.namespace)? (key.indexOf(options.namespace +':') > -1) : false

348
349
    if (key.charAt(0) !== '_' && key !== 'c') {
      _.each(args[key], function(col_name) {
Bharat R's avatar
Bharat R committed
350
351
352
353
354
355
356
357
358
        if( !isnamespaced_key || (isnamespaced_key && has_valid_namespace) ){
          var update = {}
          update[key] = col_name
          qparts.update(update, 'add')
          var display_key = (key.indexOf(options.namespace +':') > -1)? key.split(":")[1].trim() : key %>
          <a href="?<%- encodeURIComponent(key) %>=<%- encodeURIComponent(col_name) %>" data-mode="del" class="badge badge-pill badge-dark urlfilter" title="Clear <%- key %> filter">
            <%- display_key %> = <%- col_name %>
          </a>
      <% } })
359
360
361
362
363
364
365
366
    }
  })
  qparts = qparts.toString()
  if (qparts && qparts != '?') { %>
    <a href="?<%- qparts.slice(1) %>" class="badge badge-pill badge-danger urlfilter" data-mode="del" title="Clear all filters">×</a>
  <% } %>
</div>
<!-- end -->
367

368
369
370
371
372
373
374
375
<!-- var template_error -->
<div class="alert alert-warning alert-dismissible" role="alert">
  <%- message %>
  <button type="button" class="close" data-dismiss="alert" aria-label="Close">
    <span aria-hidden="true">&times;</span>
  </button>
</div>
<!-- end -->
S Anand's avatar
S Anand committed
376

377
378
379
380
381
382
383
<!-- var template_table_grid -->
<%
  var filtered_cols = args['_c'] && args['_c'].length != options.columns.length ?
                      options.columns.filter(function(col) { return args['_c'].indexOf('-' + col.name) < 0 }) :
                      options.columns
  var cols = options.columns.length ? filtered_cols : meta.columns
  var form_id = idcount, img
384
385
  if (options.rowTemplate) {
    _.each(data, function(row, rowIndex) { %>
386
    <%= typeof options.rowTemplate == 'function' ? options.rowTemplate({row: row, index: rowIndex, data: data}) : _.template(options.rowTemplate)({row: row, data: data, index: rowIndex}) %>
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
  <% })
} else { %>
  <div class="formhandler-grid row">
    <% _.each(data, function(row, rowIndex) { %>
      <div class="col-sm-3 <%= options.classes || 'formhandler-grid-cell d-inline-block p-3 box-shadow' %>">
        <div class="thumbnail">
          <% img = options.icon ? ((typeof(options.icon) == 'function' ? options.icon({row: row, data: data, index: rowIndex}) : options.icon)) : 'fa fa-home' %>
          <% if (img.indexOf('fa ') >= 0) { %>
            <i class="<%= img %>"></i>
          <% } else { %>
            <img class="img img-responsive" src="<%= img %>"/>
          <% } %>
          <div class="caption">
            <% _.each(cols, function(colinfo) {
              var fmt = typeof(colinfo.format),
                  val = row[colinfo.name],
                  disp = (fmt == "function" ? colinfo.format({index: rowIndex, name: colinfo.name, value: val, row: row, data:data }) :
                          fmt === "string" && colinfo.type === "number" ? numeral(val).format(colinfo.format) :
405
                          fmt === "string" && colinfo.type === "date" ? moment.utc(val).format(colinfo.format) :
406
407
408
                          val) %>
              <div>
                <strong><%= colinfo.name %></strong>:
S Anand's avatar
WIP    
S Anand committed
409
410
411
412
413
                <% if ('link' in colinfo) { %>
                  <% var col_link = typeof colinfo.link == 'function' ?
                      colinfo.link({row: row, value: val, index: rowIndex, name: colinfo.name, data: data, format: disp}) :
                      _.template(colinfo.link)({row: row, value: val, index: rowIndex, name: colinfo.name, data: data, format: disp}) %>
                  <% if (col_link === false) { %>
414
415
416
417
418
419
                    <%= disp %>
                  <% } else if (col_link && col_link[0] == '?') { %>
                    <a class="urlfilter" href="<%- col_link %>"><%= disp %></a>
                  <% } else { %>
                    <a href="<%- col_link %>" target="_blank"><%= disp %></a>
                  <% } %>
420
                <% } else { %>
421
                  <a class="urlfilter" href="?<%- encodeURIComponent(colinfo.name) %>=<%- encodeURIComponent(val) %>&amp;_offset=">
422
423
                    <%= disp %>
                  </a>
424
425
426
427
428
429
430
431
                <% } %>
              </div>
            <% }) %>
          </div><!-- .caption -->
        </div><!-- .thumbnail -->
      </div><!-- .col-sm-3 -->
    <% }) %>
  </div><!-- .formhandler-grid -->
432
433
<% } %>
<!-- end -->