list.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. const app = require('../../app')
  2. const {editIcon, createIcon, deleteIcon} = require('../../assets')
  3. /**
  4. * @param {CrudPagesOptions} opts
  5. */
  6. const list = (opts) => {
  7. const defaultHeader = column => html`<th md-column>${column.titleName}</th>`
  8. const defaultCell = column => {
  9. if (column.type === 'autocomplete') {
  10. return html`<td md-cell>{{${raw(opts.camelName)}.${raw(column.camelName)} && (ctrl.autocomplete.${raw(column.camelName)}.lookup[${raw(opts.camelName)}.${raw(column.camelName)}] || ctrl.autocomplete.${raw(column.camelName)}.load(${raw(opts.camelName)}.${raw(column.camelName)}))}}</td>`
  11. } else {
  12. return html`<td md-cell>{{${raw(opts.camelName)}.${raw(column.camelName)}}}</td>`
  13. }
  14. }
  15. const template = html`
  16. <app-user-area>
  17. <h1>${opts.titlePlural}</h1>
  18. <md-table-container>
  19. <table md-table md-row-select md-auto-select ng-model="ctrl.selected" md-progress="ctrl.promise">
  20. <thead md-head md-order="query.order" md-on-reorder="ctrl.getRecords">
  21. <tr md-row>
  22. ${opts.columns.filter(c => c.inList).map(c => c.header || defaultHeader(c))}
  23. <th md-column>Actions</th>
  24. </tr>
  25. </thead>
  26. <tbody md-body>
  27. <tr md-row md-select="${opts.camelName}" md-select-id="name" md-auto-select ng-repeat="${raw(opts.camelName)} in ctrl.data track by ${raw(opts.camelName)}.id">
  28. ${opts.columns.filter(c => c.inList).map(c => c.cell || defaultCell(c))}
  29. <td md-cell>
  30. <md-button ng-href="${opts.appPrefix}/{{${raw(opts.camelName)}.id}}">
  31. <md-icon md-svg-icon="${editIcon}"></md-icon>
  32. Edit
  33. </md-button>
  34. <md-button ng-click="ctrl.delete(${raw(opts.camelName)})">
  35. <md-icon md-svg-src="${deleteIcon}"></md-icon>
  36. Delete
  37. </md-button>
  38. </td>
  39. </tr>
  40. </tbody>
  41. </table>
  42. </md-table-container>
  43. <div layout="row" layout-align="end">
  44. <md-button class="md-fab" aria-label="Add ${opts.titleName}" ng-href="${opts.appPrefix}/new">
  45. <md-icon md-svg-src="${createIcon}"></md-icon>
  46. </md-button>
  47. </div>
  48. </app-user-area>
  49. `
  50. console.log(`crud: app${opts.pascalPlural}Page`)
  51. app.component(`app${opts.pascalPlural}Page`, {
  52. template,
  53. controllerAs: 'ctrl',
  54. controller: function(api, $mdToast) {
  55. const crud = api.crud(opts.apiPrefix)
  56. this.template = template
  57. this.selected = []
  58. this.data = []
  59. this.getRecords = () => {
  60. this.promise = crud.list().then(data => {
  61. this.data = data
  62. })
  63. }
  64. this.delete = async (record) => {
  65. try {
  66. await crud.delete(record.id)
  67. $mdToast.showSimple(`${opts.titleName} deleted.`)
  68. } catch (err) {
  69. console.error(err)
  70. $mdToast.showSimple(`Could not delete ${opts.titleName}: ${err.message || err}`)
  71. }
  72. }
  73. this.getRecords()
  74. /* Autocomplete fields */
  75. this.autocomplete = {}
  76. opts.columns.filter(c => c.type === 'autocomplete').forEach(c => {
  77. const crud = api.crud(c.apiPrefix)
  78. let timer = null
  79. let ids = []
  80. const load = () => {
  81. timer = null
  82. console.log('Loading', ids)
  83. crud.lookup(ids).then(records => {
  84. records.forEach(rec => {
  85. ac.lookup[rec.id] = rec.name || rec.key || rec.id
  86. })
  87. })
  88. }
  89. const ac = this.autocomplete[c.camelName] = {
  90. lookup: {},
  91. load(id) {
  92. if (!ac.lookup.hasOwnProperty(id)) {
  93. console.log('Queuing to load', id)
  94. ac.lookup[id] = '...'
  95. if (!ids.includes(id)) ids.push(id)
  96. if (!timer) timer = setTimeout(load)
  97. }
  98. return ac.lookup[id]
  99. }
  100. }
  101. })
  102. }
  103. })
  104. }
  105. module.exports = list