@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,16 @@
1
+ const app = new $fgta5.Application('mainapp')
2
+ const urlDir = 'public/modules/<%=moduleName%>'
3
+ const Crsl = new $fgta5.SectionCarousell(app.Nodes.Main)
4
+
5
+ export default {
6
+ moduleName: '<%=moduleName%>',
7
+ app: app,
8
+ urlDir: urlDir,
9
+ Crsl: Crsl,
10
+ Sections: { <% sections.forEach(section => { %>
11
+ <%= section.sectionName %>: '<%= section.sectionElementId %>', <% }) %>
12
+ },
13
+ SectionMap: { <% sections.forEach(section => { %>
14
+ '<%= section.sectionElementId %>' : '<%= section.sectionName %>', <% }) %>
15
+ }
16
+ }
@@ -0,0 +1 @@
1
+ <!-- about text here -->
@@ -0,0 +1 @@
1
+ <!-- tambahan informasi record, misalnya postby commitby, dlll -->
@@ -0,0 +1,3 @@
1
+ <!-- Additional Search Parameter-->
2
+ <template name="custom-search-panel">
3
+ </template>
@@ -0,0 +1,21 @@
1
+ import Context from './<%=moduleName%>-context.mjs'
2
+
3
+ export async function init(self, args) {
4
+ console.log('initializing <%=moduleName%>Extender ...')
5
+
6
+ // tambahkan extender inisiasi module <%=moduleName%>
7
+
8
+
9
+ /* // contoh menambahkan content dari template extender
10
+ {
11
+ const target = secRec.querySelector('#fRecord-section div[name="column"][exteder]')
12
+ const tpl = document.querySelector('template[name="record-panel"]')
13
+ if (tpl!=null) {
14
+ const clone = tpl.content.cloneNode(true); // salin isi template
15
+ target.prepend(clone)
16
+ }
17
+ }
18
+ */
19
+ }
20
+
21
+
@@ -0,0 +1,14 @@
1
+ <table cellpadding="0" cellspacing="0">
2
+ <thead>
3
+ <tr>
4
+ <td class="logheader">Date Time</td>
5
+ <td class="logheader">User</td>
6
+ <td class="logheader">Action</td>
7
+ <td class="logheader">IP</td>
8
+ <td class="logheader">Remark</td>
9
+ </tr>
10
+ </thead>
11
+ <tbody>
12
+
13
+ </tbody>
14
+ </table>
@@ -0,0 +1,48 @@
1
+ <% sections.forEach(section => { %><section id="<%= section.sectionElementId %>" class="fgta5-carousell" data-title="<%= section.sectionTitle %>">
2
+ <%- '<' %>%- include(`${moduleDir}/<%= section.sectionName %>.html`) %<%- '>' %>
3
+ </section>
4
+
5
+ <% }) %>
6
+
7
+ <section id="fRecord-section" class="fgta5-carousell" data-title="Record Status">
8
+ <div name="panel">
9
+
10
+ <!-- LEFT: Standard Info -->
11
+ <div name="column">
12
+ <div name="row">
13
+ <span name="caption">Create By</span>
14
+ <span name="value" id="fRecord-section-createby">-</span>
15
+ </div>
16
+ <div name="row">
17
+ <span name="caption">Create Date</span>
18
+ <span name="value" id="fRecord-section-createdate">-</span>
19
+ </div>
20
+ <div name="row">
21
+ <span name="caption">Modify By</span>
22
+ <span name="value" id="fRecord-section-modifyby">-</span>
23
+ </div>
24
+ <div name="row">
25
+ <span name="caption">Modify Date</span>
26
+ <span name="value"id="fRecord-section-modifydate">-</span>
27
+ </div>
28
+
29
+ </div>
30
+
31
+
32
+ <!-- RIGHT: Extended Info -->
33
+ <div name="column" exteder>
34
+ <%- '<' %>%- include(`${moduleDir}/<%= moduleName %>-ext-record.html`) %<%- '>' %>
35
+ </div>
36
+
37
+ </div>
38
+ </section>
39
+
40
+ <section id="fLogs-section" class="fgta5-carousell" data-title="Logs">
41
+ <%- '<' %>%- include(`${moduleDir}/<%= moduleName %>-logs.html`) %<%- '>' %>
42
+ </section>
43
+
44
+ <section id="fAbout-section" class="fgta5-carousell" data-title="About">
45
+ <%- '<' %>%- include(`${moduleDir}/<%= moduleName %>-ext-about.html`) %<%- '>' %>
46
+ </section>
47
+
48
+
@@ -0,0 +1,256 @@
1
+ import Context from './<%= moduleName %>-context.mjs' <% sections.forEach(section => { %>
2
+ import * as <%= section.sectionName %> from './<%= section.sectionName %>.mjs' <% }) %>
3
+ import * as Extender from './<%= moduleName %>-ext.mjs'
4
+
5
+ const app = Context.app
6
+ const Crsl = Context.Crsl
7
+
8
+
9
+ export default class extends Module {
10
+ constructor() {
11
+ super()
12
+ }
13
+
14
+ async main(args={}) {
15
+
16
+ console.log('initializing module...')
17
+ app.setTitle('<%= title %>')
18
+ app.showFooter(true)
19
+
20
+ args.autoLoadGridData = true
21
+
22
+ const self = this
23
+
24
+ // module-module yang di load perlu di pack dulu ke dalam variable
25
+ // jangan import lagi module-module ini di dalam mjs tersebut
26
+ // karena akan terjadi cyclic redudancy pada saat di rollup
27
+ self.Modules = { <% sections.forEach(section => { %>
28
+ <%= section.sectionName %>, <% }) %>
29
+ }
30
+
31
+ try {
32
+
33
+ // inisiasi sisi server
34
+ try {
35
+ const result = await Module.apiCall(`/${Context.moduleName}/init`, { })
36
+ Context.notifierId = result.notifierId
37
+ Context.notifierSocket = result.notifierSocket
38
+ Context.userId = result.userId
39
+ Context.userFullname = result.userFullname
40
+ Context.sid = result.sid
41
+ Context.appsUrls = result.appsUrls
42
+ } catch (err) {
43
+ throw err
44
+ }
45
+
46
+ await Promise.all([ <% sections.forEach(section => { %>
47
+ <%= section.sectionName %>.init(self, args), <% }) %>
48
+ Extender.init(self, args)
49
+ ])
50
+
51
+ // render dan setup halaman
52
+ await render(self)
53
+
54
+ // listen keyboard action
55
+ listenUserKeys(self)
56
+
57
+
58
+ // kalau user melakukan reload, konfirm dulu
59
+ const modNameList = [<%- entityNameList %>]
60
+ window.onbeforeunload = (evt)=>{
61
+ // cek dulu semua form
62
+ let isFormDirty = false
63
+ for (var modname of modNameList) {
64
+ const module = self.Modules[modname]
65
+ const frm = module.getForm(self)
66
+ if (frm.isChanged()) {
67
+ isFormDirty = isFormDirty || true
68
+ }
69
+ }
70
+ if (isFormDirty) {
71
+ evt.preventDefault();
72
+ return "Changes you made may not be saved."
73
+ }
74
+ };
75
+
76
+
77
+ } catch (err) {
78
+ throw err
79
+ }
80
+ }
81
+
82
+ }
83
+
84
+ async function render(self) {
85
+ try {
86
+ const footerButtonsContainer = document.getElementsByClassName('footer-buttons-container')
87
+ Module.renderFooterButtons(footerButtonsContainer)
88
+
89
+ // Setup Icon
90
+ Crsl.setIconUrl('<%= iconUrl %>')
91
+
92
+
93
+ // Set listener untuk section carousel
94
+ Crsl.addEventListener($fgta5.SectionCarousell.EVT_SECTIONSHOWING, (evt)=>{
95
+ var sectionId = evt.detail.commingSection.Id
96
+ for (let cont of footerButtonsContainer) {
97
+ var currContainerSectionId = cont.getAttribute('data-section')
98
+ if (currContainerSectionId==sectionId) {
99
+ setTimeout(()=>{
100
+ cont.classList.remove('hidden')
101
+ cont.style.animation = 'dropped 0.3s forwards'
102
+ setTimeout(()=>{
103
+ cont.style.animation = 'unset'
104
+ }, 300)
105
+ }, 500)
106
+ } else {
107
+ cont.classList.add('hidden')
108
+ }
109
+ }
110
+ })
111
+
112
+ Crsl.Items['fRecord-section'].addEventListener($fgta5.Section.EVT_BACKBUTTONCLICK, async (evt)=>{
113
+ evt.detail.fn_ShowNextSection()
114
+ })
115
+
116
+ Crsl.Items['fLogs-section'].addEventListener($fgta5.Section.EVT_BACKBUTTONCLICK, async (evt)=>{
117
+ evt.detail.fn_ShowNextSection()
118
+ })
119
+
120
+ Crsl.Items['fAbout-section'].addEventListener($fgta5.Section.EVT_BACKBUTTONCLICK, async (evt)=>{
121
+ evt.detail.fn_ShowNextSection()
122
+ })
123
+
124
+
125
+ // Set panel detil saat hover di detil item
126
+ const detilpanel = document.getElementById('panel-detil-selector')
127
+ detilpanel.querySelectorAll('.panel-detil-row a').forEach(link => {
128
+ link.addEventListener('mouseenter', () => {
129
+ link.closest('.panel-detil-row').classList.add('panel-detil-row-highligted');
130
+ });
131
+
132
+ link.addEventListener('mouseleave', () => {
133
+ link.closest('.panel-detil-row').classList.remove('panel-detil-row-highligted');
134
+ });
135
+
136
+
137
+ const sectionTargetName = link.getAttribute('data-target-section')
138
+ const sectionCurrentName = link.getAttribute('data-current-section')
139
+
140
+ link.addEventListener('click', (evt)=>{
141
+ openDetilSection(self, sectionTargetName, sectionCurrentName)
142
+ })
143
+
144
+ // jika ada event-event yang khusus untuk mobile device
145
+ // if (Module.isMobileDevice()) {
146
+ // }
147
+ });
148
+
149
+
150
+ // <%= moduleName %>-ext.mjs, export function extendPage(self) {}
151
+ const fn_name = 'extendPage'
152
+ const fn_extendPage = Extender[fn_name]
153
+ if (typeof fn_extendPage === 'function') {
154
+ fn_extendPage(self)
155
+ } else {
156
+ console.warn(`'extendPage' tidak diimplementasikan di extender`)
157
+ }
158
+
159
+ } catch (err) {
160
+ throw err
161
+ }
162
+ }
163
+
164
+
165
+ function openDetilSection(self, sectionTargetName, sectionCurrentName) {
166
+ const sectionCurrentId = Context.Sections[sectionCurrentName]
167
+ const sectionCurrent = Crsl.Items[sectionCurrentId]
168
+
169
+ const sectionId = Context.Sections[sectionTargetName]
170
+ const section = Crsl.Items[sectionId]
171
+
172
+ section.setSectionReturn(sectionCurrent)
173
+ section.show({}, ()=>{
174
+ const moduleTarget = self.Modules[sectionTargetName]
175
+ const moduleHeaderEdit = self.Modules[sectionCurrentName]
176
+ moduleTarget.openList(self, {
177
+ moduleHeaderEdit
178
+ })
179
+ })
180
+ }
181
+
182
+
183
+ function listenUserKeys(self) {
184
+
185
+ // capture tombol
186
+ const allowedKeys = /^[a-zA-Z0-9 ]$/; // huruf, angka, spasi
187
+ document.addEventListener('keydown', (evt) => {
188
+ if (evt.ctrlKey || evt.metaKey || evt.altKey) {
189
+ // bebaskan jika tekan Ctrl / alt
190
+ } else if (allowedKeys.test(evt.key) || evt.key === 'Backspace' || evt.key === 'Delete') {
191
+ // Tangani huruf, angka, spasi, backspace dan delete
192
+ const id = Crsl.CurrentSection.Id
193
+ const moduleId = Context.SectionMap[id]
194
+ const module = self.Modules[moduleId]
195
+ keyboardAction(self, module, 'typing', evt)
196
+ }
197
+ }, true)
198
+
199
+ // handle keyboard event
200
+ document.addEventListener('keydown', (evt) => {
201
+ const id = Crsl.CurrentSection.Id
202
+ const moduleId = Context.SectionMap[id]
203
+ const module = self.Modules[moduleId]
204
+
205
+ // jika ada dialog yang terbuka, semua event keyboard abaikan dulu, keculai tombol escape
206
+ const dialog = document.querySelector('dialog[open]');
207
+ if (dialog) {
208
+ if (evt.key.toLowerCase()=='escape') {
209
+ dialog.close();
210
+ evt.preventDefault();
211
+ } else if ((evt.ctrlKey || evt.metaKey) && evt.key.toLowerCase() === 's') {
212
+ evt.preventDefault();
213
+ }
214
+ return
215
+ }
216
+
217
+ // Cek apakah tombol Ctrl (atau Cmd di Mac) ditekan bersamaan dengan huruf 'S'
218
+ const key = evt.key.toLowerCase()
219
+ if ((evt.ctrlKey || evt.metaKey) && key === 's') {
220
+ evt.preventDefault(); // Mencegah aksi default (save page)
221
+ keyboardAction(self, module, 'save', evt)
222
+ } else if (((evt.ctrlKey || evt.metaKey) && key === 'n') || (evt.ctrlKey && key==='f2')) {
223
+ evt.preventDefault(); // Mencegah aksi default
224
+ keyboardAction(self, module, 'new', evt)
225
+ } else if ( key ==='escape') {
226
+ evt.preventDefault();
227
+ keyboardAction(self, module, 'escape', evt)
228
+ } else if ( key === 'f2' ) {
229
+ keyboardAction(self, module, 'togleEdit', evt)
230
+ } else if ( key === 'arrowup' ) {
231
+ keyboardAction(self, module, 'up', evt)
232
+ } else if ( key === 'arrowdown' ) {
233
+ keyboardAction(self, module, 'down', evt)
234
+ } else if ( key === 'arrowright' ) {
235
+ keyboardAction(self, module, 'right', evt)
236
+ } else if ( key === 'arrowleft' ) {
237
+ keyboardAction(self, module, 'left', evt)
238
+ } else if ( key === 'enter' ) {
239
+ keyboardAction(self, module, 'enter', evt)
240
+ }
241
+ });
242
+ }
243
+
244
+
245
+ function keyboardAction(self, module, actionName, evt) {
246
+
247
+ if (module!=null) {
248
+ module.keyboardAction(self, actionName, evt)
249
+ } else {
250
+ // untuk keperluan log dan about, saat escape: back
251
+ if (actionName=='escape') {
252
+ Crsl.CurrentSection.back()
253
+ }
254
+ }
255
+
256
+ }
@@ -0,0 +1,34 @@
1
+ <form id="<%= modulePart %>-frm" class="module-form" locked="true" autoid="<%= autoid %>" primarykey="<%= primaryKeyElementId %>"><% fields.forEach(field => { %>
2
+
3
+ <!-- <%= field.component %>: <%= field.fieldname %>-->
4
+ <div id="<%= field.elementId %>-container" class="<%= field.cssContainer %>" field="<%= field.elementId %>">
5
+ <label for="<%= field.elementId %>"><%= field.label %></label>
6
+ <input id="<%= field.elementId %>" fgta5-component="<%= field.component %>" placeholder="<%= field.placeholder %>" autocomplete="off" spellcheck="false"
7
+ binding="<%= field.binding %>"<% field.additionalAttributes.forEach(config => { %>
8
+ <%- config %> <% }) %>
9
+ >
10
+ </div>
11
+ <% }) %>
12
+ </form>
13
+
14
+ <div class="panel-recordbar" data-view="nodetil">
15
+ <div><a id="<%= moduleSection %>-btn_recordstatus" href="javascript:void(0)" tabindex="-1">Record Status</a></div>
16
+ <div><a id="<%= moduleSection %>-btn_logs" href="javascript:void(0)" tabindex="-1">Logs</a></div>
17
+ </div>
18
+
19
+
20
+ <div class="footer-buttons-container hidden">
21
+ <div>
22
+ <button id="<%= modulePart %>-btn_prev"><span>&laquo;&laquo;</span></button>
23
+ </div>
24
+ <div>
25
+ <button id="<%= modulePart %>-btn_save">Save</button>
26
+ <button id="<%= modulePart %>-btn_edit">Edit</button>
27
+ <button id="<%= modulePart %>-btn_new" data-action="<%= moduleSection %>-addrow">New</button>
28
+ <button id="<%= modulePart %>-btn_reset">Reset</button>
29
+ <button id="<%= modulePart %>-btn_delete">Delete</button>
30
+ </div>
31
+ <div>
32
+ <button id="<%= modulePart %>-btn_next"><span>&raquo;&raquo;</span></button>
33
+ </div>
34
+ </div>