waibu-db 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +23 -0
- package/bajo/.alias +1 -0
- package/bajo/config.json +17 -0
- package/bajo/method/admin-menu.js +46 -0
- package/bajo/method/get-params.js +18 -0
- package/bajo/method/get-schema-ext.js +133 -0
- package/bajo/method/method-map.js +9 -0
- package/bajo/method/record/create.js +12 -0
- package/bajo/method/record/find-one.js +20 -0
- package/bajo/method/record/find.js +26 -0
- package/bajo/method/record/get.js +14 -0
- package/bajo/method/record/remove.js +10 -0
- package/bajo/method/record/update.js +12 -0
- package/bajo/method/stat/aggregate.js +13 -0
- package/bajo/method/stat/histogram.js +13 -0
- package/bajoI18N/resource/en-US.json +18 -0
- package/bajoI18N/resource/id.json +40 -0
- package/lib/crud/add-handler.js +39 -0
- package/lib/crud/all-handler.js +31 -0
- package/lib/crud/delete-handler.js +37 -0
- package/lib/crud/details-handler.js +18 -0
- package/lib/crud/edit-handler.js +33 -0
- package/lib/crud/export-handler.js +15 -0
- package/lib/crud/helper/add-ons-handler.js +46 -0
- package/lib/crud/helper/build-params.js +14 -0
- package/lib/crud/list-handler.js +27 -0
- package/lib/prep-crud.js +17 -0
- package/package.json +30 -0
- package/waibuBootstrap/theme/component/btn-add.js +15 -0
- package/waibuBootstrap/theme/component/btn-back.js +13 -0
- package/waibuBootstrap/theme/component/btn-clone.js +30 -0
- package/waibuBootstrap/theme/component/btn-columns.js +45 -0
- package/waibuBootstrap/theme/component/btn-delete.js +43 -0
- package/waibuBootstrap/theme/component/btn-details.js +32 -0
- package/waibuBootstrap/theme/component/btn-edit.js +56 -0
- package/waibuBootstrap/theme/component/btn-export.js +129 -0
- package/waibuBootstrap/theme/component/echarts.js +58 -0
- package/waibuBootstrap/theme/component/pagination.js +60 -0
- package/waibuBootstrap/theme/component/query.js +137 -0
- package/waibuBootstrap/theme/component/recs-info.js +50 -0
- package/waibuBootstrap/theme/component/table.js +161 -0
- package/waibuMpa/extend/waibuAdmin/route/@model/@action.js +14 -0
- package/waibuMpa/partial/crud/_addons.html +5 -0
- package/waibuMpa/partial/crud/_form.html +12 -0
- package/waibuMpa/partial/crud/add-handler.html +19 -0
- package/waibuMpa/partial/crud/details-handler.html +21 -0
- package/waibuMpa/partial/crud/echarts-window.html +9 -0
- package/waibuMpa/partial/crud/edit-handler.html +19 -0
- package/waibuMpa/partial/crud/list-handler.html +26 -0
- package/waibuMpa/sumba/route/export/@action.js +14 -0
- package/waibuMpa/template/crud/add.html +2 -0
- package/waibuMpa/template/crud/details.html +2 -0
- package/waibuMpa/template/crud/edit.html +2 -0
- package/waibuMpa/template/crud/list.html +2 -0
- package/waibuMpa/template/disabled.html +8 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
async function query (params = {}) {
|
|
2
|
+
const { generateId } = this.plugin.app.bajo
|
|
3
|
+
const { jsonStringify } = this.plugin.app.waibuMpa
|
|
4
|
+
const { find, get, without, isEmpty, filter, upperFirst } = this.plugin.app.bajo.lib._
|
|
5
|
+
const qsKey = this.plugin.app.waibu.config.qsKey
|
|
6
|
+
const schema = get(this, 'locals.schema', {})
|
|
7
|
+
if (schema.view.disabled.includes('find')) {
|
|
8
|
+
params.html = ''
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
let fields = without(get(this, `locals._meta.query.${qsKey.fields}`, '').split(','), '')
|
|
12
|
+
if (isEmpty(fields)) fields = schema.view.fields
|
|
13
|
+
fields = filter(fields, f => schema.sortables.includes(f))
|
|
14
|
+
const id = generateId('alpha')
|
|
15
|
+
const columns = []
|
|
16
|
+
const models = []
|
|
17
|
+
const selects = ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'in', 'contains', 'starts', 'ends', '!in', '!contains', '!starts', '!ends']
|
|
18
|
+
for (const f of schema.view.fields) {
|
|
19
|
+
if (!fields.includes(f)) continue
|
|
20
|
+
const prop = find(schema.properties, { name: f })
|
|
21
|
+
const ops = []
|
|
22
|
+
if (['float', 'double', 'integer', 'smallint'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
|
|
23
|
+
else if (['datetime', 'date', 'time'].includes(prop.type)) ops.push('eq', 'neq', 'gt', 'gte', 'lt', 'lte')
|
|
24
|
+
else if (['boolean'].includes(prop.type)) ops.push('eq', 'neq')
|
|
25
|
+
else ops.push(...selects)
|
|
26
|
+
if (ops.length === 0) continue
|
|
27
|
+
const sels = ops.map(o => `<c:option>${o}</c:option>`)
|
|
28
|
+
models.push(`${f}Op: 'eq'`, `${f}Val: ''`)
|
|
29
|
+
columns.push(`
|
|
30
|
+
<c:grid-col col="4-md" flex="align-items:center">
|
|
31
|
+
<c:form-check x-model="selected" t:label="field.${f}" value="${f}" />
|
|
32
|
+
</c:grid-col>
|
|
33
|
+
<c:grid-col col="3-md">
|
|
34
|
+
<c:form-select x-model="${f}Op">
|
|
35
|
+
${sels.join('\n')}
|
|
36
|
+
</c:form-select>
|
|
37
|
+
</c:grid-col>
|
|
38
|
+
<c:grid-col col="5-md">
|
|
39
|
+
<c:form-input x-model="${f}Val" />
|
|
40
|
+
</c:grid-col>
|
|
41
|
+
`)
|
|
42
|
+
}
|
|
43
|
+
params.noTag = true
|
|
44
|
+
const container = params.attr.modal ? 'modal' : 'drawer'
|
|
45
|
+
params.html = await this.buildSentence(`
|
|
46
|
+
<c:form-input type="search" t:placeholder="Query" id="${id}" x-data="{ query: '' }" x-init="
|
|
47
|
+
const url = new URL(window.location.href)
|
|
48
|
+
query = url.searchParams.get('${qsKey.query}') ?? ''
|
|
49
|
+
" x-model="query" @on-query.window="query = $event.detail ?? ''" @keyup.enter="$dispatch('on-submit')">
|
|
50
|
+
<c:form-input-addon>
|
|
51
|
+
<c:${container} launch-icon="${params.attr.icon ?? 'dotsThree'}" launch-on-end t:title="Query Builder" x-ref="query" x-data="{
|
|
52
|
+
fields: ${jsonStringify(fields, true)},
|
|
53
|
+
builder: '',
|
|
54
|
+
selected: [],
|
|
55
|
+
${models.join(',\n')},
|
|
56
|
+
ops: { eq: ':', neq: ':-', gt: ':>', gte: ':>=', lt: ':<', lte: ':<=' },
|
|
57
|
+
opsIn (v, neg) { return ':' + (neg ? '-' : '') + '[' + this.expandArray(v) + ']' },
|
|
58
|
+
opsExt (v, neg, ext) {
|
|
59
|
+
let prefix = (neg ? '-' : '') + '~'
|
|
60
|
+
if (ext) prefix += ext
|
|
61
|
+
return ':' + prefix + '\\'' + v + '\\''
|
|
62
|
+
},
|
|
63
|
+
expandArray (val = '') {
|
|
64
|
+
return _.map(val.split(','), item => {
|
|
65
|
+
item = _.trim(item)
|
|
66
|
+
if (Number(item)) return item
|
|
67
|
+
return '\\'' + item + '\\''
|
|
68
|
+
})
|
|
69
|
+
},
|
|
70
|
+
rebuild () {
|
|
71
|
+
const items = []
|
|
72
|
+
for (const sel of this.selected) {
|
|
73
|
+
const key = this[sel + 'Op']
|
|
74
|
+
let val = this[sel + 'Val']
|
|
75
|
+
if (_.isEmpty(val)) continue
|
|
76
|
+
let item
|
|
77
|
+
if (key === 'in') item = this.opsIn(val)
|
|
78
|
+
else if (key === '!in') item = this.opsIn(val, true)
|
|
79
|
+
else if (key === 'contains') item = this.opsExt(val)
|
|
80
|
+
else if (key === '!contains') item = this.opsExt(val, true)
|
|
81
|
+
else if (key === 'starts') item = this.opsExt(val, false, '^')
|
|
82
|
+
else if (key === '!starts') item = this.opsExt(val, true, '^')
|
|
83
|
+
else if (key === 'ends') item = this.opsExt(val, false, '$$')
|
|
84
|
+
else if (key === '!ends') item = this.opsExt(val, true, '$$')
|
|
85
|
+
else if (val.includes(' ')) item = this.ops[key] + '\\'' + val + '\\''
|
|
86
|
+
else item = this.ops[key] + val
|
|
87
|
+
items.push(sel + item)
|
|
88
|
+
}
|
|
89
|
+
this.builder = items.join('+')
|
|
90
|
+
},
|
|
91
|
+
submit (run) {
|
|
92
|
+
if (run) {
|
|
93
|
+
const url = new URL(window.location.href)
|
|
94
|
+
const params = new URLSearchParams(url.search)
|
|
95
|
+
params.set('${qsKey.page}', 1)
|
|
96
|
+
params.set('${qsKey.query}', this.builder ?? '')
|
|
97
|
+
window.location.href = '?' + params.toString()
|
|
98
|
+
} else $dispatch('on-query', this.builder)
|
|
99
|
+
const instance = wbs.getInstance('${upperFirst(container)}', $refs.query)
|
|
100
|
+
instance.hide()
|
|
101
|
+
}
|
|
102
|
+
}" x-init="
|
|
103
|
+
const ops = _.map(fields, f => (f + 'Op'))
|
|
104
|
+
const vals = _.map(fields, f => (f + 'Val'))
|
|
105
|
+
const watcher = ['selected', ...ops, ...vals].join(',')
|
|
106
|
+
$watch(watcher, v => rebuild())
|
|
107
|
+
">
|
|
108
|
+
<c:grid-row gutter="2">
|
|
109
|
+
<c:grid-col col="12">
|
|
110
|
+
<c:form-textarea x-model="builder" readonly rows="4"/>
|
|
111
|
+
</c:grid-col>
|
|
112
|
+
${columns.join('\n')}
|
|
113
|
+
</c:grid-row>
|
|
114
|
+
<c:div flex="justify-content:end" margin="top-3">
|
|
115
|
+
<c:btn color="secondary" t:content="Close" dismiss="${container}" />
|
|
116
|
+
<c:btn color="primary" t:content="Apply" margin="start-2" @click="submit()" />
|
|
117
|
+
<c:btn color="primary" t:content="Submit Query" margin="start-2" @click="submit(true)" />
|
|
118
|
+
</c:div>
|
|
119
|
+
</c:${container}>
|
|
120
|
+
</c:form-input-addon>
|
|
121
|
+
<c:form-input-addon>
|
|
122
|
+
<c:btn t:content="Submit" x-data="{
|
|
123
|
+
submit () {
|
|
124
|
+
const val = document.getElementById('${id}').value ?? ''
|
|
125
|
+
const url = new URL(window.location.href)
|
|
126
|
+
const params = new URLSearchParams(url.search)
|
|
127
|
+
params.set('${qsKey.page}', 1)
|
|
128
|
+
params.set('${qsKey.query}', val)
|
|
129
|
+
window.location.href = '?' + params.toString()
|
|
130
|
+
}
|
|
131
|
+
}" @click="submit" @on-submit.window="submit()" />
|
|
132
|
+
</c:form-input-addon>
|
|
133
|
+
</c:form-input>
|
|
134
|
+
`)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export default query
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { getUrlOpts } from './pagination.js'
|
|
2
|
+
|
|
3
|
+
async function recordsInfo (params = {}) {
|
|
4
|
+
const { attrToObject, groupAttrs, attrToArray } = this.plugin.app.waibuMpa
|
|
5
|
+
const { get, isEmpty, omit, merge } = this.plugin.app.bajo.lib._
|
|
6
|
+
const schema = get(this, 'locals.schema', {})
|
|
7
|
+
if (schema.view.disabled.includes('find')) {
|
|
8
|
+
params.html = ''
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
let { count, limit, page, pages } = attrToObject(params.attr.options)
|
|
12
|
+
count = count ?? get(this, 'locals.list.count', 0)
|
|
13
|
+
page = page ?? get(this, 'locals.list.page', 1)
|
|
14
|
+
limit = limit ?? get(this, 'locals.list.limit', 25)
|
|
15
|
+
pages = pages ?? get(this, 'locals.list.pages', 0)
|
|
16
|
+
|
|
17
|
+
params.tag = 'div'
|
|
18
|
+
params.attr.flex = 'justify-center:start align-items:center'
|
|
19
|
+
if (count === 0) {
|
|
20
|
+
params.html = this.req.t('No record found')
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
if (!params.attr.dropdown) params.attr.dropdown = true
|
|
24
|
+
const group = groupAttrs(params.attr, ['dropdown'])
|
|
25
|
+
const html = []
|
|
26
|
+
if (params.attr.count) html.push(this.req.t('%s record(s) found', this.req.format(count, 'integer')))
|
|
27
|
+
if (params.attr.pages) {
|
|
28
|
+
if (!isEmpty(html)) html[html.length - 1] += '.'
|
|
29
|
+
html.push(this.req.t('Page %s of %s pages', this.req.format(page, 'integer'), this.req.format(pages, 'integer')))
|
|
30
|
+
}
|
|
31
|
+
if (params.attr.recsPerPage) {
|
|
32
|
+
params.attr.recsPerPageValues = params.attr.recsPerPageValues ?? '10 25 50'
|
|
33
|
+
params.attr.recsPerPageValues = attrToArray(params.attr.recsPerPageValues)
|
|
34
|
+
if (!isEmpty(html)) html[html.length - 1] += ','
|
|
35
|
+
const items = []
|
|
36
|
+
for (const i of params.attr.recsPerPageValues) {
|
|
37
|
+
const attr = { href: this._buildUrl(merge(getUrlOpts.call(this), { params: { limit: i, page: 1 } })), disabled: i === limit }
|
|
38
|
+
items.push(await this.buildTag({ tag: 'dropdownItem', attr, html: i + '' }))
|
|
39
|
+
}
|
|
40
|
+
const attr = group.dropdown
|
|
41
|
+
attr.content = limit + ''
|
|
42
|
+
attr.color = attr.color ?? 'secondary-outline'
|
|
43
|
+
html.push(await this.buildTag({ tag: 'dropdown', attr, html: items.join('\n') }))
|
|
44
|
+
html.push(' ', this.req.t('recs per page'))
|
|
45
|
+
}
|
|
46
|
+
params.attr = omit(params.attr, ['count', 'pages', 'recsPerPage', 'dropdown', 'recsPerPageValues'])
|
|
47
|
+
params.html = html.map(h => `<div class="me-1">${h}</div>`).join('\n')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default recordsInfo
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
function isRightAligned (type) {
|
|
2
|
+
return ['smallint', 'integer', 'float', 'double'].includes(type)
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
const table = {
|
|
6
|
+
handler: async function (params = {}) {
|
|
7
|
+
const { escape } = this.plugin.app.waibu
|
|
8
|
+
const { attrToArray, groupAttrs } = this.plugin.app.waibuMpa
|
|
9
|
+
const { get, omit, set, find, isEmpty, without } = this.plugin.app.bajo.lib._
|
|
10
|
+
const group = groupAttrs(params.attr, ['body', 'head', 'foot'])
|
|
11
|
+
params.attr = group._
|
|
12
|
+
|
|
13
|
+
const data = get(this, 'locals.list.data', [])
|
|
14
|
+
const schema = get(this, 'locals.schema', {})
|
|
15
|
+
if (schema.view.disabled.includes('find')) {
|
|
16
|
+
params.html = ''
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
const qsKey = this.plugin.app.waibu.config.qsKey
|
|
20
|
+
let fields = without(get(this, `locals._meta.query.${qsKey.fields}`, '').split(','), '')
|
|
21
|
+
if (isEmpty(fields)) fields = schema.view.fields
|
|
22
|
+
const sort = params.attr.sort ? attrToArray(params.attr.sort) : get(this, `locals._meta.query.${qsKey.sort}`, '')
|
|
23
|
+
|
|
24
|
+
let [sortCol, sortDir] = sort.split(':')
|
|
25
|
+
if (!['-1', '1'].includes(sortDir)) sortDir = '1'
|
|
26
|
+
|
|
27
|
+
let selection
|
|
28
|
+
const canDelete = !schema.view.disabled.includes('remove')
|
|
29
|
+
const canEdit = !schema.view.disabled.includes('update')
|
|
30
|
+
if (canEdit) selection = 'single'
|
|
31
|
+
if (canDelete) selection = 'multi'
|
|
32
|
+
if (selection) params.attr.hover = true
|
|
33
|
+
|
|
34
|
+
params.noTag = true
|
|
35
|
+
const html = []
|
|
36
|
+
let items = []
|
|
37
|
+
// head
|
|
38
|
+
for (const f of schema.view.fields) {
|
|
39
|
+
if (!fields.includes(f)) continue
|
|
40
|
+
const prop = find(schema.properties, { name: f })
|
|
41
|
+
let head = this.req.t(`field.${f}`)
|
|
42
|
+
if (!params.attr.noSort && (schema.sortables ?? []).includes(f)) {
|
|
43
|
+
let sortItem = `${f}:-1`
|
|
44
|
+
let icon = params.attr.sortUpIcon ?? 'caretUp'
|
|
45
|
+
if (f === sortCol) {
|
|
46
|
+
sortItem = `${f}:${sortDir === '1' ? '-1' : '1'}`
|
|
47
|
+
icon = sortDir === '1' ? (params.attr.sortUpIcon ?? 'caretUp') : (params.attr.sortDownIcon ?? 'caretDown')
|
|
48
|
+
}
|
|
49
|
+
const item = set({ page: 1 }, qsKey.sort, sortItem)
|
|
50
|
+
const href = this._buildUrl({ params: item })
|
|
51
|
+
const attr = isRightAligned(prop.type) ? { text: 'align:end' } : {}
|
|
52
|
+
const content = [
|
|
53
|
+
await this.buildTag({ tag: 'div', attr, html: this.req.t(`field.${f}`) }),
|
|
54
|
+
await this.buildTag({ tag: 'a', attr: { icon, href }, prepend: '<div class="ms-1">', append: '</div>' })
|
|
55
|
+
]
|
|
56
|
+
head = await this.buildTag({ tag: 'div', attr: { flex: 'justify-content:between align-items:end' }, html: content.join('\n') })
|
|
57
|
+
}
|
|
58
|
+
let text = params.attr.headerNowrap ? '' : 'nowrap'
|
|
59
|
+
if (isRightAligned(prop.type)) text += ' align:end'
|
|
60
|
+
const attr = { dataKey: f, dataType: prop.type, text }
|
|
61
|
+
items.push(await this.buildTag({ tag: 'th', attr, html: head }))
|
|
62
|
+
}
|
|
63
|
+
if (items.length > 0 && selection) {
|
|
64
|
+
let item = '<th></th>'
|
|
65
|
+
if (selection === 'multi') {
|
|
66
|
+
const attr = { 'x-model': 'toggleAll', name: '_rtm', noWrapper: true, noLabel: true }
|
|
67
|
+
item = await this.buildTag({ tag: 'formCheck', attr, prepend: '<th>', append: '</th>' })
|
|
68
|
+
} else {
|
|
69
|
+
const attr = { name: 'remove', '@click': 'selected = \'\'', style: { cursor: 'pointer' } }
|
|
70
|
+
item = await this.buildTag({ tag: 'icon', attr, prepend: '<th>', append: '</th>' })
|
|
71
|
+
}
|
|
72
|
+
items.unshift(item)
|
|
73
|
+
}
|
|
74
|
+
const header = await this.buildTag({ tag: 'tr', html: items.join('\n') })
|
|
75
|
+
html.push(await this.buildTag({ tag: 'thead', attr: group.head, html: header }))
|
|
76
|
+
// body
|
|
77
|
+
items = []
|
|
78
|
+
for (const d of data) {
|
|
79
|
+
const lines = []
|
|
80
|
+
if (selection) {
|
|
81
|
+
const tag = selection === 'single' ? 'formRadio' : 'formCheck'
|
|
82
|
+
const attr = { 'x-model': 'selected', name: '_rt', value: d.id, noLabel: true, noWrapper: true }
|
|
83
|
+
lines.push(await this.buildTag({ tag, attr, prepend: '<td>', append: '</td>' }))
|
|
84
|
+
}
|
|
85
|
+
for (const f of schema.view.fields) {
|
|
86
|
+
const prop = find(schema.properties, { name: f })
|
|
87
|
+
if (!fields.includes(f)) continue
|
|
88
|
+
const opts = {}
|
|
89
|
+
if (f === 'lng') opts.longitude = true
|
|
90
|
+
else if (f === 'lat') opts.latitude = true
|
|
91
|
+
let value = this.req.format(d[f], prop.type, opts)
|
|
92
|
+
if (prop.type === 'boolean') {
|
|
93
|
+
value = (await this.buildTag({ tag: 'icon', attr: { name: `circle${d[f] ? 'Check' : ''}` } })) +
|
|
94
|
+
' ' + (this.req.t(d[f] ? 'Yes' : 'No'))
|
|
95
|
+
} else value = escape(value)
|
|
96
|
+
let dataValue = d[f] ?? ''
|
|
97
|
+
if (['string', 'text'].includes(prop.type)) dataValue = escape(dataValue)
|
|
98
|
+
if (['array', 'object'].includes(prop.type)) dataValue = escape(JSON.stringify(d[f]))
|
|
99
|
+
const attr = { dataValue }
|
|
100
|
+
if (!['object', 'array'].includes(prop.type)) {
|
|
101
|
+
if (isRightAligned(prop.type)) attr.text = 'align:end nowrap'
|
|
102
|
+
else attr.text = 'nowrap'
|
|
103
|
+
}
|
|
104
|
+
const line = await this.buildTag({ tag: 'td', attr, html: value })
|
|
105
|
+
lines.push(line)
|
|
106
|
+
}
|
|
107
|
+
const attr = {}
|
|
108
|
+
if (!schema.view.disabled.includes('update') || !schema.view.disabled.includes('remove')) attr['@click'] = `toggle('${d.id}')`
|
|
109
|
+
if (!schema.view.disabled.includes('get')) attr['@dblclick'] = `goDetails('${d.id}')`
|
|
110
|
+
items.push(await this.buildTag({ tag: 'tr', attr, html: lines.join('\n') }))
|
|
111
|
+
}
|
|
112
|
+
html.push(await this.buildTag({ tag: 'tbody', attr: group.body, html: items.join('\n') }))
|
|
113
|
+
params.attr = omit(params.attr, ['sortUpIcon', 'sortDownIcon', 'noSort', 'selection', 'headerNowrap'])
|
|
114
|
+
const goDetails = `
|
|
115
|
+
goDetails (id) {
|
|
116
|
+
window.location.href = '${this._buildUrl({ base: 'details' })}&id=' + id
|
|
117
|
+
}
|
|
118
|
+
`
|
|
119
|
+
if (selection === 'multi') {
|
|
120
|
+
params.attr['x-data'] = `{
|
|
121
|
+
toggleAll: false,
|
|
122
|
+
selected: [],
|
|
123
|
+
toggle (id) {
|
|
124
|
+
if (this.selected.includes(id)) {
|
|
125
|
+
const idx = this.selected.indexOf(id)
|
|
126
|
+
this.selected.splice(idx, 1)
|
|
127
|
+
} else this.selected.push(id)
|
|
128
|
+
},
|
|
129
|
+
${goDetails}
|
|
130
|
+
}`
|
|
131
|
+
params.attr['x-init'] = `
|
|
132
|
+
$watch('toggleAll', val => {
|
|
133
|
+
if (val) {
|
|
134
|
+
const els = document.getElementsByName('_rt')
|
|
135
|
+
const items = Array.from(els)
|
|
136
|
+
selected = items.map(el => el.value)
|
|
137
|
+
} else selected = []
|
|
138
|
+
})
|
|
139
|
+
$watch('selected', val => $dispatch('on-selection', val))
|
|
140
|
+
`
|
|
141
|
+
} else if (selection === 'single') {
|
|
142
|
+
params.attr['x-data'] = `{
|
|
143
|
+
selected: '',
|
|
144
|
+
toggle (id) {
|
|
145
|
+
this.selected = id
|
|
146
|
+
},
|
|
147
|
+
${goDetails}
|
|
148
|
+
}`
|
|
149
|
+
params.attr['x-init'] = `
|
|
150
|
+
$watch('selected', val => $dispatch('on-selection', [val]))
|
|
151
|
+
`
|
|
152
|
+
} else {
|
|
153
|
+
params.attr['x-data'] = `{
|
|
154
|
+
${goDetails}
|
|
155
|
+
}`
|
|
156
|
+
}
|
|
157
|
+
params.html = await this.buildTag({ tag: 'table', attr: params.attr, html: html.join('\n') })
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export default table
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const action = {
|
|
2
|
+
method: ['GET', 'POST'],
|
|
3
|
+
title: 'Model Database',
|
|
4
|
+
handler: async function (req, reply) {
|
|
5
|
+
const { importModule } = this.app.bajo
|
|
6
|
+
const handler = await importModule('waibuDb:/lib/crud/all-handler.js')
|
|
7
|
+
const { model, action } = req.params
|
|
8
|
+
const template = `waibuDb.template:/crud/${action}.html`
|
|
9
|
+
const params = { page: { layout: `waibuAdmin.layout:/crud/${action === 'list' ? 'wide' : 'default'}.html` } }
|
|
10
|
+
return handler.call(this, { model, req, reply, action, params, template })
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default action
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<% for (const l of schema.view.layout) { %>
|
|
2
|
+
<c:fieldset card <% if (l.name[0] !== '_') print('t:legend="' + l.name + '"') %> grid-gutter="2">
|
|
3
|
+
<% for (const w of l.widgets) {
|
|
4
|
+
const prop = _.find(schema.properties, { name: w.name })
|
|
5
|
+
const attr = []
|
|
6
|
+
_.forOwn(w.attr, (v, k) => {
|
|
7
|
+
attr.push(`${k}="${v}"`)
|
|
8
|
+
})
|
|
9
|
+
print(`<c:${w.component} ${w.attr.label ? ('label="' + _t(w.attr.label) + '"') : ''} data-type="${prop.type}" label-floating name="${w.name}" ${attr.join(' ')} />`)
|
|
10
|
+
} %>
|
|
11
|
+
</c:fieldset>
|
|
12
|
+
<% } %>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<c:form>
|
|
2
|
+
<c:include resource="waibuDb.partial:/crud/_form.html" />
|
|
3
|
+
<c:grid-row gutter="2" margin="top-2">
|
|
4
|
+
<c:grid-col col="6-md">
|
|
5
|
+
<c:wdb-btn-back />
|
|
6
|
+
</c:grid-col>
|
|
7
|
+
<c:grid-col col="6-md">
|
|
8
|
+
<c:div flex="justify-content:end-md align-items:center">
|
|
9
|
+
<c:btn type="reset" color="secondary" t:content="Reset" />
|
|
10
|
+
<c:btn type="submit" color="primary" t:content="Submit" margin="start-2"/>
|
|
11
|
+
</c:div>
|
|
12
|
+
</c:grid-col>
|
|
13
|
+
<c:grid-col col="12" margin="top-4">
|
|
14
|
+
<c:form-switch name="_addmore" t:label="Add more after submit"/>
|
|
15
|
+
<c:form-switch name="_cloneprev" t:label="Clone previous"/>
|
|
16
|
+
</c:grid-col>
|
|
17
|
+
</c:grid-row>
|
|
18
|
+
</c:form>
|
|
19
|
+
<c:include resource="waibuDb.partial:/crud/_addons.html" />
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<c:div id="main-form">
|
|
2
|
+
<c:include resource="waibuDb.partial:/crud/_form.html" />
|
|
3
|
+
</c:div>
|
|
4
|
+
<c:grid-row gutter="2" margin="top-2">
|
|
5
|
+
<c:grid-col col="6-lg">
|
|
6
|
+
<c:wdb-btn-back />
|
|
7
|
+
<% if (schema.disabled.includes('remove') && schema.disabled.includes('update')) { %>
|
|
8
|
+
<c:wdb-btn-export selector="#main-form" handler="details" launch-margin="start-1"/>
|
|
9
|
+
<% } else { %>
|
|
10
|
+
<c:btn-group margin="start-1">
|
|
11
|
+
<c:wdb-btn-edit />
|
|
12
|
+
<c:wdb-btn-clone />
|
|
13
|
+
<c:wdb-btn-export selector="#main-form" handler="details" launch-on-end/>
|
|
14
|
+
</c:btn-group>
|
|
15
|
+
<% } %>
|
|
16
|
+
</c:grid-col>
|
|
17
|
+
<c:grid-col col="6-lg" flex="justify-content:end-lg align-items:center">
|
|
18
|
+
<c:wdb-btn-delete/>
|
|
19
|
+
</c:grid-col>
|
|
20
|
+
</c:grid-row>
|
|
21
|
+
<c:include resource="waibuDb.partial:/crud/_addons.html" />
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<c:form id="main-form">
|
|
2
|
+
<c:include resource="waibuDb.partial:/crud/_form.html" />
|
|
3
|
+
<c:grid-row gutter="2" margin="top-2">
|
|
4
|
+
<c:grid-col col="6-lg">
|
|
5
|
+
<c:wdb-btn-back />
|
|
6
|
+
<c:btn-group margin="start-1">
|
|
7
|
+
<c:wdb-btn-details />
|
|
8
|
+
<c:wdb-btn-clone/>
|
|
9
|
+
<c:wdb-btn-export selector="#main-form" handler="edit" launch-on-end/>
|
|
10
|
+
</c:btn-group>
|
|
11
|
+
</c:grid-col>
|
|
12
|
+
<c:grid-col col="6-lg" flex="justify-content:end-lg align-items:center">
|
|
13
|
+
<c:wdb-btn-delete/>
|
|
14
|
+
<c:btn type="reset" color="secondary" t:content="Reset" margin="start-2"/>
|
|
15
|
+
<c:btn type="submit" color="primary" t:content="Submit" margin="start-2"/>
|
|
16
|
+
</c:grid-col>
|
|
17
|
+
</c:grid-row>
|
|
18
|
+
</c:form>
|
|
19
|
+
<c:include resource="waibuDb.partial:/crud/_addons.html" />
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<c:grid-row gutter="3" margin="bottom-3">
|
|
2
|
+
<c:grid-col col="4-lg">
|
|
3
|
+
<c:wdb-query />
|
|
4
|
+
</c:grid-col>
|
|
5
|
+
<c:grid-col col="8-lg" flex="justify-content:end-lg">
|
|
6
|
+
<c:btn-group margin="end-2">
|
|
7
|
+
<c:wdb-btn-add text="nowrap"/>
|
|
8
|
+
<c:wdb-btn-edit on-list menu="end"/>
|
|
9
|
+
</c:btn-group>
|
|
10
|
+
<c:wdb-btn-delete on-list margin="end-2" />
|
|
11
|
+
<c:btn-group>
|
|
12
|
+
<c:wdb-btn-export selector="#main-table" handler="list"/>
|
|
13
|
+
<c:wdb-btn-columns menu="end"/>
|
|
14
|
+
</c:btn-group>
|
|
15
|
+
</c:grid-col>
|
|
16
|
+
</c:grid-row>
|
|
17
|
+
<c:wdb-table id="main-table" border body-divider strip responsive />
|
|
18
|
+
<c:grid-row gutter="3" margin="top-2">
|
|
19
|
+
<c:grid-col col="6-lg">
|
|
20
|
+
<c:wdb-recs-info pages recs-per-page recs-per-page-values="10 15 25 50" />
|
|
21
|
+
</c:grid-col>
|
|
22
|
+
<c:grid-col col="6-lg" flex="justify-content:end-lg">
|
|
23
|
+
<c:wdb-pagination />
|
|
24
|
+
</c:grid-col>
|
|
25
|
+
</c:grid-row>
|
|
26
|
+
<c:include resource="waibuDb.partial:/crud/_addons.html" />
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const action = {
|
|
2
|
+
method: ['GET', 'POST'],
|
|
3
|
+
title: 'Database Export',
|
|
4
|
+
handler: async function (req, reply) {
|
|
5
|
+
const { importModule } = this.app.bajo
|
|
6
|
+
const handler = await importModule('waibuDb:/lib/crud/all-handler.js')
|
|
7
|
+
const model = 'SumbaUser'
|
|
8
|
+
const { action } = req.params
|
|
9
|
+
const template = `sumba.template:/crud/${action}.html`
|
|
10
|
+
return handler.call(this, { model, req, reply, action, template })
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default action
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<c:div margin="top-5">
|
|
2
|
+
<c:heading type="3-display" t:content="Action Disabled" margin="bottom-5"/>
|
|
3
|
+
<c:div>
|
|
4
|
+
<c:t value="<%= action %>">Sorry, action '%s' is permanently disabled at database level. For more information,
|
|
5
|
+
please contact your Admin immediately, thank you!</c:t>
|
|
6
|
+
</c:t>
|
|
7
|
+
</c:div>
|
|
8
|
+
</c:div>
|