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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -0
  3. package/bajo/.alias +1 -0
  4. package/bajo/config.json +17 -0
  5. package/bajo/method/admin-menu.js +46 -0
  6. package/bajo/method/get-params.js +18 -0
  7. package/bajo/method/get-schema-ext.js +133 -0
  8. package/bajo/method/method-map.js +9 -0
  9. package/bajo/method/record/create.js +12 -0
  10. package/bajo/method/record/find-one.js +20 -0
  11. package/bajo/method/record/find.js +26 -0
  12. package/bajo/method/record/get.js +14 -0
  13. package/bajo/method/record/remove.js +10 -0
  14. package/bajo/method/record/update.js +12 -0
  15. package/bajo/method/stat/aggregate.js +13 -0
  16. package/bajo/method/stat/histogram.js +13 -0
  17. package/bajoI18N/resource/en-US.json +18 -0
  18. package/bajoI18N/resource/id.json +40 -0
  19. package/lib/crud/add-handler.js +39 -0
  20. package/lib/crud/all-handler.js +31 -0
  21. package/lib/crud/delete-handler.js +37 -0
  22. package/lib/crud/details-handler.js +18 -0
  23. package/lib/crud/edit-handler.js +33 -0
  24. package/lib/crud/export-handler.js +15 -0
  25. package/lib/crud/helper/add-ons-handler.js +46 -0
  26. package/lib/crud/helper/build-params.js +14 -0
  27. package/lib/crud/list-handler.js +27 -0
  28. package/lib/prep-crud.js +17 -0
  29. package/package.json +30 -0
  30. package/waibuBootstrap/theme/component/btn-add.js +15 -0
  31. package/waibuBootstrap/theme/component/btn-back.js +13 -0
  32. package/waibuBootstrap/theme/component/btn-clone.js +30 -0
  33. package/waibuBootstrap/theme/component/btn-columns.js +45 -0
  34. package/waibuBootstrap/theme/component/btn-delete.js +43 -0
  35. package/waibuBootstrap/theme/component/btn-details.js +32 -0
  36. package/waibuBootstrap/theme/component/btn-edit.js +56 -0
  37. package/waibuBootstrap/theme/component/btn-export.js +129 -0
  38. package/waibuBootstrap/theme/component/echarts.js +58 -0
  39. package/waibuBootstrap/theme/component/pagination.js +60 -0
  40. package/waibuBootstrap/theme/component/query.js +137 -0
  41. package/waibuBootstrap/theme/component/recs-info.js +50 -0
  42. package/waibuBootstrap/theme/component/table.js +161 -0
  43. package/waibuMpa/extend/waibuAdmin/route/@model/@action.js +14 -0
  44. package/waibuMpa/partial/crud/_addons.html +5 -0
  45. package/waibuMpa/partial/crud/_form.html +12 -0
  46. package/waibuMpa/partial/crud/add-handler.html +19 -0
  47. package/waibuMpa/partial/crud/details-handler.html +21 -0
  48. package/waibuMpa/partial/crud/echarts-window.html +9 -0
  49. package/waibuMpa/partial/crud/edit-handler.html +19 -0
  50. package/waibuMpa/partial/crud/list-handler.html +26 -0
  51. package/waibuMpa/sumba/route/export/@action.js +14 -0
  52. package/waibuMpa/template/crud/add.html +2 -0
  53. package/waibuMpa/template/crud/details.html +2 -0
  54. package/waibuMpa/template/crud/edit.html +2 -0
  55. package/waibuMpa/template/crud/list.html +2 -0
  56. package/waibuMpa/template/disabled.html +8 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ardhi Lukianto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # waibu-db
