@agung_dhewe/webapps 1.1.2

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 (130) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +2 -0
  3. package/jsconfig.json +10 -0
  4. package/libs/fgta5js-dist/fgta5js-v1.8.3.min.css +2 -0
  5. package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js +11 -0
  6. package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js.map +1 -0
  7. package/libs/fgta5js-dist/fonts/karla-italic-latin-ext.woff2 +0 -0
  8. package/libs/fgta5js-dist/fonts/karla-italic-latin.woff2 +0 -0
  9. package/libs/fgta5js-dist/fonts/karla-normal-latin-ext.woff2 +0 -0
  10. package/libs/fgta5js-dist/fonts/karla-normal-latin.woff2 +0 -0
  11. package/libs/fgta5js-dist/fonts/karla.css +142 -0
  12. package/libs/webmodule/module-edit.css +163 -0
  13. package/libs/webmodule/module-footer.css +22 -0
  14. package/libs/webmodule/module-list.css +25 -0
  15. package/libs/webmodule/module.css +52 -0
  16. package/libs/webmodule/module.js +195 -0
  17. package/libs/webmodule/pagehelper.mjs +45 -0
  18. package/modules/generator/appgen-components.mjs +142 -0
  19. package/modules/generator/appgen-icons.mjs +6 -0
  20. package/modules/generator/appgen-io.mjs +784 -0
  21. package/modules/generator/appgen-ui-search.mjs +173 -0
  22. package/modules/generator/appgen-ui-unique.mjs +153 -0
  23. package/modules/generator/appgen-ui.mjs +1181 -0
  24. package/modules/generator/generator-context.mjs +18 -0
  25. package/modules/generator/generator-designtemplate.html +1508 -0
  26. package/modules/generator/generator-ext.html +0 -0
  27. package/modules/generator/generator-ext.mjs +3 -0
  28. package/modules/generator/generator.css +642 -0
  29. package/modules/generator/generator.mjs +195 -0
  30. package/modules/generator/generator.png +0 -0
  31. package/modules/generator/generatorEdit.html +185 -0
  32. package/modules/generator/generatorEdit.mjs +238 -0
  33. package/modules/generator/generatorList.html +32 -0
  34. package/modules/generator/generatorList.mjs +243 -0
  35. package/modules/login/login.css +11 -0
  36. package/modules/login/login.html +12 -0
  37. package/modules/login/login.mjs +111 -0
  38. package/package.json +46 -0
  39. package/percobaan/simmpan-ke-minio.js +24 -0
  40. package/src/api.js +80 -0
  41. package/src/apis/generator.api.js +226 -0
  42. package/src/apis/login.api.js +109 -0
  43. package/src/bucket.js +24 -0
  44. package/src/context.js +26 -0
  45. package/src/datalog.sql +22 -0
  46. package/src/datarecords.js +0 -0
  47. package/src/db.js +61 -0
  48. package/src/generator/createApiExtenderModule.js +54 -0
  49. package/src/generator/createApiModule.js +218 -0
  50. package/src/generator/createIcon.js +62 -0
  51. package/src/generator/createInfoAboutExtender.js +42 -0
  52. package/src/generator/createInfoLogs.js +41 -0
  53. package/src/generator/createInfoRecordExtender.js +41 -0
  54. package/src/generator/createModuleContext.js +48 -0
  55. package/src/generator/createModuleDetilEditHtml.js +110 -0
  56. package/src/generator/createModuleDetilEditMjs.js +172 -0
  57. package/src/generator/createModuleDetilListHtml.js +146 -0
  58. package/src/generator/createModuleDetilListMjs.js +73 -0
  59. package/src/generator/createModuleEjs.js +51 -0
  60. package/src/generator/createModuleExtenderHtml.js +43 -0
  61. package/src/generator/createModuleExtenderMjs.js +43 -0
  62. package/src/generator/createModuleHeaderEditHtml.js +148 -0
  63. package/src/generator/createModuleHeaderEditMjs.js +197 -0
  64. package/src/generator/createModuleHeaderListHtml.js +144 -0
  65. package/src/generator/createModuleHeaderListMjs.js +67 -0
  66. package/src/generator/createModuleMjs.js +67 -0
  67. package/src/generator/createModuleRollup.js +42 -0
  68. package/src/generator/createProgramData.js +96 -0
  69. package/src/generator/createTable.js +156 -0
  70. package/src/generator/ddl.js +475 -0
  71. package/src/generator/helper.js +149 -0
  72. package/src/generator/templates/__rollup-module.ejs +90 -0
  73. package/src/generator/templates/api-extender-module.js.ejs +0 -0
  74. package/src/generator/templates/api-module.js.ejs +818 -0
  75. package/src/generator/templates/module-context.ejs +16 -0
  76. package/src/generator/templates/module-ext-about.ejs +1 -0
  77. package/src/generator/templates/module-ext-record.ejs +1 -0
  78. package/src/generator/templates/module-ext.html.ejs +3 -0
  79. package/src/generator/templates/module-ext.mjs.ejs +21 -0
  80. package/src/generator/templates/module-logs.ejs +14 -0
  81. package/src/generator/templates/module.ejs.ejs +48 -0
  82. package/src/generator/templates/module.mjs.ejs +256 -0
  83. package/src/generator/templates/moduleDetilEdit.html.ejs +34 -0
  84. package/src/generator/templates/moduleDetilEdit.mjs.ejs +792 -0
  85. package/src/generator/templates/moduleDetilList.html.ejs +26 -0
  86. package/src/generator/templates/moduleDetilList.mjs.ejs +319 -0
  87. package/src/generator/templates/moduleHeaderEdit.html.ejs +53 -0
  88. package/src/generator/templates/moduleHeaderEdit.mjs.ejs +807 -0
  89. package/src/generator/templates/moduleHeaderList.html.ejs +24 -0
  90. package/src/generator/templates/moduleHeaderList.mjs.ejs +308 -0
  91. package/src/generator/templates/sqlAddField.ejs +3 -0
  92. package/src/generator/templates/sqlAddForeignKey.ejs +12 -0
  93. package/src/generator/templates/sqlAddUniqueIndex.ejs +4 -0
  94. package/src/generator/templates/sqlCreateTable.ejs +9 -0
  95. package/src/generator/templates/sqlDropForeignKey.ejs +3 -0
  96. package/src/generator/templates/sqlDropUniqueIndex.ejs +4 -0
  97. package/src/generator/templates/sqlModifyField.ejs +6 -0
  98. package/src/generator/trygenerate.js +83 -0
  99. package/src/generator/worker.js +389 -0
  100. package/src/helper.js +82 -0
  101. package/src/logger.js +39 -0
  102. package/src/router.js +84 -0
  103. package/src/routers/defaultLoginApi.js +29 -0
  104. package/src/routers/defaultLoginAsset.js +18 -0
  105. package/src/routers/defaultLoginPage.js +36 -0
  106. package/src/routers/defaultRootIndex.js +16 -0
  107. package/src/routers/downloadHandler.js +51 -0
  108. package/src/routers/fileUploadApi.js +15 -0
  109. package/src/routers/generatorApi.js +30 -0
  110. package/src/routers/generatorAsset.js +18 -0
  111. package/src/routers/generatorPage.js +37 -0
  112. package/src/routers/handleError.js +43 -0
  113. package/src/routers/handleModuleNotfound.js +12 -0
  114. package/src/routers/moduleApi.js +34 -0
  115. package/src/routers/modulePage.js +102 -0
  116. package/src/sequencerdoc.js +311 -0
  117. package/src/sequencerline.js +214 -0
  118. package/src/session.js +57 -0
  119. package/src/startup.js +59 -0
  120. package/src/webapps.js +239 -0
  121. package/src/workermanager.js +83 -0
  122. package/templates/_lib_debug.ejs +11 -0
  123. package/templates/_lib_production.ejs +5 -0
  124. package/templates/application.page.ejs +143 -0
  125. package/templates/generator.page.ejs +131 -0
  126. package/templates/index.page.ejs +24 -0
  127. package/templates/login.page.ejs +102 -0
  128. package/templates/moduleError.ejs +16 -0
  129. package/templates/moduleNotfound.ejs +14 -0
  130. package/webapps.code-workspace +11 -0
