You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
7.7 KiB

import {Component} from '../../vues6.js'
import {Resource, ResourceActions} from '../Resource.js'
import {uuid} from '../util.js'
import {ActionService} from '../service/Action.service.js'
export const Status = Object.freeze({
loading: 'loading',
ready: 'ready',
errorUnsupported: 'errorUnsupported',
saving: 'saving',
})
const template = `
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
{{ title }}
</h6>
</div>
<div class="card-body">
<div v-if="status === 'loading'">Loading...</div>
<div class="row m-5" v-if="status === 'errorUnsupported'">
<div class="col-12 text-center">
<h4 class="pad-top">This resource does not support listing.</h4>
</div>
</div>
<div class="" v-if="status === 'ready'">
<div class="actions" style="display: flex; flex-direction: row; justify-content: end">
<span v-for="action of actions">
<cobalt-action-button v-if="action.overall" :action="action" @perform="() => onAction(action)"></cobalt-action-button>
</span>
</div>
<table class="table table-bordered display compact" :id="'table-' + id" width="99%" cellspacing="0">
<thead>
<tr>
<th>#</th>
<th v-for="col of columns">{{ col.display }}</th>
<th></th>
</tr>
</thead>
<tfoot>
<tr>
<th>#</th>
<th v-for="col of columns">{{ col.display }}</th>
<th></th>
</tr>
</tfoot>
<tbody></tbody>
</table>
</div>
</div>
</div>
`
export class ListingComponent extends Component {
static get selector() { return 'cobalt-listing' }
static get template() { return template }
static get props() { return ['resourcekey'] }
constructor() {
super()
this.status = Status.loading
this.title = 'Loading...'
this.rows = []
this.columns = []
this.actions = []
this.unregister = []
this.id = uuid()
/** @var {Resource} */
this.resource = undefined
}
vue_on_destroy() {
this.unregister.forEach(actionName => {
if ( window[actionName] ) {
delete window[actionName]
}
})
}
async vue_on_create() {
this.resource = await Resource.get(this.resourcekey)
this.title = this.resource.plural()
if ( !this.resource.supports(ResourceActions.read) ) {
this.status = Status.errorUnsupported
return
}
await this.load()
}
async load(reload = false) {
this.columns = [...this.resource.configuration.fields]
if ( !reload && this.resource.supports(ResourceActions.create) ) {
this.actions.push({
title: 'Add New',
color: 'success',
icon: 'fa-plus',
overall: true,
type: 'resource',
resource: this.resource.key,
action: 'insert',
defer: true,
})
}
if ( !reload && this.resource.supports(ResourceActions.update) ) {
this.actions.push({
title: 'Edit',
color: 'primary',
icon: 'fa-edit',
type: 'resource',
resource: this.resource.key,
action: 'update',
defer: true,
})
}
if ( !reload && this.resource.supports(ResourceActions.delete) ) {
this.actions.push({
title: 'Delete',
color: 'danger',
icon: 'fa-trash-alt',
type: 'resource',
resource: this.resource.key,
action: 'delete',
defer: true,
})
}
this.rows = await this.resource.read()
this.rows.forEach((row, idx) => row.idx = idx)
const order = []
this.columns.forEach((col, i) => {
if ( col.sort ) {
order.push([i+1, col.sort])
}
})
this.status = Status.ready
this.$nextTick(() => {
$(`#table-${this.id}`).DataTable({
order,
data: this.rows,
columns: [
{
data: 'idx',
render: data => `${data + 1}`,
},
...this.columns.map((col, idx) => {
return {
data: col.key,
render: (data, type) => {
if ( type === 'display' ) {
if ( col.renderer === 'html' ) {
return String(data || '')
} else if ( col.renderer === 'bool' ) {
return data ? 'Yes' : 'No'
} else if ( col.renderer === 'date' && data ) {
return (new Date(data)).toDisplay()
} else if ( col.renderer === 'time' && data ) {
return (new Date(data)).toLocaleTimeString()
} else if ( col.renderer === 'datetime' && data ) {
return `${(new Date(data)).toDisplay()} ${(new Date(data)).toLocaleTimeString()}`
}
return String(data || '-').replace(/</g, '&lt;').replace(/>/, '&gt;')
}
return data
}
}
}),
{
data: 'idx',
render: (data, type, row) => {
if ( type === 'display' ) {
const html = []
this.actions.forEach((action, idx) => {
if ( action.overall ) return
const actionName = `action_${uuid().replace(/-/g, '')}`
this.unregister.push(actionName)
window[actionName] = () => {
this.onAction(action, row)
}
html.push(`
<a
class="btn btn-sm btn-${action.color}"
href="#"
title="${action.title}"
onclick="${actionName}()"
>
<i class="fa ${action.icon}"></i>
</a>
`)
})
return html.join('')
}
return data
}
},
],
})
})
}
async onAction(action, row) {
await ActionService.get().perform(action, row, async () => {
this.status = Status.loading
await this.load(true)
})
}
}