2
+
3
+ Plugin name: **waibuDemo**, alias: **wdb**
4
+
5
+ ![GitHub package.json version](https://img.shields.io/github/package-json/v/ardhi/waibu-db) ![NPM Version](https://img.shields.io/npm/v/waibu-db)
6
+
7
+ > <br />**Attention**: I do NOT accept any pull request at the moment, thanks!<br /><br />
8
+
9
+ DB Helper for [Waibu MPA](https://github.com/ardhi/waibu-mpa)
10
+
11
+ ## Installation
12
+
13
+ Goto your ```<bajo-base-dir>``` and type:
14
+
15
+ ```bash
16
+ $ npm install waibu-db
17
+ ```
18
+
19
+ Now open your ```<bajo-data-dir>/config/.plugins``` and put ```waibu-db``` in it
20
+
21
+ ## License
22
+
23
+ [MIT](LICENSE)
package/bajo/.alias ADDED
@@ -0,0 +1 @@
1
+ wdb
@@ -0,0 +1,17 @@
1
+ {
2
+ "waibu": {
3
+ "prefix": "db",
4
+ "title": "Model Database"
5
+ },
6
+ "waibuAdmin": {
7
+ "menuHandler": "waibuDb:adminMenu"
8
+ },
9
+ "waibuMpa": {
10
+ "icon": "database"
11
+ },
12
+ "dbModel": {
13
+ "count": false,
14
+ "patchEnabled": false
15
+ },
16
+ "dependencies": ["waibu", "dobo"]
17
+ }
@@ -0,0 +1,46 @@
1
+ function modelsMenu (req) {
2
+ const { getPluginPrefix } = this.app.waibu
3
+ const { titleize, pascalCase } = this.app.bajo
4
+ const { getAppTitle } = this.app.waibuMpa
5
+ const { map, pick, groupBy, keys, kebabCase, filter, get } = this.app.bajo.lib._
6
+
7
+ const prefix = getPluginPrefix(this.name)
8
+ const schemas = filter(this.app.dobo.schemas, s => {
9
+ const byModelFind = !s.disabled.includes('find')
10
+ let modelDisabled = get(this, `app.${s.ns}.config.waibuAdmin.modelDisabled`)
11
+ if (modelDisabled) {
12
+ const allModels = map(filter(this.app.dobo.schemas, { ns: s.ns }), 'name')
13
+ if (modelDisabled === 'all') modelDisabled = allModels
14
+ else modelDisabled = map(modelDisabled, m => pascalCase(`${this.app[s.ns].alias} ${m}`))
15
+ } else modelDisabled = []
16
+ const byDbDisabled = !modelDisabled.includes(s.name)
17
+ return byModelFind && byDbDisabled
18
+ })
19
+ const omenu = groupBy(map(schemas, s => {
20
+ const item = pick(s, ['name', 'ns'])
21
+ item.nsTitle = getAppTitle(s.ns)
22
+ return item
23
+ }), 'nsTitle')
24
+ const menu = []
25
+ for (const k of keys(omenu).sort()) {
26
+ const items = omenu[k]
27
+ const plugin = this.app[items[0].ns]
28
+ menu.push({
29
+ name: k,
30
+ children: map(items, item => {
31
+ return {
32
+ name: titleize(item.name.slice(plugin.alias.length)),
33
+ href: `waibuAdmin:/${prefix}/${kebabCase(item.name)}/list`
34
+ }
35
+ })
36
+ })
37
+ }
38
+ return menu
39
+ }
40
+
41
+ async function adminMenu (locals, req) {
42
+ const { buildAccordionMenu } = this.app.waibuAdmin
43
+ return buildAccordionMenu(modelsMenu.call(this, locals, req), locals, req)
44
+ }
45
+
46
+ export default adminMenu
@@ -0,0 +1,18 @@
1
+ function getParams (req, ...items) {
2
+ const { map, trim, get } = this.app.bajo.lib._
3
+ let fields
4
+ req.query = req.query ?? {}
5
+ req.params = req.params ?? {}
6
+ if (req.query.fields) fields = map((req.query.fields ?? '').split(','), i => trim(i))
7
+ const params = {
8
+ fields,
9
+ count: get(this, 'config.dbModel.count', false),
10
+ body: req.body
11
+ }
12
+ items.forEach(i => {
13
+ params[i] = req.params[i]
14
+ })
15
+ return params
16
+ }
17
+
18
+ export default getParams
@@ -0,0 +1,133 @@
1
+ import path from 'path'
2
+
3
+ const defReadonly = ['id', 'createdAt', 'updatedAt']
4
+
5
+ function getCommons (action, schema, ext, opts = {}) {
6
+ const { map, get, set, without, uniq } = this.app.bajo.lib._
7
+ const hidden = get(ext, `view.${action}.hidden`, get(ext, 'common.hidden', []))
8
+ hidden.push(...schema.hidden, ...(opts.hidden ?? []))
9
+ const allFields = without(map(schema.properties, 'name'), ...hidden)
10
+ const forFields = get(ext, `view.${action}.fields`, get(ext, 'common.fields', allFields))
11
+ set(schema, 'view.stat.aggregate', get(ext, `view.${action}.stat.aggregate`, get(ext, 'common.stat.aggregate', [])))
12
+ set(schema, 'view.disabled', get(ext, 'disabled', []))
13
+ if (schema.disabled.length > 0) schema.view.disabled.push(...schema.disabled)
14
+ let fields = []
15
+ for (const f of forFields) {
16
+ if (allFields.includes(f)) fields.push(f)
17
+ }
18
+ fields = uniq(without(fields, ...hidden))
19
+ if (action !== 'add' && !fields.includes('id')) fields.unshift('id')
20
+ return { fields, allFields }
21
+ }
22
+
23
+ function autoLayout ({ action, schema, ext, layout, allWidgets }) {
24
+ const matches = ['id', 'createdAt', 'updatedAt']
25
+ const meta = []
26
+ const general = []
27
+ for (const w of allWidgets) {
28
+ if (matches.includes(w.name)) meta.push(w)
29
+ else general.push(w)
30
+ }
31
+ if (meta.length <= 1) layout.push({ name: '_common', widgets: allWidgets })
32
+ else {
33
+ layout.push({ name: 'Meta', widgets: meta })
34
+ layout.push({ name: 'General', widgets: general })
35
+ }
36
+ }
37
+
38
+ function customLayout ({ action, schema, ext, layout, allWidgets, readonly }) {
39
+ const { find, omit, merge, isString } = this.app.bajo.lib._
40
+ const items = [...layout]
41
+ layout.splice(0, layout.length)
42
+ for (const item of items) {
43
+ const widgets = []
44
+ for (let f of item.fields) {
45
+ if (isString(f)) {
46
+ const [name, col, label] = f.split(':')
47
+ f = { name, col, label }
48
+ }
49
+ const widget = find(allWidgets, { name: f.name })
50
+ if (!widget && !f.component) continue
51
+ widget.attr = merge({}, widget.attr, omit(f, ['component']))
52
+ if (f.component && !readonly.includes(f.name) && action !== 'details') widget.component = f.component
53
+ widgets.push(widget)
54
+ }
55
+ if (widgets.length > 0) layout.push({ name: item.name, widgets })
56
+ }
57
+ }
58
+
59
+ function applyLayout (action, schema, ext) {
60
+ const { set, get, isEmpty, map, find } = this.app.bajo.lib._
61
+ const { fields } = getCommons.call(this, action, schema, ext)
62
+ const layout = get(ext, `view.${action}.layout`, get(ext, 'common.layout', []))
63
+ const readonly = get(ext, `view.${action}.readonly`, get(ext, 'common.readonly', defReadonly))
64
+ const allWidgets = map(fields, f => {
65
+ const prop = find(schema.properties, { name: f })
66
+ const result = { name: f, component: 'form-input', attr: { col: '4-md' } }
67
+ if (['array', 'object', 'text'].includes(prop.type)) {
68
+ result.attr.col = '12'
69
+ result.component = 'form-textarea'
70
+ result.attr.rows = '3'
71
+ }
72
+ if (action === 'details') {
73
+ result.component = 'form-plaintext'
74
+ } else {
75
+ if (prop.type === 'boolean') {
76
+ result.component = 'form-select'
77
+ result.attr.options = 'false:No true:Yes'
78
+ }
79
+ if (prop.values) {
80
+ result.component = 'form-select'
81
+ result.attr.options = prop.values.join(' ')
82
+ }
83
+ if (['string', 'text'].includes(prop.type) && prop.maxLength) set(result, 'attr.maxlength', prop.maxLength)
84
+ if (readonly.includes(f)) result.component = 'form-plaintext'
85
+ }
86
+ return result
87
+ })
88
+ if (isEmpty(layout)) autoLayout.call(this, { layout, allWidgets, schema, action, ext })
89
+ else customLayout.call(this, { layout, allWidgets, schema, action, ext, readonly })
90
+ set(schema, 'view.layout', layout)
91
+ set(schema, 'view.fields', fields)
92
+ }
93
+
94
+ const handler = {
95
+ list: async function (schema, ext, opts) {
96
+ const { get, set } = this.app.bajo.lib._
97
+ const { fields } = getCommons.call(this, 'list', schema, ext, opts)
98
+ const qsFields = []
99
+ for (const f of get(schema, 'view.qs.fields', '').split(',')) {
100
+ if (fields.includes(f)) qsFields.push(f)
101
+ }
102
+ let [col, dir] = get(schema, 'view.qs.sort', '').split(':')
103
+ if (!fields.includes(col) || !col) col = 'id'
104
+ if (!['1', '-1'].includes(dir)) dir = '1'
105
+ set(schema, 'view.fields', fields)
106
+ set(schema, 'view.qs.fields', qsFields.join(','))
107
+ set(schema, 'view.qs.sort', `${col}:${dir}`)
108
+ },
109
+ details: async function (schema, ext, opts) {
110
+ applyLayout.call(this, 'details', schema, ext, opts)
111
+ },
112
+ add: async function (schema, ext, opts) {
113
+ applyLayout.call(this, 'add', schema, ext, opts)
114
+ },
115
+ edit: async function (schema, ext, opts) {
116
+ applyLayout.call(this, 'edit', schema, ext, opts)
117
+ }
118
+ }
119
+
120
+ async function getSchemaExt (model, view, opts) {
121
+ const { readConfig } = this.app.bajo
122
+ const { getSchema } = this.app.dobo
123
+ const { pick } = this.app.bajo.lib._
124
+
125
+ let schema = getSchema(model)
126
+ const base = path.basename(schema.file, path.extname(schema.file))
127
+ const ext = await readConfig(`${schema.ns}:/waibuDb/schema/${base}.*`, { ignoreError: true })
128
+ await handler[view].call(this, schema, ext, opts)
129
+ schema = pick(schema, ['name', 'properties', 'indexes', 'disabled', 'attachment', 'sortables', 'view'])
130
+ return { schema, ext }
131
+ }
132
+
133
+ export default getSchemaExt
@@ -0,0 +1,9 @@
1
+ const methodMap = {
2
+ create: 'POST',
3
+ find: 'GET',
4
+ get: 'GET',
5
+ update: 'PUT',
6
+ remove: 'DELETE'
7
+ }
8
+
9
+ export default methodMap
@@ -0,0 +1,12 @@
1
+ import prepCrud from '../../../lib/prep-crud.js'
2
+
3
+ async function create ({ model, req, reply, body, options = {} }) {
4
+ const { recordCreate, attachmentFind } = this.app.dobo
5
+ const { name, input, opts } = prepCrud.call(this, { model, req, body, options, args: ['model'] })
6
+ const ret = await recordCreate(name, input, opts)
7
+ const { attachment, stats, mimeType } = req.query
8
+ if (attachment) ret.data._attachment = await attachmentFind(name, ret.data.id, { stats, mimeType })
9
+ return ret
10
+ }
11
+
12
+ export default create
@@ -0,0 +1,20 @@
1
+ import prepCrud from '../../../lib/prep-crud.js'
2
+
3
+ async function find ({ model, req, reply, options = {} }) {
4
+ const { recordFindOne, attachmentFind } = this.app.dobo
5
+ const { parseFilter } = this.app.waibu
6
+ const { name, opts } = prepCrud.call(this, { model, req, options, args: ['model'] })
7
+ const cfgWeb = this.app.waibu.config
8
+ opts.bboxLatField = req.query[cfgWeb.qsKey.bboxLatField]
9
+ opts.bboxLngField = req.query[cfgWeb.qsKey.bboxLngField]
10
+ const filter = parseFilter(req)
11
+ const ret = await recordFindOne(name, filter, opts)
12
+ ret.filter = filter
13
+ const { attachment, stats, mimeType } = req.query
14
+ if (attachment) {
15
+ ret.data._attachment = await attachmentFind(name, ret.data.id, { stats, mimeType })
16
+ }
17
+ return ret
18
+ }
19
+
20
+ export default find
@@ -0,0 +1,26 @@
1
+ import prepCrud from '../../../lib/prep-crud.js'
2
+
3
+ async function find ({ model, req, reply, options = {} }) {
4
+ const { recordFind, attachmentFind } = this.app.dobo
5
+ const { cloneDeep } = this.app.bajo.lib._
6
+ const { name, opts } = prepCrud.call(this, { model, req, options, args: ['model'] })
7
+ const { parseFilter } = this.app.waibu
8
+ const cfgWeb = this.app.waibu.config
9
+ opts.bboxLatField = req.query[cfgWeb.qsKey.bboxLatField]
10
+ opts.bboxLngField = req.query[cfgWeb.qsKey.bboxLngField]
11
+ const filter = parseFilter(req)
12
+ if (options.query) {
13
+ filter.query = cloneDeep(options.query)
14
+ delete options.query
15
+ }
16
+ const ret = await recordFind(name, filter, opts)
17
+ const { attachment, stats, mimeType } = req.query
18
+ if (attachment) {
19
+ for (const d of ret.data) {
20
+ d._attachment = await attachmentFind(name, d.id, { stats, mimeType })
21
+ }
22
+ }
23
+ return ret
24
+ }
25
+
26
+ export default find
@@ -0,0 +1,14 @@
1
+ import prepCrud from '../../../lib/prep-crud.js'
2
+
3
+ async function get ({ model, req, reply, id, options = {} }) {
4
+ const { recordGet, attachmentFind } = this.app.dobo
5
+ const { parseFilter } = this.app.waibu
6
+ const { name, recId, opts } = prepCrud.call(this, { model, req, id, options, args: ['model', 'id'] })
7
+ opts.filter = parseFilter(req)
8
+ const ret = await recordGet(name, recId, opts)
9
+ const { attachment, stats, mimeType } = req.query
10
+ if (attachment) ret.data._attachment = await attachmentFind(name, id, { stats, mimeType })
11
+ return ret
12
+ }
13
+
14
+ export default get
@@ -0,0 +1,10 @@
1
+ import prepCrud from '../../../lib/prep-crud.js'
2
+
3
+ async function remove ({ model, req, reply, id, options = {} }) {
4
+ const { recordRemove } = this.app.dobo
5
+ const { name, recId, opts } = prepCrud.call(this, { model, req, id, options, args: ['model', 'id'] })
6
+ const result = await recordRemove(name, recId, opts)
7
+ return result
8
+ }
9
+
10
+ export default remove
@@ -0,0 +1,12 @@
1
+ import prepCrud from '../../../lib/prep-crud.js'
2
+
3
+ async function update ({ model, req, reply, id, body, options = {} }) {
4
+ const { recordUpdate, attachmentFind } = this.app.dobo
5
+ const { name, input, opts, recId } = prepCrud.call(this, { model, req, body, id, options, args: ['model', 'id'] })
6
+ const ret = await recordUpdate(name, recId, input, opts)
7
+ const { attachment, stats, mimeType } = req.query
8
+ if (attachment) ret.data._attachment = await attachmentFind(name, id, { stats, mimeType })
9
+ return ret
10
+ }
11
+
12
+ export default update
@@ -0,0 +1,13 @@
1
+ import prepCrud from '../../../lib/prep-crud.js'
2
+
3
+ async function aggregate ({ model, req, reply, options = {} }) {
4
+ const { statAggregate } = this.app.dobo
5
+ const { parseFilter } = this.app.waibu
6
+ const { name, opts } = prepCrud.call(this, { model, req, options, args: ['model'] })
7
+ for (const item of ['group', 'aggregate']) {
8
+ opts[item] = options[item] ?? req.params[item] ?? req.query[item]
9
+ }
10
+ return await statAggregate(name, parseFilter(req), opts)
11
+ }
12
+
13
+ export default aggregate
@@ -0,0 +1,13 @@
1
+ import prepCrud from '../../../lib/prep-crud.js'
2
+
3
+ async function histogram ({ model, req, reply, options = {} }) {
4
+ const { statHistogram } = this.app.dobo
5
+ const { parseFilter } = this.app.waibu
6
+ const { name, opts } = prepCrud.call(this, { model, req, options, args: ['model'] })
7
+ for (const item of ['type', 'group', 'aggregate']) {
8
+ opts[item] = options[item] ?? req.params[item] ?? req.query[item]
9
+ }
10
+ return await statHistogram(name, parseFilter(req), opts)
11
+ }
12
+
13
+ export default histogram
@@ -0,0 +1,18 @@
1
+ {
2
+ "op": {
3
+ "equals": "Equals",
4
+ "notEquals": "Not Equals",
5
+ "greaterThan": "Greater Than",
6
+ "greaterThanOrEquals": "Greater Than or Equals",
7
+ "lessThan": "Less Than",
8
+ "lessThanOrEquals": "Less Than or Equals",
9
+ "in": "Includes",
10
+ "notIn": "Not Includes",
11
+ "contains": "Contains",
12
+ "notContains": "Not Contains",
13
+ "startsWith": "Starts With",
14
+ "notStartsWith": "Not Starts With",
15
+ "endsWith": "Ends With",
16
+ "notEndsWith": "Not Ends With"
17
+ }
18
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "Page %s of %s pages": "Hal %s dari %s",
3
+ "%s record(s) found": "Ditemukan %s data",
4
+ "recs per page": "data per halaman",
5
+ "Add as New Clone": "Tambah sbg Duplikat",
6
+ "Details": "Detil",
7
+ "Clone": "Gandakan",
8
+ "Delivery": "Penyampaian",
9
+ "Save as File": "Simpan sbg Berkas",
10
+ "Formatted Field": "Kolom Terformat",
11
+ "Formatted Value": "Nilai Terformat",
12
+ "Zipped": "Di kompres",
13
+ "File Type": "Tipe Berkas",
14
+ "You're about to remove one or more records. Are you really sure to do this?": "Anda akan menghapus satu atau lebih data. Anda yakin akan melakukannya?",
15
+ "Columns": "Kolom",
16
+ "No statistic yet, sorry": "Maaf, belum ada statistik",
17
+ "%s - %s": "%s - %s",
18
+ "List": "Daftar",
19
+ "Add": "Tambah",
20
+ "Edit": "Ubah",
21
+ "Yes": "Ya",
22
+ "No": "Tidak",
23
+ "Db": "DB",
24
+ "op": {
25
+ "equals": "Sama Dengan",
26
+ "notEquals": "Tidak Sama Dengan",
27
+ "greaterThan": "Lebih Besar Dari",
28
+ "greaterThanOrEquals": "Lebih Besar Dari atau Sama Dengan",
29
+ "lessThan": "Lebih Kecil Dari",
30
+ "lessThanOrEquals": "Lebih Kecil Dari atau Sama Dengan",
31
+ "in": "Termasuk",
32
+ "notIn": "Tidak Termasuk",
33
+ "contains": "Mengandung",
34
+ "notContains": "Tidak Mengandung",
35
+ "startsWith": "Dimulai Dengan",
36
+ "notStartsWith": "Tidak Dimulai Dengan",
37
+ "endsWith": "Diakhiri Dengan",
38
+ "notEndsWith": "Tidak Diakhiri Dengan"
39
+ }
40
+ }
@@ -0,0 +1,39 @@
1
+ async function addHandler ({ req, reply, model, params = {}, template, addOnsHandler, templateDisabled = 'waibuDb.template:/disabled.html' } = {}) {
2
+ const { pascalCase } = this.app.bajo
3
+ const { recordCreate, recordGet, getSchemaExt } = this.app.waibuDb
4
+ const { buildUrl } = this.app.waibuMpa
5
+ const { pick, map, merge, defaultsDeep, omit, isEmpty } = this.app.bajo.lib._
6
+ const options = {}
7
+ model = model ?? pascalCase(req.params.model)
8
+ const { schema } = await getSchemaExt(model, 'add', options)
9
+ if (schema.disabled.includes('create')) return reply.view(templateDisabled, { action: 'add' })
10
+ // req.query.attachment = true
11
+ options.fields = schema.view.fields
12
+ let def = {}
13
+ if (req.method === 'GET' && req.query.mode === 'clone' && req.query.id) {
14
+ const resp = await recordGet({ model, req, id: req.query.id, options: { fields: map(schema.properties, 'name') } })
15
+ def = omit(resp.data, ['id', 'createdAt', 'updatedAt'])
16
+ }
17
+ let form = defaultsDeep(req.body, def)
18
+ let error
19
+ let resp
20
+ if (req.method === 'POST') {
21
+ req.session[`wdb${model}AddMore`] = form._addmore
22
+ req.session[`wdb${model}ClonePrev`] = form._cloneprev
23
+ try {
24
+ resp = await recordCreate({ model, req, reply, options })
25
+ if (isEmpty(form._addmore)) return reply.redirectTo(buildUrl({ url: req.url, base: 'list', params: { page: 1 }, exclude: ['id', 'mode'] }))
26
+ if (isEmpty(form._cloneprev)) form = pick(form, ['_addmore', '_cloneprev'])
27
+ } catch (err) {
28
+ error = err
29
+ }
30
+ } else {
31
+ form._addmore = req.session[`wdb${model}AddMore`]
32
+ form._cloneprev = req.session[`wdb${model}ClonePrev`]
33
+ }
34
+ const addOns = addOnsHandler ? await addOnsHandler.call(this.app[req.ns], { req, reply, params, data: resp, schema, error }) : undefined
35
+ merge(params, { form, schema, error, addOns })
36
+ return reply.view(template, params)
37
+ }
38
+
39
+ export default addHandler
@@ -0,0 +1,31 @@
1
+ import buildParams from './helper/build-params.js'
2
+ import addOnsHandler from './helper/add-ons-handler.js'
3
+
4
+ import addHandler from './add-handler.js'
5
+ import deleteHandler from './delete-handler.js'
6
+ import detailsHandler from './details-handler.js'
7
+ import editHandler from './edit-handler.js'
8
+ import exportHandler from './export-handler.js'
9
+ import listHandler from './list-handler.js'
10
+
11
+ const handler = {
12
+ add: addHandler,
13
+ delete: deleteHandler,
14
+ details: detailsHandler,
15
+ edit: editHandler,
16
+ export: exportHandler,
17
+ list: listHandler
18
+ }
19
+
20
+ async function allHandler ({ model, action, req, reply, template, params = {} }) {
21
+ const { upperFirst, merge, keys } = this.app.bajo.lib._
22
+ if (!keys(handler).includes(action)) throw this.error('notFound')
23
+ if (['delete', 'export'].includes(action)) {
24
+ if (req.method === 'GET') throw this.error('notFound')
25
+ return await handler[action].call(this, { model, req, reply })
26
+ }
27
+ const allParams = merge(buildParams.call(this, { model, req, reply, action: upperFirst(action) }), params)
28
+ return await handler[action].call(this, { model, req, reply, params: allParams, template, addOnsHandler })
29
+ }
30
+
31
+ export default allHandler
@@ -0,0 +1,37 @@
1
+ async function deleteHandler ({ req, reply, model, params = {}, templateDisabled = 'waibuDb.template:/disabled.html' } = {}) {
2
+ const { pascalCase } = this.app.bajo
3
+ const { recordRemove, getSchemaExt } = this.app.waibuDb
4
+ const { buildUrl } = this.app.waibuMpa
5
+ const { reduce } = this.app.bajo.lib._
6
+ const options = {}
7
+ model = model ?? pascalCase(req.params.model)
8
+ const { schema } = await getSchemaExt(model, 'add', options)
9
+ if (schema.disabled.includes('remove')) return reply.view(templateDisabled, { action: 'delete' })
10
+ options.fields = schema.view.fields
11
+ const ids = (req.body.ids ?? '').split(',')
12
+ if (ids.length > 0) {
13
+ const result = []
14
+ const options = { noResult: true, noFlash: true }
15
+ for (const id of ids) {
16
+ try {
17
+ await recordRemove({ model, id, req, reply, options })
18
+ result.push(true)
19
+ } catch (err) {
20
+ result.push(err.message)
21
+ }
22
+ }
23
+ const success = reduce(result, (sum, n) => {
24
+ return n === true ? (sum + 1) : sum
25
+ }, 0)
26
+ let type = 'danger'
27
+ if (success > 0) type = 'warning'
28
+ if (success === ids.length) type = 'info'
29
+ req.flash('notify', req.t('%d of %d record(s) successfully removed', success, ids.length) + '\t' + type)
30
+ req.query.page = 1
31
+ }
32
+
33
+ const url = buildUrl({ url: req.url, base: 'list', params: { page: 1 } })
34
+ return reply.redirectTo(url)
35
+ }
36
+
37
+ export default deleteHandler
@@ -0,0 +1,18 @@
1
+ async function detailsHandler ({ req, reply, model, params = {}, template, addOnsHandler, templateDisabled = 'waibuDb.template:/disabled.html' } = {}) {
2
+ const { pascalCase } = this.app.bajo
3
+ const { recordGet, getSchemaExt } = this.app.waibuDb
4
+ const { merge } = this.app.bajo.lib._
5
+ const options = {}
6
+ model = model ?? pascalCase(req.params.model)
7
+ const { schema } = await getSchemaExt(model, 'details', options)
8
+ if (schema.disabled.includes('get')) return reply.view(templateDisabled, { action: 'details' })
9
+ // req.query.attachment = true
10
+ options.fields = schema.view.fields
11
+ const resp = await recordGet({ model, req, options })
12
+ const form = resp.data
13
+ const addOns = addOnsHandler ? await addOnsHandler.call(this.app[req.ns], { req, reply, params, data: resp, schema }) : undefined
14
+ merge(params, { form, schema, addOns })
15
+ return reply.view(template, params)
16
+ }
17
+
18
+ export default detailsHandler
@@ -0,0 +1,33 @@
1
+ async function editHandler ({ req, reply, model, params = {}, template, addOnsHandler, templateDisabled = 'waibuDb.template:/disabled.html' } = {}) {
2
+ const { pascalCase } = this.app.bajo
3
+ const { recordUpdate, recordGet, getSchemaExt } = this.app.waibuDb
4
+ const { buildUrl } = this.app.waibuMpa
5
+ const { merge, defaultsDeep } = this.app.bajo.lib._
6
+ const options = {}
7
+ model = model ?? pascalCase(req.params.model)
8
+ const { schema } = await getSchemaExt(model, 'edit', options)
9
+ if (schema.disabled.includes('update')) return reply.view(templateDisabled, { action: 'edit' })
10
+ // req.query.attachment = true
11
+ options.fields = schema.view.fields
12
+ let error
13
+ let resp
14
+ let form
15
+ if (req.method === 'GET') {
16
+ const old = await recordGet({ model, req, id: req.query.id, options })
17
+ form = defaultsDeep(req.body, old.data)
18
+ } else {
19
+ form = req.body
20
+ try {
21
+ resp = await recordUpdate({ model, req, id: req.query.id, reply, options })
22
+ form = resp.data
23
+ return reply.redirectTo(buildUrl({ url: req.url, base: 'list', params: { page: 1 }, exclude: ['id'] }))
24
+ } catch (err) {
25
+ error = err
26
+ }
27
+ }
28
+ const addOns = addOnsHandler ? await addOnsHandler.call(this.app[req.ns], { req, reply, params, data: resp, schema, error }) : undefined
29
+ merge(params, { form, schema, error, addOns })
30
+ return reply.view(template, params)
31
+ }
32
+
33
+ export default editHandler
@@ -0,0 +1,15 @@
1
+ async function exportHandler ({ req, reply, model, params = {}, templateDisabled = 'waibuDb.template:/disabled.html' } = {}) {
2
+ const { pascalCase } = this.app.bajo
3
+ const { getSchemaExt } = this.app.waibuDb
4
+ const { buildUrl } = this.app.waibuMpa
5
+ const options = {}
6
+ model = model ?? pascalCase(req.params.model)
7
+ const { schema } = await getSchemaExt(model, 'add', options)
8
+ if (schema.disabled.includes('find')) return reply.view(templateDisabled, { action: 'list' })
9
+ options.fields = schema.view.fields
10
+ const url = buildUrl({ url: req.url, base: req.body.handler })
11
+ req.flash('notify', req.t('Data export in queue. You\'ll be notified once completed'))
12
+ return reply.redirectTo(url)
13
+ }
14
+
15
+ export default exportHandler