@@ -0,0 +1,792 @@
1
+ import Context from './<%= moduleName %>-context.mjs'
2
+ import * as Extender from './<%= moduleName %>-ext.mjs'
3
+ import * as pageHelper from '/public/libs/webmodule/pagehelper.mjs'
4
+
5
+
6
+ const CurrentState = {}
7
+ const Crsl = Context.Crsl
8
+ const CurrentSectionId = Context.Sections.<%= modulePart %>
9
+ const CurrentSection = Crsl.Items[CurrentSectionId]
10
+ const Source = Context.Source
11
+
12
+ const TitleWhenNew = 'New <%= title %>'
13
+ const TitleWhenView = 'View <%= title %>'
14
+ const TitleWhenEdit = 'Edit <%= title %>'
15
+ const EditModeText = 'Edit'
16
+ const LockModeText = 'Lock'
17
+
18
+
19
+
20
+ const btn_edit = new $fgta5.ActionButton('<%= modulePart %>-btn_edit')
21
+ const btn_save = new $fgta5.ActionButton('<%= modulePart %>-btn_save')
22
+ const btn_new = new $fgta5.ActionButton('<%= modulePart %>-btn_new', '<%= moduleSection %>-addrow')
23
+ const btn_del = new $fgta5.ActionButton('<%= modulePart %>-btn_delete', '<%= moduleSection %>-delrow')
24
+ const btn_reset = new $fgta5.ActionButton('<%= modulePart %>-btn_reset')
25
+ const btn_prev = new $fgta5.ActionButton('<%= modulePart %>-btn_prev')
26
+ const btn_next = new $fgta5.ActionButton('<%= modulePart %>-btn_next')
27
+
28
+ const btn_recordstatus = document.getElementById('<%= moduleSection %>-btn_recordstatus')
29
+ const btn_logs = document.getElementById('<%= moduleSection %>-btn_logs')
30
+
31
+ const frm = new $fgta5.Form('<%= modulePart %>-frm');<% fields.forEach(field => { %>
32
+ const <%= field.inputname %> = frm.Inputs['<%= field.elementId %>']<% }) %>
33
+ const obj_createby = document.getElementById('fRecord-section-createby')
34
+ const obj_createdate = document.getElementById('fRecord-section-createdate')
35
+ const obj_modifyby = document.getElementById('fRecord-section-modifyby')
36
+ const obj_modifydate = document.getElementById('fRecord-section-modifydate')
37
+
38
+ export const Section = CurrentSection
39
+
40
+
41
+ export async function init(self, args) {
42
+
43
+ CurrentSection.addEventListener($fgta5.Section.EVT_BACKBUTTONCLICK, async (evt)=>{
44
+ backToList(self, evt)
45
+ })
46
+
47
+ frm.addEventListener('locked', (evt) => { frm_locked(self, evt) });
48
+ frm.addEventListener('unlocked', (evt) => { frm_unlocked(self, evt) });
49
+ frm.render()
50
+
51
+ btn_edit.addEventListener('click', (evt)=>{ btn_edit_click(self, evt) })
52
+ btn_save.addEventListener('click', (evt)=>{ btn_save_click(self, evt) })
53
+ btn_new.addEventListener('click', (evt)=>{ btn_new_click(self, evt) })
54
+ btn_del.addEventListener('click', (evt)=>{ btn_del_click(self, evt) })
55
+ btn_reset.addEventListener('click', (evt)=>{ btn_reset_click(self, evt)})
56
+ btn_prev.addEventListener('click', (evt)=>{ btn_prev_click(self, evt)})
57
+ btn_next.addEventListener('click', (evt)=>{ btn_next_click(self, evt)})
58
+
59
+
60
+ btn_recordstatus.addEventListener('click', evt=>{ btn_recordstatus_click(self, evt) })
61
+ btn_logs.addEventListener('click', evt=>{ btn_logs_click(self, evt) })
62
+
63
+ CurrentState.headerFormLocked = true
64
+ CurrentState.editDisabled = false
65
+
66
+
67
+ <% fieldHandles.forEach(field => { %>
68
+ // <%= field.component %>: <%= field.inputname %><% field.handles.forEach(hnd => { %>
69
+ <%= field.inputname %>.addEventListener('<%= hnd.eventname %>', <% if (hnd.eventname=='selecting') { %>async <% } %>(evt)=>{
70
+ const fn_<%= hnd.eventname %>_name = '<%= field.inputname %>_<%= hnd.eventname %>'
71
+ const fn_<%= hnd.eventname %> = Extender[fn_<%= hnd.eventname %>_name]
72
+ if (typeof fn_<%= field.inputname %>_<%= hnd.eventname %> === 'function') {
73
+ // create function di Extender<% if (field.component=='Combobox' && hnd.eventname=='selecting') { %> (jika perlu)<% } %>:
74
+ // export async function <%= field.inputname %>_<%= hnd.eventname %>(self, <%= field.inputname %>, frm, evt)
75
+ fn_<%= field.inputname %>_<%= hnd.eventname %>(self, <%= field.inputname %>, frm, evt)
76
+ } else {<% if (field.component=='Combobox' && hnd.eventname=='selecting') { %>
77
+ // default selecting
78
+ const cbo = evt.detail.sender
79
+ const dialog = evt.detail.dialog
80
+ const searchtext = evt.detail.searchtext!=null ? evt.detail.searchtext : ''
81
+ const url = `${Context.appsUrls.<%= hnd.appId %>.url}/<%- hnd.path %>`
82
+ const criteria = {
83
+ searchtext: searchtext,
84
+ }
85
+
86
+ const fn_<%= hnd.eventname %>_criteria_name = '<%= field.inputname %>_<%= hnd.eventname %>_criteria'
87
+ const fn_<%= field.inputname %>_<%= hnd.eventname %>_criteria = Extender[fn_<%= hnd.eventname %>_criteria_name]
88
+ if (typeof fn_<%= hnd.eventname %>_criteria === 'function') {
89
+ fn_<%= field.inputname %>_<%= hnd.eventname %>_criteria(self, <%= field.inputname %>, criteria)
90
+ }
91
+
92
+ cbo.wait()
93
+ try {
94
+ const result = await Module.apiCall(url, {
95
+ criteria,
96
+ offset: evt.detail.offset,
97
+ limit: evt.detail.limit,
98
+ })
99
+
100
+ for (var row of result.data) {
101
+ evt.detail.addRow(row.<%= hnd.field_value %>, row.<%= hnd.field_text %>, row)
102
+ }
103
+
104
+ dialog.setNext(result.nextoffset, result.limit)
105
+ } catch (err) {
106
+ $fgta5.MessageBox.error(err.message)
107
+ } finally {
108
+ cbo.wait(false)
109
+ }
110
+
111
+ <% } else { %>
112
+ console.warn('Extender.<%= field.inputname %>_<%= hnd.eventname %> is not implemented')<% } %>
113
+ }
114
+ })<% }) %>
115
+ <% }) %>
116
+ }
117
+
118
+
119
+ export async function openSelectedData(self, params) {
120
+ console.log('openSelectedData')
121
+
122
+ let mask = $fgta5.Modal.createMask()
123
+ try {
124
+ <% comboboxList.forEach(cbo => { %><%= cbo.inputname %>.clear()
125
+ <% }) %>
126
+ const id = params.keyvalue
127
+ const data = await openData(self, id)
128
+
129
+ <% if (detilHasUpload) { %>
130
+ dataOpened(self, data)<% } %>
131
+
132
+ CurrentState.currentOpenedId = id
133
+
134
+
135
+ // jika posisi header dalam keadaan unlock (bisa edit, perlu cek kondisi data, untuk menentukan bisa diedit atau tidak)
136
+ if (!CurrentState.headerFormLocked) {
137
+ const fn_iseditdisabled_name = '<%= modulePart %>_isEditDisabled'
138
+ const fn_iseditdisabled = Extender[fn_iseditdisabled_name]
139
+ if (typeof fn_iseditdisabled === 'function') {
140
+ const editDisabled = fn_iseditdisabled(self, data)
141
+ CurrentState.editDisabled = editDisabled
142
+ }
143
+ }
144
+
145
+ // disable primary key
146
+ setPrimaryKeyState(self, {disabled:true})
147
+
148
+ frm.setData(data)
149
+ frm.acceptChanges()
150
+ frm.lock()
151
+
152
+ const fn_formopened_name = '<%= modulePart %>_formOpened'
153
+ const fn_formopened = Extender[fn_formopened_name]
154
+ if (typeof fn_formopened === 'function') {
155
+ fn_formopened(self, frm, CurrentState)
156
+ }
157
+ } catch (err) {
158
+ CurrentState.currentOpenedId = null
159
+ throw err
160
+ } finally {
161
+ mask.close()
162
+ mask = null
163
+ }
164
+ }
165
+
166
+ export function getForm(self) {
167
+ return frm
168
+ }
169
+
170
+ export function clearForm(self, text) {
171
+ frm.clear(text)
172
+ }
173
+
174
+ export function headerLocked(self) {
175
+ CurrentState.headerFormLocked = true
176
+ CurrentState.editDisabled = true
177
+ btn_new.disabled = true
178
+ }
179
+
180
+ export function headerUnlocked(self) {
181
+ CurrentState.headerFormLocked = false
182
+ CurrentState.editDisabled = false
183
+ btn_new.disabled = false
184
+ }
185
+
186
+ export function disableNextButton(self, disabled=true) {
187
+ btn_next.disabled = disabled
188
+ }
189
+
190
+ export function disablePrevButton(self, disabled=true) {
191
+ btn_prev.disabled = disabled
192
+ }
193
+
194
+ export function keyboardAction(self, actionName) {
195
+ if (actionName=='save') {
196
+ frm.acceptInput()
197
+ btn_save.click()
198
+ } else if (actionName=='new') {
199
+ frm.acceptInput()
200
+ btn_new.click()
201
+ } else if (actionName=='escape') {
202
+ frm.acceptInput()
203
+ if (frm.isLocked() || frm.isNew()) {
204
+ backToList(self)
205
+ } else {
206
+ btn_edit.click() // untuk lock data
207
+ }
208
+ } else if (actionName=='togleEdit') {
209
+ frm.acceptInput()
210
+ btn_edit.click()
211
+ } else if (actionName=='right') {
212
+ btn_next.click()
213
+ } else if (actionName=='left') {
214
+ btn_prev.click()
215
+ }
216
+ }
217
+
218
+
219
+ async function newData(self, datainit) {
220
+ try {
221
+ <% if (detilHasUpload) { %>
222
+ <% uploadFields.forEach(uploadfield => { %>
223
+ <%= uploadfield.inputname %>.setDownloadLink(null)
224
+
225
+ <% }) %><% } %>frm.newData(datainit)
226
+ frm.acceptChanges()
227
+ frm.setAsNewData()
228
+ } catch (err) {
229
+ throw err
230
+ }
231
+ }
232
+
233
+
234
+ async function openData(self, id) {
235
+ const url = `/${Context.moduleName}/<%= sectionName %>-open`
236
+ try {
237
+ const result = await Module.apiCall(url, { id })
238
+ return result
239
+ } catch (err) {
240
+ throw err
241
+ }
242
+ }
243
+
244
+ async function createData(self, data, formData) {
245
+ const url = `/${Context.moduleName}/<%= sectionName %>-create`
246
+ try {
247
+ const result = await Module.apiCall(url, { data, source: Source }, formData)
248
+ return result
249
+ } catch (err) {
250
+ throw err
251
+ }
252
+ }
253
+
254
+ async function updateData(self, data, formData) {
255
+ const url = `/${Context.moduleName}/<%= sectionName %>-update`
256
+ try {
257
+ const result = await Module.apiCall(url, { data, source: Source }, formData)
258
+ return result
259
+ } catch (err) {
260
+ throw err
261
+ }
262
+ }
263
+
264
+ async function deleteData(self, id) {
265
+ const url = `/${Context.moduleName}/<%= sectionName %>-delete`
266
+ try {
267
+ const result = await Module.apiCall(url, { id, source: Source })
268
+ return result
269
+ } catch (err) {
270
+ throw err
271
+ }
272
+ }
273
+
274
+
275
+ async function backToList(self, evt) {
276
+ // cek apakah ada perubahan data
277
+ let goback = false
278
+ if (frm.isChanged()) {
279
+ // ada perubahan data, konfirmasi apakah mau lanjut back
280
+ var ret = await $fgta5.MessageBox.confirm(Module.BACK_CONFIRM)
281
+ if (ret=='ok') {
282
+ // user melanjutkan back, walaupun data berubah
283
+ // reset dahulu data form
284
+ frm.reset()
285
+ goback = true
286
+ }
287
+ } else {
288
+ goback = true
289
+ }
290
+
291
+ if (goback) {
292
+ frm.lock()
293
+ const listId = Context.Sections.<%= moduleSection %>List
294
+ const listSection = Crsl.Items[listId]
295
+ listSection.show({direction: 1})
296
+ }
297
+ }
298
+
299
+
300
+ async function frm_locked(self, evt) {
301
+ console.log('frm_locked')
302
+
303
+ CurrentSection.Title = TitleWhenView
304
+
305
+ btn_edit.setText(EditModeText)
306
+
307
+ // todo: cek dulu apakah boleh add/remove rows
308
+
309
+ btn_edit.disabled = false
310
+ btn_save.disabled = true
311
+ btn_new.disabled = false
312
+ btn_del.disabled = true
313
+ btn_reset.disabled = true
314
+ btn_prev.disabled = false
315
+ btn_next.disabled = false
316
+
317
+
318
+ // Extender untuk event locked
319
+ const fn_name = '<%= modulePart %>_formLocked'
320
+ const fn = Extender[fn_name]
321
+ if (typeof fn === 'function') {
322
+ fn(self, frm, CurrentState)
323
+ }
324
+
325
+ // jika heder form dalam kondisi lock,
326
+ // tetap tidak bisa hapus
327
+ if (CurrentState.editDisabled) {
328
+ btn_edit.disabled = true
329
+ btn_new.disabled = true
330
+ }
331
+
332
+ }
333
+
334
+ async function frm_unlocked(self, evt) {
335
+ console.log('frm_unlocked')
336
+
337
+ if (frm.isNew()) {
338
+ CurrentSection.Title = TitleWhenNew
339
+
340
+ } else {
341
+ CurrentSection.Title = TitleWhenEdit
342
+ }
343
+
344
+ btn_edit.setText(LockModeText)
345
+
346
+ btn_edit.disabled = false
347
+ btn_save.disabled = false
348
+ btn_new.disabled = true
349
+ btn_del.disabled = false
350
+ btn_reset.disabled = false
351
+ btn_prev.disabled = true
352
+ btn_next.disabled = true
353
+
354
+ // Extender untuk event Unlocked
355
+ const fn_name = '<%= modulePart %>_formUnlocked'
356
+ const fn = Extender[fn_name]
357
+ if (typeof fn === 'function') {
358
+ fn(self, frm)
359
+ }
360
+ }
361
+
362
+ async function setPrimaryKeyState(self, opt) {
363
+ const obj_pk = frm.getPrimaryInput()
364
+ obj_pk.disabled = opt.disabled===true
365
+ if (opt.placeholder!==undefined) {
366
+ obj_pk.placeholder = opt.placeholder
367
+ }
368
+ if (opt.value!==undefined) {
369
+ obj_pk.value = opt.value
370
+ }
371
+ }
372
+
373
+ <% if (detilHasUpload) { %>
374
+ async function downloadFile(self, objectname, filename) {
375
+ console.log(`downloading ${filename}`)
376
+ const url = `/download`
377
+ try {
378
+ const param = {
379
+ bucketname: Context.moduleName,
380
+ objectname: objectname
381
+ }
382
+ await Module.download(url, param)
383
+ } catch (err) {
384
+ console.error(err)
385
+ await $fgta5.MessageBox.error(err.message)
386
+ }
387
+ }
388
+
389
+ async function dataOpened(self, data) {
390
+ const docid = data.<%= headerPrimaryKey %>
391
+ const rowid = data.<%= detilPrimaryKey %>
392
+
393
+ <% uploadFields.forEach(uploadfield => { %>
394
+ <%= uploadfield.inputname %>.setDownloadLink(data.<%= uploadfield.fieldname %>, async ()=>{
395
+ const filename = data.<%= uploadfield.fieldname %>
396
+ const objectname = `${docid}/<%= entityName %>/${rowid}-<%= uploadfield.fieldname %>`
397
+ await downloadFile(self, objectname, filename)
398
+ })
399
+ <% }) %>
400
+ }
401
+ <% } %>
402
+
403
+ async function btn_edit_click(self, evt) {
404
+ console.log('btn_edit_click')
405
+
406
+ if (frm.isLocked()) {
407
+ // user mau inlock
408
+ frm.lock(false)
409
+ } else {
410
+ if (frm.isChanged() || frm.isNew()) {
411
+ await $fgta5.MessageBox.warning(Module.EDIT_WARNING)
412
+ return
413
+ }
414
+ frm.lock(true)
415
+ }
416
+ }
417
+
418
+
419
+ async function btn_new_click(self, evt) {
420
+ console.log('new')
421
+ const sourceSection = evt.target.getAttribute('data-sectionsource')
422
+
423
+ const <%= moduleSection %>List = self.Modules.<%= moduleSection %>List
424
+ const listsecid = <%= moduleSection %>List.Section.Id
425
+ const fromListSection = sourceSection===listsecid
426
+
427
+ if (fromListSection) {
428
+ console.log('tambahkan row baru')
429
+ CurrentSection.setSectionReturn(<%= moduleSection %>List.Section)
430
+ await CurrentSection.show()
431
+ } else {
432
+ // klik new dari form
433
+ let cancel_new = false
434
+ if (frm.isChanged()) {
435
+ const ret = await $fgta5.MessageBox.confirm(Module.NEWDATA_CONFIRM)
436
+ if (ret=='cancel') {
437
+ cancel_new = true
438
+ }
439
+ }
440
+ if (cancel_new) {
441
+ return
442
+ }
443
+ }
444
+
445
+ if (frm.AutoID) {
446
+ setPrimaryKeyState(self, {disabled:true, placeholder:'[AutoID]'})
447
+ } else {
448
+ setPrimaryKeyState(self, {disabled:false, placeholder:'ID'})
449
+ }
450
+
451
+
452
+ try {
453
+
454
+ // ambil id header
455
+ const <%= moduleName %>HeaderEdit = self.Modules.<%= moduleName %>HeaderEdit
456
+ const frmHeader = <%= moduleName %>HeaderEdit.getHeaderForm()
457
+ const header_pk = frmHeader.getPrimaryInput()
458
+ const <%= headerPrimaryKey %> = header_pk.value
459
+
460
+ // inisiasi data baru
461
+ let datainit = {
462
+ <%= headerPrimaryKey %>,<% defaultInits.forEach(setdefault => { %>
463
+ <%- setdefault %>,
464
+ <% }) %>}
465
+
466
+
467
+ // jika perlu modifikasi data initial,
468
+ // atau dialog untuk opsi data baru,
469
+ // dapat dibuat di Extender.newData
470
+ const fn_newdata_name = '<%= modulePart %>_newData'
471
+ const fn_newdata = Extender[fn_newdata_name]
472
+ if (typeof fn_newdata === 'function') {
473
+ await fn_newdata(self, datainit, frm)
474
+ }
475
+
476
+ // buat data baru
477
+ await newData(self, datainit)
478
+
479
+ // buka lock, agar user bisa edit
480
+ frm.lock(false)
481
+
482
+ // matikan tombol edit dan del saat kondisi form adalah data baru
483
+ btn_edit.disabled = true
484
+ btn_del.disabled = true
485
+ } catch (err) {
486
+ console.error(err)
487
+ await $fgta5.MessageBox.error(err.message)
488
+ if (fromListSection) {
489
+ // jika saat tombol baru dipilih saat di list, tampilan kembalikan ke list
490
+ self.Modules.<%= moduleSection %>List.Section.show()
491
+ }
492
+ }
493
+ }
494
+
495
+
496
+ async function btn_save_click(self, evt) {
497
+ console.log('btn_save_click')
498
+
499
+ // Extender Autofill
500
+ const fn_autofill_name = '<%= modulePart %>_autofill'
501
+ const fn_autofill = Extender[fn_autofill_name]
502
+ if (typeof fn_autofill === 'function') {
503
+ await fn_autofill(self, frm)
504
+ }
505
+
506
+ // cek apakah data valid
507
+ const valid = frm.validate()
508
+ if (!valid) {
509
+ const message = frm.getLastError()
510
+ console.warn(message)
511
+ $fgta5.MessageBox.warning(message)
512
+ return
513
+ }
514
+
515
+
516
+ // abaikan jika bukan data baru dan tidak ada perubahan
517
+ let dataToSave
518
+ const isNewData = frm.isNew()
519
+ if (!isNewData) {
520
+ // cek dulu apakah ada perubahaan
521
+ if (!frm.isChanged()) {
522
+ // skip save jika tidak ada perubahan data
523
+ console.log('tidak ada perubahan data, skip save')
524
+ return
525
+ }
526
+
527
+ // ambil hanya data yang berubah
528
+ dataToSave = frm.getDataChanged()
529
+
530
+ } else {
531
+
532
+ // untuk posisi data baru, ambil semua data
533
+ dataToSave = frm.getData()
534
+ }
535
+
536
+
537
+ // bila ada file, upload filenya
538
+ let formData = null
539
+ const files = frm.getFiles()
540
+ if (files!=null) {
541
+ formData = new FormData();
542
+ for (let name in files) {
543
+ const file = files[name]
544
+ formData.append(name, file)
545
+ }
546
+ }
547
+
548
+
549
+ // Extender Saving
550
+ const fn_datasaving_name = '<%= modulePart %>_dataSaving'
551
+ const fn_datasaving = Extender[fn_datasaving_name]
552
+ if (typeof fn_datasaving === 'function') {
553
+ await fn_datasaving(self, dataToSave, frm)
554
+ }
555
+
556
+
557
+
558
+ let mask = $fgta5.Modal.createMask()
559
+ try {
560
+ let result
561
+
562
+ if (isNewData) {
563
+ result = await createData(self, dataToSave, formData)
564
+ } else {
565
+ result = await updateData(self, dataToSave, formData)
566
+ }
567
+
568
+ console.log('result', result)
569
+ const obj_pk = frm.getPrimaryInput()
570
+ const pk = obj_pk.getBindingData()
571
+ const idValue = result[pk]
572
+
573
+ console.log(`get data id ${idValue}`)
574
+ const data = await openData(self, idValue)
575
+ console.log('data', data)
576
+
577
+ <% if (detilHasUpload) { %>
578
+ dataOpened(self, data)<% } %>
579
+
580
+ CurrentState.currentOpenedId = idValue
581
+
582
+ if (frm.AutoID) {
583
+ console.log('update field ID di form')
584
+ obj_pk.value = idValue
585
+ } else {
586
+ // jika bukan autoID, kunci field PK menjadi disabled
587
+ setPrimaryKeyState(self, {disabled:true})
588
+
589
+ }
590
+
591
+ // update form
592
+ frm.setData(data)
593
+
594
+
595
+ // Extender Saving
596
+ const fn_datasaved_name = '<%= modulePart %>_dataSaved'
597
+ const fn_datasaved = Extender[fn_datasaved_name]
598
+ if (typeof fn_datasaved === 'function') {
599
+ await fn_datasaved(self, data, frm)
600
+ }
601
+
602
+
603
+ // persist perubahan di form
604
+ frm.acceptChanges()
605
+
606
+
607
+ if (isNewData) {
608
+ // saat new data, posisi button toggle edit akan disabled
609
+ // setelah berhasil save, nyalakan button edit (untuk lock)
610
+ btn_edit.disabled = false
611
+
612
+ // buat baris baru di grid
613
+ console.log('tamabah baris baru di grid')
614
+ self.Modules.<%= moduleSection %>List.addNewRow(self, data)
615
+ } else {
616
+ console.log('update data baris yang dibuka')
617
+ self.Modules.<%= moduleSection %>List.updateCurrentRow(self, data)
618
+ }
619
+
620
+ } catch (err) {
621
+ console.error(err)
622
+ await $fgta5.MessageBox.error(err.message)
623
+ } finally {
624
+ mask.close()
625
+ mask = null
626
+ }
627
+ }
628
+
629
+ async function btn_del_click(self, evt) {
630
+ console.log('btn_del_click')
631
+
632
+ // jika data masih dalam kondisi baru (belum di save,
633
+ // perintah delete harus dibatalkan,
634
+ // karena belum ada data di database)
635
+ const isNewData = frm.isNew()
636
+ if (isNewData) {
637
+ console.log('posisi data baru, skip delete')
638
+ return
639
+ }
640
+
641
+ const obj_pk = frm.getPrimaryInput()
642
+ const idValue = obj_pk.value
643
+
644
+ // konfirmasi untuk delete data
645
+ const resp = await $fgta5.MessageBox.confirm(Module.DELETE_CONFIRM + `id: ${idValue}`)
646
+ if (resp!='ok') {
647
+ return
648
+ }
649
+
650
+ console.log('delete data')
651
+ let mask = $fgta5.Modal.createMask()
652
+ try {
653
+ const result = await deleteData(self, idValue)
654
+
655
+ // hapus current row yang dipilih di list
656
+ self.Modules.<%= moduleSection %>List.removeCurrentRow(self)
657
+
658
+ // kembali ke list
659
+ self.Modules.<%= moduleSection %>List.Section.show()
660
+
661
+
662
+ // lock kembali form
663
+ frm.lock()
664
+
665
+ } catch (err) {
666
+ console.error(err)
667
+ await $fgta5.MessageBox.error(err.message)
668
+ } finally {
669
+ mask.close()
670
+ mask = null
671
+ }
672
+ }
673
+
674
+ async function btn_reset_click(self, evt) {
675
+ console.log('btn_reset_click')
676
+
677
+ const isNewData = frm.isNew()
678
+ if (isNewData) {
679
+ // untuk data baru, di reset berarti sama seperti membuat data baru
680
+ console.log('reset: buat data baru')
681
+ newData(self)
682
+ } else {
683
+ if (frm.isChanged()) {
684
+ // ada perubahan data, tampilkan konfirmasi perubahan data
685
+ var resp = await $fgta5.MessageBox.confirm(Module.RESET_CONFIRM)
686
+ if (resp!='ok') {
687
+ // user klik tombil cancel
688
+ console.log('cancel reset')
689
+ return
690
+ }
691
+ console.log('reset form')
692
+ frm.reset()
693
+ } else {
694
+ console.log('tidak ada perubahan data, reset data tidak dieksekusi')
695
+ }
696
+ }
697
+ }
698
+
699
+
700
+ async function btn_prev_click(self, evt) {
701
+ console.log('btn_prev_click')
702
+ self.Modules.<%= moduleSection %>List.selectPreviousRow(self)
703
+ }
704
+
705
+ async function btn_next_click(self, evt) {
706
+ console.log('btn_next_click')
707
+ self.Modules.<%= moduleSection %>List.selectNextRow(self)
708
+ }
709
+
710
+
711
+
712
+ async function btn_recordstatus_click(self, evt) {
713
+ console.log('btn_recordstatus_click')
714
+ const params = {
715
+ Context,
716
+ sectionReturn: CurrentSection
717
+ }
718
+
719
+ pageHelper.openSection(self, 'fRecord-section', params, async ()=>{
720
+
721
+ let mask = $fgta5.Modal.createMask()
722
+ try {
723
+ // ambil data
724
+ const pk = frm.getPrimaryInput()
725
+ const id = pk.value
726
+ const data = await openData(self, id)
727
+
728
+ obj_createby.innerHTML = data._createby
729
+ obj_createdate.innerHTML = data._createdate
730
+ obj_modifyby.innerHTML = data._modifyby
731
+ obj_modifydate.innerHTML = data._modifydate
732
+
733
+
734
+ // jika mau menambah beberapa informasi mengenai record,
735
+ // misalnya commit by, postby, dll
736
+ // melalui extender <%= modulePart %>_addRecordInfo
737
+ const fn_addrecordinfo_name = '<%= modulePart %>_addRecordInfo'
738
+ const fn_addrecordinfo = Extender[fn_addrecordinfo_name]
739
+ if (typeof fn_addrecordinfo === 'function') {
740
+ await fn_addrecordinfo(self, data)
741
+ }
742
+
743
+ } catch (err) {
744
+ console.error(err)
745
+ $fgta5.MessageBox.error(err.message)
746
+ } finally {
747
+ mask.close()
748
+ mask = null
749
+ }
750
+ })
751
+
752
+ }
753
+
754
+ async function btn_logs_click(self, evt) {
755
+ const params = {
756
+ Context,
757
+ sectionReturn: CurrentSection
758
+ }
759
+
760
+ pageHelper.openSection(self, 'fLogs-section', params, async ()=>{
761
+ // get log data
762
+ const pk = frm.getPrimaryInput()
763
+ const id = pk.value
764
+
765
+
766
+ let mask = $fgta5.Modal.createMask()
767
+ try {
768
+
769
+ const url = `${Context.appsUrls.core.url}/logs/list`
770
+ const criteria = {
771
+ module: Context.moduleName,
772
+ table: '<%= tablename %>',
773
+ id: id
774
+ }
775
+
776
+ const result = await Module.apiCall(url, {
777
+ criteria
778
+ })
779
+
780
+ const sc = document.getElementById('fLogs-section')
781
+ const tbody = sc.querySelector('tbody')
782
+ pageHelper.renderLog(tbody, result.data)
783
+ } catch (err) {
784
+ console.error(err)
785
+ $fgta5.MessageBox.error(err.message)
786
+ } finally {
787
+ mask.close()
788
+ mask = null
789
+ }
790
+
791
+ })
792
+ }