@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.
- package/LICENSE +28 -0
- package/README.md +2 -0
- package/jsconfig.json +10 -0
- package/libs/fgta5js-dist/fgta5js-v1.8.3.min.css +2 -0
- package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js +11 -0
- package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js.map +1 -0
- package/libs/fgta5js-dist/fonts/karla-italic-latin-ext.woff2 +0 -0
- package/libs/fgta5js-dist/fonts/karla-italic-latin.woff2 +0 -0
- package/libs/fgta5js-dist/fonts/karla-normal-latin-ext.woff2 +0 -0
- package/libs/fgta5js-dist/fonts/karla-normal-latin.woff2 +0 -0
- package/libs/fgta5js-dist/fonts/karla.css +142 -0
- package/libs/webmodule/module-edit.css +163 -0
- package/libs/webmodule/module-footer.css +22 -0
- package/libs/webmodule/module-list.css +25 -0
- package/libs/webmodule/module.css +52 -0
- package/libs/webmodule/module.js +195 -0
- package/libs/webmodule/pagehelper.mjs +45 -0
- package/modules/generator/appgen-components.mjs +142 -0
- package/modules/generator/appgen-icons.mjs +6 -0
- package/modules/generator/appgen-io.mjs +784 -0
- package/modules/generator/appgen-ui-search.mjs +173 -0
- package/modules/generator/appgen-ui-unique.mjs +153 -0
- package/modules/generator/appgen-ui.mjs +1181 -0
- package/modules/generator/generator-context.mjs +18 -0
- package/modules/generator/generator-designtemplate.html +1508 -0
- package/modules/generator/generator-ext.html +0 -0
- package/modules/generator/generator-ext.mjs +3 -0
- package/modules/generator/generator.css +642 -0
- package/modules/generator/generator.mjs +195 -0
- package/modules/generator/generator.png +0 -0
- package/modules/generator/generatorEdit.html +185 -0
- package/modules/generator/generatorEdit.mjs +238 -0
- package/modules/generator/generatorList.html +32 -0
- package/modules/generator/generatorList.mjs +243 -0
- package/modules/login/login.css +11 -0
- package/modules/login/login.html +12 -0
- package/modules/login/login.mjs +111 -0
- package/package.json +46 -0
- package/percobaan/simmpan-ke-minio.js +24 -0
- package/src/api.js +80 -0
- package/src/apis/generator.api.js +226 -0
- package/src/apis/login.api.js +109 -0
- package/src/bucket.js +24 -0
- package/src/context.js +26 -0
- package/src/datalog.sql +22 -0
- package/src/datarecords.js +0 -0
- package/src/db.js +61 -0
- package/src/generator/createApiExtenderModule.js +54 -0
- package/src/generator/createApiModule.js +218 -0
- package/src/generator/createIcon.js +62 -0
- package/src/generator/createInfoAboutExtender.js +42 -0
- package/src/generator/createInfoLogs.js +41 -0
- package/src/generator/createInfoRecordExtender.js +41 -0
- package/src/generator/createModuleContext.js +48 -0
- package/src/generator/createModuleDetilEditHtml.js +110 -0
- package/src/generator/createModuleDetilEditMjs.js +172 -0
- package/src/generator/createModuleDetilListHtml.js +146 -0
- package/src/generator/createModuleDetilListMjs.js +73 -0
- package/src/generator/createModuleEjs.js +51 -0
- package/src/generator/createModuleExtenderHtml.js +43 -0
- package/src/generator/createModuleExtenderMjs.js +43 -0
- package/src/generator/createModuleHeaderEditHtml.js +148 -0
- package/src/generator/createModuleHeaderEditMjs.js +197 -0
- package/src/generator/createModuleHeaderListHtml.js +144 -0
- package/src/generator/createModuleHeaderListMjs.js +67 -0
- package/src/generator/createModuleMjs.js +67 -0
- package/src/generator/createModuleRollup.js +42 -0
- package/src/generator/createProgramData.js +96 -0
- package/src/generator/createTable.js +156 -0
- package/src/generator/ddl.js +475 -0
- package/src/generator/helper.js +149 -0
- package/src/generator/templates/__rollup-module.ejs +90 -0
- package/src/generator/templates/api-extender-module.js.ejs +0 -0
- package/src/generator/templates/api-module.js.ejs +818 -0
- package/src/generator/templates/module-context.ejs +16 -0
- package/src/generator/templates/module-ext-about.ejs +1 -0
- package/src/generator/templates/module-ext-record.ejs +1 -0
- package/src/generator/templates/module-ext.html.ejs +3 -0
- package/src/generator/templates/module-ext.mjs.ejs +21 -0
- package/src/generator/templates/module-logs.ejs +14 -0
- package/src/generator/templates/module.ejs.ejs +48 -0
- package/src/generator/templates/module.mjs.ejs +256 -0
- package/src/generator/templates/moduleDetilEdit.html.ejs +34 -0
- package/src/generator/templates/moduleDetilEdit.mjs.ejs +792 -0
- package/src/generator/templates/moduleDetilList.html.ejs +26 -0
- package/src/generator/templates/moduleDetilList.mjs.ejs +319 -0
- package/src/generator/templates/moduleHeaderEdit.html.ejs +53 -0
- package/src/generator/templates/moduleHeaderEdit.mjs.ejs +807 -0
- package/src/generator/templates/moduleHeaderList.html.ejs +24 -0
- package/src/generator/templates/moduleHeaderList.mjs.ejs +308 -0
- package/src/generator/templates/sqlAddField.ejs +3 -0
- package/src/generator/templates/sqlAddForeignKey.ejs +12 -0
- package/src/generator/templates/sqlAddUniqueIndex.ejs +4 -0
- package/src/generator/templates/sqlCreateTable.ejs +9 -0
- package/src/generator/templates/sqlDropForeignKey.ejs +3 -0
- package/src/generator/templates/sqlDropUniqueIndex.ejs +4 -0
- package/src/generator/templates/sqlModifyField.ejs +6 -0
- package/src/generator/trygenerate.js +83 -0
- package/src/generator/worker.js +389 -0
- package/src/helper.js +82 -0
- package/src/logger.js +39 -0
- package/src/router.js +84 -0
- package/src/routers/defaultLoginApi.js +29 -0
- package/src/routers/defaultLoginAsset.js +18 -0
- package/src/routers/defaultLoginPage.js +36 -0
- package/src/routers/defaultRootIndex.js +16 -0
- package/src/routers/downloadHandler.js +51 -0
- package/src/routers/fileUploadApi.js +15 -0
- package/src/routers/generatorApi.js +30 -0
- package/src/routers/generatorAsset.js +18 -0
- package/src/routers/generatorPage.js +37 -0
- package/src/routers/handleError.js +43 -0
- package/src/routers/handleModuleNotfound.js +12 -0
- package/src/routers/moduleApi.js +34 -0
- package/src/routers/modulePage.js +102 -0
- package/src/sequencerdoc.js +311 -0
- package/src/sequencerline.js +214 -0
- package/src/session.js +57 -0
- package/src/startup.js +59 -0
- package/src/webapps.js +239 -0
- package/src/workermanager.js +83 -0
- package/templates/_lib_debug.ejs +11 -0
- package/templates/_lib_production.ejs +5 -0
- package/templates/application.page.ejs +143 -0
- package/templates/generator.page.ejs +131 -0
- package/templates/index.page.ejs +24 -0
- package/templates/login.page.ejs +102 -0
- package/templates/moduleError.ejs +16 -0
- package/templates/moduleNotfound.ejs +14 -0
- package/webapps.code-workspace +11 -0
|
@@ -0,0 +1,1181 @@
|
|
|
1
|
+
import Components from './appgen-components.mjs'
|
|
2
|
+
import AppGenIO from './appgen-io.mjs'
|
|
3
|
+
import { AppGenLayout_setUniqueME, AppGenLayout_setupUniqueDesigner, AppGenLayout_addUnique } from './appgen-ui-unique.mjs'
|
|
4
|
+
import { AppGenLayout_setSearchME, AppGenLayout_setupSearchDesigner, AppGenLayout_addSearch } from './appgen-ui-search.mjs'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const IO = new AppGenIO()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const URL_LAYOUT = 'appgen-layout'
|
|
11
|
+
const ME = {}
|
|
12
|
+
|
|
13
|
+
const ENT_COL_ID = 'col_id'
|
|
14
|
+
const ENT_COL_NAME = 'col_name'
|
|
15
|
+
const ENT_COL_TITLE = 'col_title'
|
|
16
|
+
const ENT_COL_TABLE = 'col_table'
|
|
17
|
+
const ENT_COL_PK = 'col_pk'
|
|
18
|
+
const ENT_COL_BTNDESIGN = 'col_btndesign'
|
|
19
|
+
const ENT_COL_BTNREMOVE = 'col_btnremove'
|
|
20
|
+
|
|
21
|
+
const ATTR_NAME = 'name'
|
|
22
|
+
const ATTR_ENTITYID = 'data-entity-id'
|
|
23
|
+
const ATTR_DROPTARGET = 'drop-target'
|
|
24
|
+
const ATTR_DRAGOVER = 'data-dragover'
|
|
25
|
+
const ATTR_CURRENTENTITY = 'data-currententity'
|
|
26
|
+
const ATTR_COMPNAME = 'data-component-name'
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
const ID_ENTITYEDITOR = 'entity-editor'
|
|
31
|
+
const ID_DESIGNERINFO = 'designer-info'
|
|
32
|
+
const ID_DESIGNERSEARCH = 'designer-search'
|
|
33
|
+
const ID_DESIGNERUNIQ = 'designer-unique'
|
|
34
|
+
const ID_ICONTOOL = 'component-icon-tool'
|
|
35
|
+
const ID_DESIGNFIELD = 'design-data-field'
|
|
36
|
+
|
|
37
|
+
const CLS_HIDDEN = 'hidden'
|
|
38
|
+
const CLS_ENTITYEDITOR = 'entity-editor'
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
const DRAG_ICONTOOL = 'drag-icon-tool'
|
|
43
|
+
const DRAG_REORDERFIELD = 'drag-reorderfield'
|
|
44
|
+
|
|
45
|
+
/* icon default untuk program yang akan di generate, muncul di tombol [upload icon program] */
|
|
46
|
+
const ICON_DEFAULT = `<svg version="1.1" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
|
47
|
+
<g fill="currentColor">
|
|
48
|
+
<path d="m20.019 2c-2.9332 0-5.2348 2.3057-5.2348 5.2389v3.1417h-8.3806c-2.3047 0-4.1903 1.8856-4.1903 4.1903v7.9628h3.1417c3.1427 0 5.6567 2.514 5.6567 5.6567 0 3.1427-2.514 5.6567-5.6567 5.6567h-3.3547v7.9628c0 2.3047 1.8856 4.1903 4.1903 4.1903h7.9628v-3.1458c0-3.1427 2.514-5.6567 5.6567-5.6567 3.1427 0 5.6567 2.514 5.6567 5.6567v3.1458h7.9628c2.3047 0 4.1903-1.8856 4.1903-4.1903v-8.3806h3.1417c2.9332 0 5.2389-2.3057 5.2389-5.2389 0-2.9332-2.3057-5.2389-5.2389-5.2389h-3.1417v-8.3806c0-2.3047-1.8856-4.1903-4.1903-4.1903h-8.3806v-3.1417c0.20951-2.9332-2.0968-5.2389-5.03-5.2389z"/>
|
|
49
|
+
</g>
|
|
50
|
+
</svg>`
|
|
51
|
+
|
|
52
|
+
const ICON_CLOSE = `<svg version="1.1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
|
53
|
+
<g stroke="currentColor" stroke-linecap="round" stroke-width="5">
|
|
54
|
+
<path d="M4 4 L28 28"/>
|
|
55
|
+
<path d="M4 28 L28 4"/>
|
|
56
|
+
</g>
|
|
57
|
+
</svg>`
|
|
58
|
+
|
|
59
|
+
const ICON_PK = `<svg version="1.1" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
|
|
60
|
+
<g stroke-linecap="round" stroke-linejoin="round">
|
|
61
|
+
<rect x="11" y="8" width="26" height="32" fill="#fc0" stroke="#ffd42a" stroke-width="2"/>
|
|
62
|
+
<ellipse cx="23.333" cy="15.79" rx="8.6667" ry="4.2097" fill="none" stroke="#540" stroke-width="4"/>
|
|
63
|
+
<path d="m22 22.419v13.161" fill="none" stroke="#540" stroke-width="4"/>
|
|
64
|
+
<path d="m22 29.387h7.3333" fill="none" stroke="#540" stroke-width="4"/>
|
|
65
|
+
<path d="m22 35.581h10.667" fill="none" stroke="#540" stroke-width="4"/>
|
|
66
|
+
</g>
|
|
67
|
+
</svg>`
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
// let drop_valid = false
|
|
71
|
+
|
|
72
|
+
const CURRENT = {
|
|
73
|
+
drag_action: null,
|
|
74
|
+
drop_valid: false,
|
|
75
|
+
entity_id: null,
|
|
76
|
+
Design: null,
|
|
77
|
+
droptarget: null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
const generateId = (prefix = "el") => {
|
|
82
|
+
return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 5)}`;
|
|
83
|
+
}
|
|
84
|
+
const isValidName = str => /^[_a-z0-9]+$/.test(str) ;
|
|
85
|
+
|
|
86
|
+
const capitalizeFirst = (str) => {
|
|
87
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const createInputElement = (type, entity_id, data={}) => {
|
|
91
|
+
const input = document.createElement('input')
|
|
92
|
+
input.setAttribute('type', type)
|
|
93
|
+
input.setAttribute(ATTR_NAME, data.name)
|
|
94
|
+
input.setAttribute(ATTR_ENTITYID, entity_id)
|
|
95
|
+
input.value = data.value
|
|
96
|
+
return input
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default class AppGenUI {
|
|
100
|
+
#app
|
|
101
|
+
|
|
102
|
+
constructor(app) {
|
|
103
|
+
this.#app = app
|
|
104
|
+
|
|
105
|
+
IO.addFunction('addUnique', AppGenLayout_addUnique)
|
|
106
|
+
IO.addFunction('addSearch', AppGenLayout_addSearch)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get App() { return this.#app}
|
|
110
|
+
|
|
111
|
+
async NewData() {
|
|
112
|
+
await AppGenLayout_NewData(this)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async Init(context) {
|
|
116
|
+
this.Context = context
|
|
117
|
+
await AppGenLayout_Render(this, context)
|
|
118
|
+
AppGenLayout_NewData(this)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async getCurrentData() {
|
|
122
|
+
return await IO.getCurrentData()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
setCurrentId(id) {
|
|
126
|
+
const obj_programid = document.getElementById('obj_programid')
|
|
127
|
+
obj_programid.value = id
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
load(data) {
|
|
131
|
+
IO.load(data)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pauseAutoSave(pause) {
|
|
135
|
+
IO.pauseAutoSave(pause)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async updateCache() {
|
|
139
|
+
IO.updateCache()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async reset() {
|
|
143
|
+
await IO.reset()
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
async function AppGenLayout_Render(self, context) {
|
|
150
|
+
ME.IconButton = document.getElementById('upload-icon')
|
|
151
|
+
ME.ComponentList = document.getElementById('crud-component-list')
|
|
152
|
+
ME.DesignTemplate = document.getElementById('DESIGNTEMPLATE')
|
|
153
|
+
ME.DataEntities = document.getElementById('data-entities')
|
|
154
|
+
ME.EntityDesigner = document.getElementById('entities-design')
|
|
155
|
+
ME.LayoutEditor = document.getElementById('layout-editor')
|
|
156
|
+
ME.LayoutSidebar = document.getElementById('layout-sidebar')
|
|
157
|
+
// ME.EntityDesigner = document.getElementById('entities-design')
|
|
158
|
+
ME.tbl_entity = document.getElementById('tbl_entity')
|
|
159
|
+
ME.btn_addEntity = document.getElementById('btn_addentity')
|
|
160
|
+
ME.btn_addEntity.addEventListener('click', (evt)=>{
|
|
161
|
+
btn_addEntity_click(self, evt)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
AppGenLayout_createComponentList(this)
|
|
165
|
+
AppGenLayout_createButtons(this)
|
|
166
|
+
AppGenLayout_handleActionForm(this)
|
|
167
|
+
|
|
168
|
+
AppGenLayout_setUniqueME(ME)
|
|
169
|
+
AppGenLayout_setSearchME(ME)
|
|
170
|
+
|
|
171
|
+
console.log(context.appsUrls)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
IO.Setup({
|
|
175
|
+
AddEntity: (data) => {
|
|
176
|
+
AppGenLayout_AddEntity(self, data)
|
|
177
|
+
},
|
|
178
|
+
startDesign: (entity_id, suppress) => {
|
|
179
|
+
AppGenLayout_startDesign(self, entity_id, suppress)
|
|
180
|
+
},
|
|
181
|
+
addComponentToDesigner: (droptarget, comp) => {
|
|
182
|
+
return AppGenLayout_addComponentToDesigner(self, droptarget, comp)
|
|
183
|
+
},
|
|
184
|
+
addAction: (data) => {
|
|
185
|
+
AppGenLayout_addAction(self, data)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function AppGenLayout_createButtons(self) {
|
|
192
|
+
// upload image icon
|
|
193
|
+
ME.IconButton.style.backgroundImage = `url('data:image/svg+xml,${encodeURIComponent(ICON_DEFAULT)}')`;
|
|
194
|
+
console.log(ME.IconButton.style.backgroundImage)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
const btnUpload = document.getElementById('btn_IconUpload')
|
|
198
|
+
btnUpload.addEventListener('change', (evt)=>{
|
|
199
|
+
const file = btnUpload.files[0];
|
|
200
|
+
if (!file) return;
|
|
201
|
+
|
|
202
|
+
const validTypes = ["image/png", "image/svg+xml"];
|
|
203
|
+
if (!validTypes.includes(file.type)) {
|
|
204
|
+
alert("Hanya file PNG atau SVG yang diperbolehkan.");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const reader = new FileReader();
|
|
209
|
+
reader.onload = function (e) {
|
|
210
|
+
ME.IconButton.style.backgroundImage = `url('${e.target.result}')`
|
|
211
|
+
};
|
|
212
|
+
reader.readAsDataURL(file);
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
//button design summary
|
|
217
|
+
const btn_ShowSummary = document.getElementById('btn_ShowSummary')
|
|
218
|
+
btn_ShowSummary.addEventListener('click', (evt)=>{
|
|
219
|
+
btn_ShowSummary_click(self, evt)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
// button design detil
|
|
223
|
+
const btn_ShowDetail = document.getElementById('btn_ShowDetail')
|
|
224
|
+
btn_ShowDetail.addEventListener('click', (evt)=>{
|
|
225
|
+
btn_ShowDetail_click(self, evt)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
// menampilkan design fields
|
|
230
|
+
const btn_ShowFields = document.getElementById('btn_ShowFields')
|
|
231
|
+
btn_ShowFields.addEventListener('click', (evt)=>{
|
|
232
|
+
btn_ShowFields_click(self, evt)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// menampilkan design uniq
|
|
236
|
+
const btn_ShowUniq = document.getElementById('btn_ShowUniq')
|
|
237
|
+
btn_ShowUniq.addEventListener('click', (evt)=>{
|
|
238
|
+
btn_ShowUniq_click(self, evt)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
// menampilkan design search
|
|
242
|
+
const btn_ShowSearch = document.getElementById('btn_ShowSearch')
|
|
243
|
+
btn_ShowSearch.addEventListener('click', (evt)=>{
|
|
244
|
+
btn_ShowSearch_click(self, evt)
|
|
245
|
+
})
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function btn_ShowSummary_click(self, evt) {
|
|
249
|
+
CURRENT.Design.classList.add('design-view-as-summary')
|
|
250
|
+
|
|
251
|
+
const entitties = CURRENT.Design.querySelectorAll('div[name="design-data-field"]')
|
|
252
|
+
entitties.forEach(el=>{
|
|
253
|
+
el.classList.remove('minimized')
|
|
254
|
+
el.classList.remove('maximized')
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
const droptarget = CURRENT.Design.querySelector('div[name="drop-target"')
|
|
258
|
+
droptarget.classList.add('minimized-drop-target')
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function btn_ShowDetail_click(self, evt) {
|
|
262
|
+
CURRENT.Design.classList.remove('design-view-as-summary')
|
|
263
|
+
const entitties = CURRENT.Design.querySelectorAll('div[name="design-data-field"]')
|
|
264
|
+
entitties.forEach(el=>{
|
|
265
|
+
el.classList.remove('minimized')
|
|
266
|
+
el.classList.remove('maximized')
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
const droptarget = CURRENT.Design.querySelector('div[name="drop-target"')
|
|
270
|
+
droptarget.classList.remove('minimized-drop-target')
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function showOnly(toShow) {
|
|
274
|
+
var nodes = CURRENT.Design.children
|
|
275
|
+
for (var node of nodes) {
|
|
276
|
+
var name = node.getAttribute('name')
|
|
277
|
+
if (toShow.includes(name)) {
|
|
278
|
+
node.classList.remove('hidden')
|
|
279
|
+
} else {
|
|
280
|
+
node.classList.add('hidden')
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function btn_ShowFields_click(self, evt) {
|
|
287
|
+
showOnly(['designer-info', 'design-data-field'])
|
|
288
|
+
|
|
289
|
+
// tampilkan kembali drop target
|
|
290
|
+
const entity_id = CURRENT.entity_id
|
|
291
|
+
const designer = ME.EntityDesigner.querySelector(`div[${ATTR_ENTITYID}="${entity_id}"]`)
|
|
292
|
+
let droptarget = designer.querySelector(`[name="${ATTR_DROPTARGET}"]`)
|
|
293
|
+
if (droptarget!=null) {
|
|
294
|
+
droptarget.classList.remove('hidden')
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function btn_ShowUniq_click(self, evt) {
|
|
300
|
+
showOnly(['designer-info', 'designer-unique'])
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function btn_ShowSearch_click(self, evt) {
|
|
304
|
+
showOnly(['designer-info', 'designer-search'])
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
function btn_addEntity_click(self, evt) {
|
|
314
|
+
ME.btn_addEntity.style.animation = 'tombolDiTekanMenghilang 0.3s forwards'
|
|
315
|
+
setTimeout(()=>{
|
|
316
|
+
ME.btn_addEntity.classList.add('hidden')
|
|
317
|
+
ME.btn_addEntity.style.animation = 'unset'
|
|
318
|
+
AppGenLayout_AddEntity(self, {})
|
|
319
|
+
}, 300)
|
|
320
|
+
|
|
321
|
+
// nanti muncul lagi setelah 1 detil
|
|
322
|
+
setTimeout(()=>{
|
|
323
|
+
ME.btn_addEntity.classList.remove('hidden')
|
|
324
|
+
ME.btn_addEntity.style.animation = 'tombolMunculLagi 0.3s forwards'
|
|
325
|
+
setTimeout(()=>{
|
|
326
|
+
ME.btn_addEntity.style.animation = 'unset'
|
|
327
|
+
}, 300)
|
|
328
|
+
}, 1000)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function AppGenLayout_AddEntity(self, entity={}) {
|
|
337
|
+
const ID = entity[ENT_COL_ID] ?? generateId('en')
|
|
338
|
+
const tbl_entity = ME.tbl_entity
|
|
339
|
+
const tbody = tbl_entity.querySelector('tbody')
|
|
340
|
+
const cols = tbl_entity.querySelectorAll('thead > tr th')
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
const newtr = document.createElement('tr')
|
|
344
|
+
newtr.setAttribute(ATTR_ENTITYID, ID)
|
|
345
|
+
|
|
346
|
+
if (entity.isheader) {
|
|
347
|
+
newtr.setAttribute('data-isheader', '')
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
for (var th of cols) {
|
|
351
|
+
const name = th.getAttribute(ATTR_NAME)
|
|
352
|
+
const td = document.createElement('td')
|
|
353
|
+
td.setAttribute(ATTR_NAME, name)
|
|
354
|
+
newtr.appendChild(td)
|
|
355
|
+
}
|
|
356
|
+
tbody.appendChild(newtr)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
const fn_set_input = (type, value, entity_column_identity, isheader) => {
|
|
362
|
+
const col = newtr.querySelector(`td[${ATTR_NAME}="${entity_column_identity}"]`)
|
|
363
|
+
const data = {}
|
|
364
|
+
data[`${ATTR_ENTITYID}`] = ID
|
|
365
|
+
data[`${ATTR_NAME}`] = entity_column_identity
|
|
366
|
+
data['value'] = value ?? ""
|
|
367
|
+
const input = col.appendChild(createInputElement(type, ID, data))
|
|
368
|
+
|
|
369
|
+
input.setAttribute('autocomplete', 'off')
|
|
370
|
+
if (type=='text') {
|
|
371
|
+
input.addEventListener('change', (evt)=>{
|
|
372
|
+
AppGenLayout_entityInputChanged(self, evt)
|
|
373
|
+
})
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
if (isheader===true) {
|
|
378
|
+
type='hidden'
|
|
379
|
+
input.setAttribute('type', 'hidden')
|
|
380
|
+
}
|
|
381
|
+
if (type=='hidden') {
|
|
382
|
+
col.innerHTML = `<span>${data['value']}</span>`
|
|
383
|
+
}
|
|
384
|
+
col.appendChild(input)
|
|
385
|
+
return input
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
fn_set_input('hidden', ID, ENT_COL_ID, false)
|
|
389
|
+
fn_set_input('text', entity[ENT_COL_NAME], ENT_COL_NAME, entity.isheader)
|
|
390
|
+
fn_set_input('text', entity[ENT_COL_TITLE], ENT_COL_TITLE)
|
|
391
|
+
fn_set_input('text', entity[ENT_COL_TABLE], ENT_COL_TABLE)
|
|
392
|
+
fn_set_input('text', entity[ENT_COL_PK], ENT_COL_PK)
|
|
393
|
+
|
|
394
|
+
let btn_design = fn_set_input('button', "design", ENT_COL_BTNDESIGN)
|
|
395
|
+
btn_design.addEventListener('click', (evt)=>{
|
|
396
|
+
btn_design_click(self, evt)
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
if (entity.isheader!==true) {
|
|
400
|
+
let btn_remove = fn_set_input('button', "remove", ENT_COL_BTNREMOVE)
|
|
401
|
+
btn_remove.addEventListener('click', (evt)=>{
|
|
402
|
+
btn_remove_click(self, evt)
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
// tambahkan ke ME.EntityDesigner
|
|
409
|
+
AppGenLayout_addDesigner(self, ID, entity.isheader)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
return ID
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
async function AppGenLayout_entityInputChanged(self, evt) {
|
|
417
|
+
const input = evt.target
|
|
418
|
+
const name = input.getAttribute(ATTR_NAME)
|
|
419
|
+
const entity_id = input.getAttribute(ATTR_ENTITYID)
|
|
420
|
+
const value = input.value
|
|
421
|
+
|
|
422
|
+
const data = {}
|
|
423
|
+
data[name] = value
|
|
424
|
+
|
|
425
|
+
const valid = await AppGenLayout_CekEntityData(self, data)
|
|
426
|
+
if (valid) {
|
|
427
|
+
AppGenLayout_changeEntityInfo(self, entity_id, data)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (name==ENT_COL_PK) {
|
|
431
|
+
AppGenLayout_designerFieldNameChanged(self, entity_id, input, true)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
async function AppGenLayout_CekEntityData(self, data) {
|
|
438
|
+
for (var name in data) {
|
|
439
|
+
if (data[name]==null || data[name].trim()=='') {
|
|
440
|
+
await $fgta5.MessageBox.warning("lengkapi dahulu semua data entity")
|
|
441
|
+
return false
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const rule = "hanya boleh alphabet dan angka, dengan huruf kecil,<br>tanpa spasi, tanpa character khusus."
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
if (data[ENT_COL_NAME]!==undefined) {
|
|
449
|
+
if (!isValidName(data[ENT_COL_NAME].trim())) {
|
|
450
|
+
await $fgta5.MessageBox.warning(`nama entity: <span style="font-weight:bold; color:red">${data[ENT_COL_NAME]}</span> tidak valid!<br>${rule}`)
|
|
451
|
+
return false
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/*
|
|
456
|
+
if (data[ENT_COL_TABLE]!==undefined) {
|
|
457
|
+
if (!isValidName(data[ENT_COL_TABLE].trim())) {
|
|
458
|
+
await $fgta5.MessageBox.warning(`nama table: <span style="font-weight:bold; color:red">${data[ENT_COL_TABLE]}</span> tidak valid!<br>${rule}`)
|
|
459
|
+
return false
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
*/
|
|
463
|
+
|
|
464
|
+
if (data[ENT_COL_PK]!==undefined) {
|
|
465
|
+
if (!isValidName(data[ENT_COL_PK].trim())) {
|
|
466
|
+
await $fgta5.MessageBox.warning(`nama PK: <span style="font-weight:bold; color:red">${data[ENT_COL_PK]}</span> tidak valid!<br>${rule}`)
|
|
467
|
+
return false
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return true
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
async function btn_design_click(self, evt) {
|
|
476
|
+
const btn = evt.target
|
|
477
|
+
const entity_id = btn.getAttribute(ATTR_ENTITYID)
|
|
478
|
+
const editores = ME.EntityDesigner.querySelectorAll(`div[name="${ID_ENTITYEDITOR}"]`)
|
|
479
|
+
const tr = btn.closest('tr')
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
const data = AppGenLayout_GetEntityData(self, tr)
|
|
483
|
+
const valid = await AppGenLayout_CekEntityData(self, data)
|
|
484
|
+
|
|
485
|
+
if (!valid) {
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
for (let el of editores) {
|
|
490
|
+
let ceid = el.getAttribute(ATTR_ENTITYID)
|
|
491
|
+
if (ceid==entity_id) {
|
|
492
|
+
el.classList.remove(CLS_HIDDEN)
|
|
493
|
+
AppGenLayout_entityDesign(self, entity_id, tr)
|
|
494
|
+
AppGenLayout_startDesign(self, entity_id)
|
|
495
|
+
} else {
|
|
496
|
+
el.classList.add(CLS_HIDDEN)
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
function AppGenLayout_GetEntityData(self, tr) {
|
|
503
|
+
const ens = [ENT_COL_NAME, ENT_COL_TITLE, ENT_COL_TABLE, ENT_COL_PK]
|
|
504
|
+
const data = {}
|
|
505
|
+
|
|
506
|
+
for (var name of ens) {
|
|
507
|
+
const obj = tr.querySelector(`td[name="${name}"] input`)
|
|
508
|
+
data[name] = obj.value
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return data
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
function AppGenLayout_entityDesign(self, entity_id, tr) {
|
|
516
|
+
const data = AppGenLayout_GetEntityData(self, tr)
|
|
517
|
+
AppGenLayout_changeEntityInfo(self, entity_id, data)
|
|
518
|
+
|
|
519
|
+
// tandaii tool: ATTR_CURRENTENTITY
|
|
520
|
+
let arr = Array.from(ME.ComponentList.children)
|
|
521
|
+
for (let el of arr) {
|
|
522
|
+
el.setAttribute(ATTR_CURRENTENTITY, entity_id)
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
function AppGenLayout_changeEntityInfo(self, entity_id, data) {
|
|
528
|
+
const info = ME.EntityDesigner.querySelector(`div[${ATTR_ENTITYID}="${entity_id}"] div[name="${ID_DESIGNERINFO}"]`)
|
|
529
|
+
const ens = [ENT_COL_NAME, ENT_COL_TITLE, ENT_COL_TABLE, ENT_COL_PK]
|
|
530
|
+
for (var name of ens) {
|
|
531
|
+
if (data[name]!==undefined) {
|
|
532
|
+
const ed = info.querySelector(`div[name="${name}"]`)
|
|
533
|
+
ed.innerHTML = data[name]
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
async function btn_remove_click(self, evt) {
|
|
540
|
+
const btn = evt.target
|
|
541
|
+
const tr = btn.closest('tr')
|
|
542
|
+
const data = AppGenLayout_GetEntityData(self, tr)
|
|
543
|
+
|
|
544
|
+
var sudahadadata = false
|
|
545
|
+
for (var name in data) {
|
|
546
|
+
if (data[name].trim()!='') {
|
|
547
|
+
sudahadadata = true
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const remove = (tr) => {
|
|
552
|
+
var entity_id = tr.getAttribute(ATTR_ENTITYID)
|
|
553
|
+
var editor = ME.EntityDesigner.querySelector(`[${ATTR_ENTITYID}="${entity_id}"]`)
|
|
554
|
+
if (editor!=null) {
|
|
555
|
+
editor.remove()
|
|
556
|
+
}
|
|
557
|
+
tr.remove()
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
if (sudahadadata) {
|
|
562
|
+
var ret = await $fgta5.MessageBox.confirm("Apakah anda yakin mau menghapus design entity ini?")
|
|
563
|
+
if (ret=='ok') {
|
|
564
|
+
remove(tr)
|
|
565
|
+
}
|
|
566
|
+
} else {
|
|
567
|
+
remove(tr)
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
function AppGenLayout_addDesigner(self, ID, isheader) {
|
|
575
|
+
|
|
576
|
+
const editem = document.createElement('div')
|
|
577
|
+
editem.classList.add('hidden')
|
|
578
|
+
editem.classList.add(CLS_ENTITYEDITOR)
|
|
579
|
+
editem.setAttribute('name', ID_ENTITYEDITOR)
|
|
580
|
+
editem.setAttribute(ATTR_ENTITYID, ID)
|
|
581
|
+
|
|
582
|
+
// ambil data template untuk entity info
|
|
583
|
+
const tplInfo = ME.DesignTemplate.querySelector(`div[name="${ID_DESIGNERINFO}"]`)
|
|
584
|
+
const elinfo = tplInfo.cloneNode(true)
|
|
585
|
+
|
|
586
|
+
// ambil data template uniq design
|
|
587
|
+
const tplUniq = ME.DesignTemplate.querySelector(`div[name="${ID_DESIGNERUNIQ}"]`)
|
|
588
|
+
const eluniq = tplUniq.cloneNode(true)
|
|
589
|
+
|
|
590
|
+
// ambil data template uniq search
|
|
591
|
+
const tplSearch = ME.DesignTemplate.querySelector(`div[name="${ID_DESIGNERSEARCH}"]`)
|
|
592
|
+
const elsearch = tplSearch.cloneNode(true)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
// console.log(ME.EntityDesigner)
|
|
597
|
+
// eluniq.innerHTML = `designer unig ${ID}`
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
editem.appendChild(elinfo)
|
|
602
|
+
editem.appendChild(eluniq)
|
|
603
|
+
editem.appendChild(elsearch)
|
|
604
|
+
|
|
605
|
+
const chka = elinfo.querySelector('input[type="checkbox"][name="allow-row-add"]')
|
|
606
|
+
const chkr = elinfo.querySelector('input[type="checkbox"][name="allow-row-remove"]')
|
|
607
|
+
|
|
608
|
+
chka.checked = true
|
|
609
|
+
chkr.checked = true
|
|
610
|
+
|
|
611
|
+
if (isheader===true) {
|
|
612
|
+
chka.disabled = true
|
|
613
|
+
chkr.disabled = true
|
|
614
|
+
const dcs = elinfo.querySelectorAll('[data-control]')
|
|
615
|
+
for (var dc of dcs) {
|
|
616
|
+
dc.classList.add('hidden')
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
} else {
|
|
620
|
+
const dai = elinfo.querySelectorAll('[data-autoid]')
|
|
621
|
+
for (var ai of dai) {
|
|
622
|
+
ai.classList.add('hidden')
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// setup designer uniq & search
|
|
627
|
+
AppGenLayout_setupUniqueDesigner(eluniq, ID)
|
|
628
|
+
AppGenLayout_setupSearchDesigner(elsearch, ID)
|
|
629
|
+
|
|
630
|
+
// tambahkan editor ke designer
|
|
631
|
+
ME.EntityDesigner.appendChild(editem)
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
async function AppGenLayout_NewData(self) {
|
|
655
|
+
// jika belum ada header
|
|
656
|
+
const tbl_entity = ME.tbl_entity
|
|
657
|
+
const tbody = tbl_entity.querySelector('tbody')
|
|
658
|
+
const entity_id = await AppGenLayout_AddEntity(self, {
|
|
659
|
+
isheader: true,
|
|
660
|
+
col_name: 'header',
|
|
661
|
+
col_title: 'programtitle',
|
|
662
|
+
col_table: `schema.tablename`,
|
|
663
|
+
col_pk: 'pk'
|
|
664
|
+
})
|
|
665
|
+
|
|
666
|
+
const btn = tbody.querySelector(`[name="col_btndesign"][${ATTR_ENTITYID}="${entity_id}"]`)
|
|
667
|
+
btn.click()
|
|
668
|
+
|
|
669
|
+
setTimeout(()=>{
|
|
670
|
+
IO.AutoSave()
|
|
671
|
+
}, 2000)
|
|
672
|
+
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
function AppGenLayout_highlightElement(self, droptarget) {
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
droptarget.style.animation = 'pulseHighlight 1s forwards'
|
|
680
|
+
setTimeout(()=>{
|
|
681
|
+
droptarget.style.animation = 'unset'
|
|
682
|
+
}, 1000)
|
|
683
|
+
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function AppGenLayout_startDesign(self, entity_id, suppress) {
|
|
687
|
+
if (suppress===undefined) {
|
|
688
|
+
suppress = false
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
if (!suppress) {
|
|
693
|
+
ME.LayoutEditor.classList.remove('hidden')
|
|
694
|
+
ME.LayoutSidebar.classList.remove('hidden')
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
// buat drop target
|
|
699
|
+
// cek apakah sudah ada drop target
|
|
700
|
+
const designer = ME.EntityDesigner.querySelector(`div[${ATTR_ENTITYID}="${entity_id}"]`)
|
|
701
|
+
let droptarget = designer.querySelector(`[name="${ATTR_DROPTARGET}"]`)
|
|
702
|
+
if (droptarget==null) {
|
|
703
|
+
// blum ada, buat dulu
|
|
704
|
+
const tpl = ME.DesignTemplate.querySelector(`div[name="${ATTR_DROPTARGET}"]`)
|
|
705
|
+
droptarget = tpl.cloneNode(true)
|
|
706
|
+
droptarget.setAttribute(ATTR_ENTITYID, entity_id)
|
|
707
|
+
designer.appendChild(droptarget)
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// scroll ke element editor
|
|
711
|
+
designer.scrollIntoView({
|
|
712
|
+
behavior: 'smooth',
|
|
713
|
+
block: 'start'
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
AppGenLayout_setupDropTarget(self, droptarget)
|
|
717
|
+
|
|
718
|
+
// higlight drop target
|
|
719
|
+
AppGenLayout_highlightElement(self, droptarget)
|
|
720
|
+
|
|
721
|
+
CURRENT.entity_id = entity_id
|
|
722
|
+
CURRENT.Design = designer
|
|
723
|
+
CURRENT.droptarget = droptarget
|
|
724
|
+
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function AppGenLayout_setupDropTarget(self, droptarget) {
|
|
728
|
+
const dr = droptarget.getAttribute(ATTR_DROPTARGET)
|
|
729
|
+
if (dr===true) {
|
|
730
|
+
// drop target sudah di setup
|
|
731
|
+
return
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
droptarget.addEventListener('dragover', (evt)=>{
|
|
736
|
+
evt.preventDefault()
|
|
737
|
+
droptarget.setAttribute(ATTR_DRAGOVER, '')
|
|
738
|
+
})
|
|
739
|
+
|
|
740
|
+
droptarget.addEventListener('dragleave', (evt)=>{
|
|
741
|
+
droptarget.removeAttribute(ATTR_DRAGOVER)
|
|
742
|
+
// droptarget.classList.add('hidden')
|
|
743
|
+
})
|
|
744
|
+
|
|
745
|
+
droptarget.addEventListener('drop', (evt)=>{
|
|
746
|
+
CURRENT.drop_valid = true
|
|
747
|
+
droptarget.removeAttribute(ATTR_DRAGOVER)
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
if (CURRENT.drag_action==DRAG_ICONTOOL) {
|
|
751
|
+
const compname = evt.dataTransfer.getData('compname');
|
|
752
|
+
AppGenLayout_addComponentToDesigner(self, droptarget, Components[compname])
|
|
753
|
+
|
|
754
|
+
setTimeout(()=>{
|
|
755
|
+
CURRENT.Design.appendChild(droptarget)
|
|
756
|
+
droptarget.scrollIntoView({
|
|
757
|
+
behavior: 'smooth',
|
|
758
|
+
block: 'start'
|
|
759
|
+
});
|
|
760
|
+
}, 300)
|
|
761
|
+
|
|
762
|
+
} else if (CURRENT.drag_action==DRAG_REORDERFIELD) {
|
|
763
|
+
const datafield_id = evt.dataTransfer.getData('datafield_id');
|
|
764
|
+
const el = document.getElementById(datafield_id)
|
|
765
|
+
droptarget.after(el)
|
|
766
|
+
setTimeout(()=>{
|
|
767
|
+
droptarget.classList.add('hidden')
|
|
768
|
+
}, 300)
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
CURRENT.drag_action = ''
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
})
|
|
775
|
+
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
function AppGenLayout_createComponentList(self) {
|
|
780
|
+
const tpl = ME.DesignTemplate.querySelector(`div[name="${ID_ICONTOOL}"]`)
|
|
781
|
+
for (var name in Components) {
|
|
782
|
+
let comp = Components[name]
|
|
783
|
+
|
|
784
|
+
comp.name = name
|
|
785
|
+
var iconTool = AppGenLayout_createIconTool(self, comp, tpl)
|
|
786
|
+
ME.ComponentList.appendChild(iconTool)
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function AppGenLayout_createIconTool(self, comp, tpl) {
|
|
792
|
+
const tool = tpl.cloneNode(true)
|
|
793
|
+
const icon = tool.querySelector('div[data-icon')
|
|
794
|
+
const label = tool.querySelector('div[data-label]')
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
icon.innerHTML =comp.icon
|
|
798
|
+
label.innerHTML = comp.title
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
tool.addEventListener('dragstart', (evt)=>{
|
|
802
|
+
CURRENT.drop_valid = false
|
|
803
|
+
CURRENT.drag_action = DRAG_ICONTOOL
|
|
804
|
+
evt.dataTransfer.setData('compname', comp.name);
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
tool.addEventListener('dragend', (evt)=>{
|
|
808
|
+
if (CURRENT.droptarget==null) {
|
|
809
|
+
return
|
|
810
|
+
}
|
|
811
|
+
const fields = CURRENT.Design.querySelectorAll('[name="design-data-field"]')
|
|
812
|
+
if (fields.length==0) {
|
|
813
|
+
return
|
|
814
|
+
}
|
|
815
|
+
if (!CURRENT.drop_valid) {
|
|
816
|
+
CURRENT.droptarget.classList.add('hidden')
|
|
817
|
+
}
|
|
818
|
+
})
|
|
819
|
+
|
|
820
|
+
tool.addEventListener('dblclick', (evt)=>{
|
|
821
|
+
// pindah drop target ke bawah
|
|
822
|
+
// droptarget.classList.remove('hidden')
|
|
823
|
+
CURRENT.droptarget.classList.remove('hidden')
|
|
824
|
+
CURRENT.Design.appendChild(CURRENT.droptarget)
|
|
825
|
+
AppGenLayout_addComponentToDesigner(self, CURRENT.droptarget, comp)
|
|
826
|
+
|
|
827
|
+
// terus sroll ke bawah
|
|
828
|
+
setTimeout(()=>{
|
|
829
|
+
CURRENT.droptarget.scrollIntoView({
|
|
830
|
+
behavior: 'smooth',
|
|
831
|
+
block: 'start'
|
|
832
|
+
});
|
|
833
|
+
}, 300)
|
|
834
|
+
})
|
|
835
|
+
|
|
836
|
+
return tool
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
function AppGenLayout_addComponentToDesigner(self, droptarget, comp) {
|
|
841
|
+
const tpl = ME.DesignTemplate.querySelector(`div[name="${ID_DESIGNFIELD}"][data-template=${comp.template}]`)
|
|
842
|
+
const datafield = tpl.cloneNode(true)
|
|
843
|
+
const entity_id = droptarget.getAttribute(ATTR_ENTITYID)
|
|
844
|
+
|
|
845
|
+
const datafield_id = generateId('datafield')
|
|
846
|
+
datafield.setAttribute('id', datafield_id)
|
|
847
|
+
datafield.setAttribute(ATTR_ENTITYID, entity_id)
|
|
848
|
+
datafield.setAttribute(ATTR_COMPNAME, comp.name)
|
|
849
|
+
|
|
850
|
+
// masukkan element baru sebelum drop target
|
|
851
|
+
droptarget.before(datafield)
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
// setup datafiled
|
|
855
|
+
const comptype = datafield.querySelector('[name="component-type"]')
|
|
856
|
+
|
|
857
|
+
const compicon = comptype.querySelector('[name="icon"]')
|
|
858
|
+
const comptitle = comptype.querySelector('[name="title"]')
|
|
859
|
+
comptitle.innerHTML = comp.title
|
|
860
|
+
|
|
861
|
+
const compsummary = datafield.querySelector('[name="component-summary"]')
|
|
862
|
+
const compiconsummary = compsummary.querySelector('[name="icon"]')
|
|
863
|
+
|
|
864
|
+
const compiconspk = datafield.querySelectorAll('[name="icon-pk"]')
|
|
865
|
+
for (var iconspk of compiconspk) {
|
|
866
|
+
iconspk.setAttribute(ATTR_ENTITYID, entity_id)
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
compsummary.setAttribute('draggable', true)
|
|
870
|
+
compsummary.addEventListener('dragstart', (evt)=>{
|
|
871
|
+
CURRENT.drop_valid = false
|
|
872
|
+
CURRENT.drag_action = DRAG_REORDERFIELD
|
|
873
|
+
evt.dataTransfer.setData('datafield_id', datafield_id);
|
|
874
|
+
})
|
|
875
|
+
compsummary.addEventListener('dragend', (evt)=>{
|
|
876
|
+
if (CURRENT.droptarget==null) {
|
|
877
|
+
return
|
|
878
|
+
}
|
|
879
|
+
if (!CURRENT.drop_valid) {
|
|
880
|
+
CURRENT.droptarget.classList.add('hidden')
|
|
881
|
+
}
|
|
882
|
+
})
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
// copy icon dari detil design ke summary design
|
|
887
|
+
compiconsummary.innerHTML = compicon.innerHTML
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
const obj_namesummary = datafield.querySelector('input[name="fieldname-summary"]')
|
|
892
|
+
obj_namesummary.addEventListener('dblclick', (evt)=>{
|
|
893
|
+
evt.stopPropagation() // mencegar trigger maximize/minimized saat dblclick textbox
|
|
894
|
+
})
|
|
895
|
+
|
|
896
|
+
// kalau data field dilewati DRAG ICON, munculkan droptarget di bawahnya
|
|
897
|
+
datafield.addEventListener('dragover', (evt)=>{
|
|
898
|
+
if (CURRENT.drag_action==DRAG_ICONTOOL || CURRENT.drag_action==DRAG_REORDERFIELD) {
|
|
899
|
+
evt.preventDefault()
|
|
900
|
+
droptarget.classList.remove('hidden')
|
|
901
|
+
|
|
902
|
+
const rect = datafield.getBoundingClientRect();
|
|
903
|
+
const offsetY = evt.clientY - rect.top;
|
|
904
|
+
const offsetX = evt.clientX - rect.left;
|
|
905
|
+
|
|
906
|
+
if (offsetX > rect.width - 100) {
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
if (offsetY < rect.height / 2) {
|
|
912
|
+
setTimeout(()=>{
|
|
913
|
+
datafield.before(droptarget)
|
|
914
|
+
}, 300)
|
|
915
|
+
|
|
916
|
+
} else {
|
|
917
|
+
setTimeout(()=>{
|
|
918
|
+
datafield.after(droptarget)
|
|
919
|
+
}, 300)
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
}
|
|
923
|
+
})
|
|
924
|
+
|
|
925
|
+
// handle button close
|
|
926
|
+
const btncs = datafield.querySelectorAll(`[class="field-remove-button"]`)
|
|
927
|
+
for (const btn of btncs) {
|
|
928
|
+
btn.innerHTML = ICON_CLOSE
|
|
929
|
+
btn.addEventListener('click', async (evt)=>{
|
|
930
|
+
var res = await $fgta5.MessageBox.confirm('removing field is irreversible. Are you sure ?')
|
|
931
|
+
if (res=='ok') {
|
|
932
|
+
// hapus
|
|
933
|
+
datafield.style.animation = 'fieldDihapus 0.3s forwards'
|
|
934
|
+
setTimeout(()=>{
|
|
935
|
+
datafield.remove()
|
|
936
|
+
}, 300)
|
|
937
|
+
}
|
|
938
|
+
})
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
const bars = datafield.querySelectorAll("div[field-handle-bar]")
|
|
943
|
+
for (let bar of bars) {
|
|
944
|
+
bar.addEventListener('dblclick', (evt)=>{
|
|
945
|
+
const varname = bar.getAttribute('name')
|
|
946
|
+
if (varname=='component-summary') {
|
|
947
|
+
datafield.classList.add('maximized')
|
|
948
|
+
datafield.classList.remove('minimized')
|
|
949
|
+
} else {
|
|
950
|
+
datafield.classList.add('minimized')
|
|
951
|
+
datafield.classList.remove('maximized')
|
|
952
|
+
}
|
|
953
|
+
})
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
AppGenGenLayout_HandleDataField(self, entity_id, comp, datafield)
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
return datafield
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
function AppGenLayout_designerFieldNameChanged(self, entity_id, obj, fromentity) {
|
|
964
|
+
const query = `tr[${ATTR_ENTITYID}="${entity_id}"] [name="${ENT_COL_PK}"] input`
|
|
965
|
+
const elpk = ME.DataEntities.querySelector(query)
|
|
966
|
+
|
|
967
|
+
const design = ME.EntityDesigner.querySelector(`[name="entity-editor"][${ATTR_ENTITYID}="${entity_id}"]`)
|
|
968
|
+
const datatypeEl = design.querySelector('select[name="datatype"]')
|
|
969
|
+
datatypeEl.disabled = false
|
|
970
|
+
|
|
971
|
+
console.log('AppGenLayout_designerFieldNameChanged')
|
|
972
|
+
const setPk = (df, primary_key) => {
|
|
973
|
+
// reset seluruh primary key
|
|
974
|
+
const alliconspk = df.querySelectorAll('[name="icon-pk"]')
|
|
975
|
+
for (var icoalpk of alliconspk) {
|
|
976
|
+
icoalpk.innerHTML = ''
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
const field = df.querySelector(`input[name="fieldname"][${ATTR_ENTITYID}="${entity_id}"]`)
|
|
980
|
+
const current_fieldname_value = field.value
|
|
981
|
+
if (current_fieldname_value==primary_key) {
|
|
982
|
+
const iconspk = df.querySelectorAll(`[name="icon-pk"][${ATTR_ENTITYID}="${entity_id}"]`)
|
|
983
|
+
for (var ico of iconspk) {
|
|
984
|
+
ico.innerHTML = ICON_PK
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
// untuk primary key detail tipe data otomatis di set ke bigInt
|
|
991
|
+
// ambil enentity header untuk pengecekan tipe data
|
|
992
|
+
if (current_fieldname_value==primary_key) {
|
|
993
|
+
const entityHeader = ME.DataEntities.querySelector('tr[data-isheader')
|
|
994
|
+
const headerEntityId = entityHeader.getAttribute(ATTR_ENTITYID)
|
|
995
|
+
if (entity_id!=headerEntityId) {
|
|
996
|
+
// set tipedata untuk PK di detil menjadi bigint
|
|
997
|
+
console.log('SET tO BIG INT')
|
|
998
|
+
datatypeEl.value = 'bigint'
|
|
999
|
+
datatypeEl.disabled = true
|
|
1000
|
+
// console.log(datatypeEl)
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
let primary_key
|
|
1008
|
+
if (fromentity===true) {
|
|
1009
|
+
// diedit dari tabel entity
|
|
1010
|
+
primary_key = obj.value
|
|
1011
|
+
|
|
1012
|
+
// cari semua field dengan valuenya primary_key
|
|
1013
|
+
const design = ME.EntityDesigner.querySelector(`[name="entity-editor"][${ATTR_ENTITYID}="${entity_id}"]`)
|
|
1014
|
+
const dfs = design.querySelectorAll('[name="design-data-field"]')
|
|
1015
|
+
for (const df of dfs) {
|
|
1016
|
+
setPk(df, primary_key)
|
|
1017
|
+
}
|
|
1018
|
+
} else {
|
|
1019
|
+
// diedit dari designer
|
|
1020
|
+
primary_key = elpk.value
|
|
1021
|
+
const df = obj.closest('[name="design-data-field"]') //cari [name="design-data-field"] terdekat
|
|
1022
|
+
setPk(df, primary_key)
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function AppGenLayout_handleActionForm(self) {
|
|
1034
|
+
const btn_action_add = document.getElementById('btn_action_add')
|
|
1035
|
+
btn_action_add.addEventListener('click', (evt)=>{
|
|
1036
|
+
const txt_action_name = document.getElementById('txt_action_name')
|
|
1037
|
+
const txt_action_title = document.getElementById('txt_action_title')
|
|
1038
|
+
const txt_action_permission = document.getElementById('txt_action_permission')
|
|
1039
|
+
|
|
1040
|
+
AppGenLayout_addAction(self, {
|
|
1041
|
+
name: txt_action_name.value,
|
|
1042
|
+
title: txt_action_title.value,
|
|
1043
|
+
permission: txt_action_permission.value
|
|
1044
|
+
})
|
|
1045
|
+
})
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
async function AppGenLayout_addAction(self, data) {
|
|
1049
|
+
const tbody = document.getElementById('action-lists')
|
|
1050
|
+
const name = data.name
|
|
1051
|
+
const title = data.title
|
|
1052
|
+
const permission = data.permission ?? ''
|
|
1053
|
+
|
|
1054
|
+
if (!isValidName(name)) {
|
|
1055
|
+
await $fgta5.MessageBox.warning('nama action tidak valid')
|
|
1056
|
+
return
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
if (title.trim()=='') {
|
|
1060
|
+
await $fgta5.MessageBox.warning('title tidak valid')
|
|
1061
|
+
return
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
const tr = document.createElement('tr')
|
|
1066
|
+
const tdName = document.createElement('td')
|
|
1067
|
+
const tdTitle = document.createElement('td')
|
|
1068
|
+
const tdPermission = document.createElement('td')
|
|
1069
|
+
const tdButton = document.createElement('td')
|
|
1070
|
+
const rmButton = document.createElement('div')
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
tdName.innerHTML = `<div name="action-name" class="action-cell">${name}</div>`
|
|
1074
|
+
tdTitle.innerHTML = `<div name="action-title" class="action-cell">${title}</div>`
|
|
1075
|
+
tdPermission.innerHTML = `<div name="action-title" class="action-cell">${permission}</div>`
|
|
1076
|
+
|
|
1077
|
+
rmButton.innerHTML = ICON_CLOSE
|
|
1078
|
+
rmButton.classList.add("action-button-remove")
|
|
1079
|
+
rmButton.addEventListener('click', async (evt)=>{
|
|
1080
|
+
var res = await $fgta5.MessageBox.confirm('Are you sure removing this action ?')
|
|
1081
|
+
if (res=='ok') {
|
|
1082
|
+
tr.remove()
|
|
1083
|
+
}
|
|
1084
|
+
})
|
|
1085
|
+
|
|
1086
|
+
tdButton.appendChild(rmButton)
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
tr.appendChild(tdName)
|
|
1090
|
+
tr.appendChild(tdTitle)
|
|
1091
|
+
tr.appendChild(tdPermission)
|
|
1092
|
+
tr.appendChild(tdButton)
|
|
1093
|
+
tbody.appendChild(tr)
|
|
1094
|
+
|
|
1095
|
+
txt_action_name.value = ''
|
|
1096
|
+
txt_action_title.value = ''
|
|
1097
|
+
txt_action_permission.value = ''
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
function AppGenGenLayout_HandleDataField(self, entity_id, comp, datafield) {
|
|
1101
|
+
const fieldname = datafield.querySelector('input[name="fieldname"]')
|
|
1102
|
+
const namesummary = datafield.querySelector('input[name="fieldname-summary"]')
|
|
1103
|
+
const objectname = datafield.querySelector('input[name="objectname"]')
|
|
1104
|
+
const labeltext = datafield.querySelector('input[name="labeltext"]')
|
|
1105
|
+
const placeholder = datafield.querySelector('input[name="placeholder"]')
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
const nameChanged = (value) => {
|
|
1111
|
+
if (objectname.value.trim()=='') {
|
|
1112
|
+
objectname.value = 'obj_' + fieldname.value
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
if (labeltext.value.trim()=='') {
|
|
1116
|
+
labeltext.value = capitalizeFirst(fieldname.value)
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if (placeholder.value.trim()=='') {
|
|
1120
|
+
placeholder.value = fieldname.value
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
const nameKeydown = (evt) => {
|
|
1125
|
+
const allowed = /^[a-zA-Z0-9_]$/; // hanya huruf, angka, dan underscore
|
|
1126
|
+
|
|
1127
|
+
if (
|
|
1128
|
+
evt.ctrlKey || evt.metaKey || evt.altKey || // biarkan shortcut seperti Ctrl+C
|
|
1129
|
+
evt.key === "Backspace" ||
|
|
1130
|
+
evt.key === "Tab" ||
|
|
1131
|
+
evt.key === "ArrowLeft" ||
|
|
1132
|
+
evt.key === "ArrowRight" ||
|
|
1133
|
+
evt.key === "Delete"
|
|
1134
|
+
) {
|
|
1135
|
+
return; // izinkan navigasi dan kontrol
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
if (!allowed.test(evt.key)) {
|
|
1139
|
+
evt.preventDefault(); // blokir karakter lain
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
|
|
1144
|
+
fieldname.setAttribute(ATTR_ENTITYID, entity_id)
|
|
1145
|
+
fieldname.addEventListener('change', (evt)=>{
|
|
1146
|
+
namesummary.value = fieldname.value
|
|
1147
|
+
const entity_id = fieldname.getAttribute(ATTR_ENTITYID)
|
|
1148
|
+
AppGenLayout_designerFieldNameChanged(self, entity_id, fieldname)
|
|
1149
|
+
nameChanged(fieldname.value)
|
|
1150
|
+
})
|
|
1151
|
+
|
|
1152
|
+
|
|
1153
|
+
namesummary.setAttribute(ATTR_ENTITYID, entity_id)
|
|
1154
|
+
namesummary.addEventListener('change', (evt)=>{
|
|
1155
|
+
fieldname.value = namesummary.value
|
|
1156
|
+
const entity_id = namesummary.getAttribute(ATTR_ENTITYID)
|
|
1157
|
+
AppGenLayout_designerFieldNameChanged(self, entity_id, namesummary)
|
|
1158
|
+
nameChanged(namesummary.value)
|
|
1159
|
+
})
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
fieldname.addEventListener('keydown', (evt)=>{
|
|
1163
|
+
nameKeydown(evt)
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
namesummary.addEventListener('keydown', (evt)=>{
|
|
1167
|
+
nameKeydown(evt)
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
if (comp.name=='Textbox') {
|
|
1172
|
+
const datalength = datafield.querySelector('input[name="datalength"]')
|
|
1173
|
+
const maximum = datafield.querySelector('input[name="maximum"]')
|
|
1174
|
+
maximum.value = datalength.value
|
|
1175
|
+
datalength.addEventListener('change', (evt)=>{
|
|
1176
|
+
maximum.value = datalength.value
|
|
1177
|
+
})
|
|
1178
|
+
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
}
|