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