@aimeloic/monkey-tester 4.0.3 → 4.0.5
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/html.backup.js +109 -101
- package/htmlTemplate.js +376 -120
- package/monkey.backup.js +50 -97
- package/monkey.js +38 -100
- package/package.json +1 -1
- package/temp.backup.js +279 -0
package/htmlTemplate.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
// This function is stringified safely to eliminate string template syntax bugs
|
|
3
|
+
// Original Sandbox Runtime — Stringified safely to browser context
|
|
5
4
|
function runtimeClientSandbox() {
|
|
6
5
|
const ENDPOINTS = JSON.parse(atob(document.getElementById('__monkey_data__').getAttribute('data-payload')));
|
|
7
6
|
let currentKey = null;
|
|
8
7
|
|
|
9
8
|
document.getElementById('base-url').value = window.location.origin;
|
|
10
9
|
|
|
10
|
+
// Auto-paste Token directly if it exists in local storage
|
|
11
|
+
const savedToken = localStorage.getItem('__auth_token__');
|
|
12
|
+
if (savedToken) {
|
|
13
|
+
document.getElementById('jwt-input').value = savedToken;
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
function buildSidebar() {
|
|
12
17
|
const sidebar = document.getElementById('sidebar-nav');
|
|
13
18
|
const keys = Object.keys(ENDPOINTS);
|
|
14
|
-
|
|
15
19
|
if (keys.length === 0) {
|
|
16
20
|
sidebar.innerHTML += '<div style="padding:18px;color:var(--text-dim);font-size:12px">No endpoints discovered.</div>';
|
|
17
21
|
return;
|
|
18
22
|
}
|
|
19
|
-
|
|
20
23
|
keys.forEach((key, i) => {
|
|
21
24
|
const ep = ENDPOINTS[key];
|
|
22
25
|
const item = document.createElement('div');
|
|
23
26
|
item.className = 'nav-item' + (i === 0 ? ' active' : '');
|
|
24
27
|
item.setAttribute('data-key', key);
|
|
25
|
-
item.innerHTML =
|
|
26
|
-
'<span class="method-badge ' + ep.method + '">' + ep.method + '</span>' +
|
|
27
|
-
'<span class="nav-label">' + ep.path + '</span>';
|
|
28
|
+
item.innerHTML = '<span class="method-badge ' + ep.method + '">' + ep.method + '</span><span class="nav-label">' + ep.path + '</span>';
|
|
28
29
|
item.addEventListener('click', () => {
|
|
29
30
|
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
|
30
31
|
item.classList.add('active');
|
|
@@ -33,7 +34,6 @@ function runtimeClientSandbox() {
|
|
|
33
34
|
});
|
|
34
35
|
sidebar.appendChild(item);
|
|
35
36
|
});
|
|
36
|
-
|
|
37
37
|
renderPanel(keys[0]);
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -43,44 +43,28 @@ function runtimeClientSandbox() {
|
|
|
43
43
|
const main = document.getElementById('main-panel');
|
|
44
44
|
if (!ep) return;
|
|
45
45
|
|
|
46
|
-
let html =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'<span>' + ep.path + '</span></div>' +
|
|
50
|
-
'<div class="endpoint-desc">' + ep.desc + '</div>';
|
|
46
|
+
let html = '<div class="endpoint-title">' + ep.title + '</div>' +
|
|
47
|
+
'<div class="endpoint-path"><span class="method-badge ' + ep.method + '">' + ep.method + '</span><span>' + ep.path + '</span></div>' +
|
|
48
|
+
'<div class="endpoint-desc">' + ep.desc + '</div>';
|
|
51
49
|
|
|
52
50
|
if (ep.params && ep.params.length) {
|
|
53
51
|
html += '<div class="form-section"><div class="form-section-title">Path Parameters</div>';
|
|
54
|
-
ep.params.forEach(
|
|
55
|
-
html +=
|
|
56
|
-
'<div class="field-row">' +
|
|
57
|
-
'<label class="field-label">' + p.label + '</label>' +
|
|
58
|
-
'<input type="text" id="param-' + p.name + '" placeholder="' + p.placeholder + '" />' +
|
|
59
|
-
'</div>';
|
|
52
|
+
ep.params.forEach(p => {
|
|
53
|
+
html += '<div class="field-row"><label class="field-label">' + p.label + '</label><input type="text" id="param-' + p.name + '" placeholder="' + p.placeholder + '" /></div>';
|
|
60
54
|
});
|
|
61
55
|
html += '</div>';
|
|
62
56
|
}
|
|
63
57
|
|
|
64
58
|
if (ep.fields && ep.fields.length) {
|
|
65
|
-
html += '<div class="form-section"><div class="form-section-title">HTTP JSON
|
|
66
|
-
ep.fields.forEach(
|
|
67
|
-
html +=
|
|
68
|
-
'<div class="field-row">' +
|
|
69
|
-
'<label class="field-label">' + f.label + '</label>' +
|
|
70
|
-
'<input type="' + (f.type || 'text') + '" id="field-' + f.name + '" placeholder="' + (f.placeholder || '') + '" />' +
|
|
71
|
-
'</div>';
|
|
59
|
+
html += '<div class="form-section"><div class="form-section-title">HTTP JSON Payload Parameters</div>';
|
|
60
|
+
ep.fields.forEach(f => {
|
|
61
|
+
html += '<div class="field-row"><label class="field-label">' + f.label + '</label><input type="' + (f.type || 'text') + '" id="field-' + f.name + '" placeholder="' + (f.placeholder || '') + '" /></div>';
|
|
72
62
|
});
|
|
73
63
|
html += '</div>';
|
|
74
64
|
}
|
|
75
65
|
|
|
76
|
-
html +=
|
|
77
|
-
'<div class="btn-row">' +
|
|
78
|
-
'<button class="btn" id="_exec_btn">Execute Route</button>' +
|
|
79
|
-
'<button class="btn btn-secondary" id="_clear_btn">Clear Context</button>' +
|
|
80
|
-
'</div>';
|
|
81
|
-
|
|
66
|
+
html += '<div class="btn-row"><button class="btn" id="_exec_btn">Execute Route</button><button class="btn btn-secondary" id="_clear_btn">Clear Context</button></div>';
|
|
82
67
|
main.innerHTML = html;
|
|
83
|
-
|
|
84
68
|
document.getElementById('_exec_btn').addEventListener('click', sendRequest);
|
|
85
69
|
document.getElementById('_clear_btn').addEventListener('click', clearResponse);
|
|
86
70
|
}
|
|
@@ -88,7 +72,6 @@ function runtimeClientSandbox() {
|
|
|
88
72
|
async function sendRequest() {
|
|
89
73
|
const ep = ENDPOINTS[currentKey];
|
|
90
74
|
let path = ep.path;
|
|
91
|
-
|
|
92
75
|
if (ep.params && ep.params.length) {
|
|
93
76
|
for (const p of ep.params) {
|
|
94
77
|
const val = (document.getElementById('param-' + p.name) || {}).value || '';
|
|
@@ -96,7 +79,6 @@ function runtimeClientSandbox() {
|
|
|
96
79
|
path = path.replace(':' + p.name, encodeURIComponent(val.trim()));
|
|
97
80
|
}
|
|
98
81
|
}
|
|
99
|
-
|
|
100
82
|
const baseUrl = document.getElementById('base-url').value.replace(/\/+$/, '');
|
|
101
83
|
const url = baseUrl + path;
|
|
102
84
|
const headers = { 'Content-Type': 'application/json' };
|
|
@@ -106,7 +88,7 @@ function runtimeClientSandbox() {
|
|
|
106
88
|
let body = undefined;
|
|
107
89
|
if (['POST', 'PUT', 'PATCH'].includes(ep.method) && ep.fields && ep.fields.length) {
|
|
108
90
|
const payload = {};
|
|
109
|
-
ep.fields.forEach(
|
|
91
|
+
ep.fields.forEach(f => {
|
|
110
92
|
const el = document.getElementById('field-' + f.name);
|
|
111
93
|
if (!el) return;
|
|
112
94
|
let v = el.value.trim();
|
|
@@ -118,13 +100,11 @@ function runtimeClientSandbox() {
|
|
|
118
100
|
|
|
119
101
|
setResponse(null, 'loading');
|
|
120
102
|
const t0 = Date.now();
|
|
121
|
-
|
|
122
103
|
try {
|
|
123
104
|
const res = await fetch(url, { method: ep.method, headers, body });
|
|
124
105
|
const ms = Date.now() - t0;
|
|
125
106
|
const text = await res.text();
|
|
126
|
-
let data;
|
|
127
|
-
try { data = JSON.parse(text); } catch { data = text; }
|
|
107
|
+
let data; try { data = JSON.parse(text); } catch { data = text; }
|
|
128
108
|
setResponse(data, res.ok ? 'ok' : 'err', res.status, ms);
|
|
129
109
|
} catch (err) {
|
|
130
110
|
setResponse({ error: err.message }, 'err', 'FAIL', 0);
|
|
@@ -133,16 +113,12 @@ function runtimeClientSandbox() {
|
|
|
133
113
|
|
|
134
114
|
function setResponse(data, state, status, ms) {
|
|
135
115
|
const badge = document.getElementById('status-badge');
|
|
136
|
-
const body
|
|
137
|
-
|
|
116
|
+
const body = document.getElementById('response-body');
|
|
138
117
|
if (state === 'loading') {
|
|
139
|
-
badge.className = 'status-badge status-idle';
|
|
140
|
-
|
|
141
|
-
body.className = 'response-body empty';
|
|
142
|
-
body.textContent = 'Executing transmission…';
|
|
118
|
+
badge.className = 'status-badge status-idle'; badge.textContent = '…';
|
|
119
|
+
body.className = 'response-body empty'; body.textContent = 'Executing transmission…';
|
|
143
120
|
return;
|
|
144
121
|
}
|
|
145
|
-
|
|
146
122
|
badge.className = 'status-badge ' + (state === 'ok' ? 'status-ok' : 'status-err');
|
|
147
123
|
badge.textContent = status + ' · ' + ms + 'ms';
|
|
148
124
|
body.className = 'response-body';
|
|
@@ -154,134 +130,414 @@ function runtimeClientSandbox() {
|
|
|
154
130
|
document.getElementById('status-badge').className = 'status-badge status-idle';
|
|
155
131
|
document.getElementById('status-badge').textContent = '—';
|
|
156
132
|
const body = document.getElementById('response-body');
|
|
157
|
-
body.className = 'response-body empty';
|
|
158
|
-
body.textContent = 'Execute a request row to generate feedback data';
|
|
133
|
+
body.className = 'response-body empty'; body.textContent = 'Execute a request row to generate feedback data';
|
|
159
134
|
}
|
|
160
135
|
|
|
161
136
|
function highlight(str) {
|
|
162
|
-
return str
|
|
163
|
-
.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
|
137
|
+
return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
|
164
138
|
.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false)\b|\bnull\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(m) {
|
|
165
|
-
if (/^"/.test(m)) return /:$/.test(m)
|
|
166
|
-
? '<span class="json-key">' + m + '</span>'
|
|
167
|
-
: '<span class="json-str">' + m + '</span>';
|
|
139
|
+
if (/^"/.test(m)) return /:$/.test(m) ? '<span class="json-key">' + m + '</span>' : '<span class="json-str">' + m + '</span>';
|
|
168
140
|
if (/true|false/.test(m)) return '<span class="json-bool">' + m + '</span>';
|
|
169
|
-
if (/null/.test(m))
|
|
141
|
+
if (/null/.test(m)) return '<span class="json-null">' + m + '</span>';
|
|
170
142
|
return '<span class="json-num">' + m + '</span>';
|
|
171
143
|
});
|
|
172
144
|
}
|
|
173
145
|
|
|
174
146
|
function showToast(msg) {
|
|
175
|
-
const t = document.getElementById('toast');
|
|
176
|
-
t.textContent = msg;
|
|
177
|
-
t.classList.add('show');
|
|
147
|
+
const t = document.getElementById('toast'); t.textContent = msg; t.classList.add('show');
|
|
178
148
|
setTimeout(() => t.classList.remove('show'), 2500);
|
|
179
149
|
}
|
|
180
150
|
|
|
181
151
|
buildSidebar();
|
|
182
152
|
}
|
|
183
153
|
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return `<!DOCTYPE html>
|
|
154
|
+
// Global UI layouts object
|
|
155
|
+
const UI = {
|
|
156
|
+
tester: (endpointsJsonB64) => `<!DOCTYPE html>
|
|
189
157
|
<html lang="en">
|
|
190
158
|
<head>
|
|
191
|
-
<meta charset="UTF-8">
|
|
192
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
193
|
-
<title>Endtester — API Environment</title>
|
|
159
|
+
<meta charset="UTF-8"><title>Endtester — Environment Hub</title>
|
|
194
160
|
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=DM+Mono:wght@400;500&family=DM+Sans:wght@300;400;500&display=swap" rel="stylesheet">
|
|
195
161
|
<style>
|
|
196
|
-
:root {
|
|
197
|
-
--bg: #0e0c09; --surface: #181510; --surface2: #221d14; --border: #3a3020;
|
|
198
|
-
--accent: #e8a838; --accent2: #c47a1e; --text: #f0e8d8; --text-dim: #9a8c78;
|
|
199
|
-
--red: #d45c3c; --green: #6ba05a; --blue: #5a86c0; --radius: 8px;
|
|
200
|
-
}
|
|
162
|
+
:root { --bg: #0e0c09; --surface: #181510; --surface2: #221d14; --border: #3a3020; --accent: #e8a838; --text: #f0e8d8; --text-dim: #9a8c78; --red: #d45c3c; --green: #6ba05a; --blue: #5a86c0; --radius: 8px; }
|
|
201
163
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
202
164
|
body { background: var(--bg); color: var(--text); font-family: 'DM Sans', sans-serif; font-size: 14px; height: 100vh; overflow: hidden; background-image: radial-gradient(ellipse 80% 60% at 50% -20%, #3a2a0a22 0%, transparent 70%); }
|
|
203
165
|
header { border-bottom: 1px solid var(--border); padding: 16px 32px; display: flex; align-items: center; gap: 20px; background: #0e0c09ee; backdrop-filter: blur(8px); height: 65px; }
|
|
204
|
-
.logo { font-family: 'Playfair Display', serif; font-size: 20px; color: var(--accent);
|
|
205
|
-
.logo span { color: var(--text-dim); font-size: 11px; font-family: 'DM Mono', monospace;
|
|
166
|
+
.logo { font-family: 'Playfair Display', serif; font-size: 20px; color: var(--accent); }
|
|
167
|
+
.logo span { color: var(--text-dim); font-size: 11px; font-family: 'DM Mono', monospace; margin-left: 8px; }
|
|
206
168
|
.header-right { margin-left: auto; display: flex; align-items: center; gap: 16px; }
|
|
207
169
|
.jwt-wrap, .base-url-wrap { display: flex; align-items: center; gap: 8px; }
|
|
208
|
-
.jwt-wrap label, .base-url-wrap label { color: var(--text-dim); font-size: 11px; font-family: 'DM Mono', monospace;
|
|
170
|
+
.jwt-wrap label, .base-url-wrap label { color: var(--text-dim); font-size: 11px; font-family: 'DM Mono', monospace; }
|
|
209
171
|
#jwt-input, #base-url { background: var(--surface2); border: 1px solid var(--border); color: var(--text); font-family: 'DM Mono', monospace; font-size: 12px; padding: 6px 12px; border-radius: var(--radius); width: 220px; outline: none; }
|
|
210
172
|
.layout { display: grid; grid-template-columns: 280px 1fr 450px; height: calc(100vh - 65px); overflow: hidden; }
|
|
211
173
|
aside { border-right: 1px solid var(--border); overflow-y: auto; padding: 16px 0; background: #0b0907; }
|
|
212
|
-
.section-label { font-size: 10px; font-family: 'DM Mono', monospace; color: var(--text-dim);
|
|
213
|
-
.nav-item { display: flex; align-items: center; gap: 10px; padding: 10px 18px; cursor: pointer; border-left: 2px solid transparent; color: var(--text-dim);
|
|
214
|
-
.nav-item:hover { background: var(--surface); color: var(--text); }
|
|
174
|
+
.section-label { font-size: 10px; font-family: 'DM Mono', monospace; color: var(--text-dim); text-transform: uppercase; padding: 12px 18px 6px; }
|
|
175
|
+
.nav-item { display: flex; align-items: center; gap: 10px; padding: 10px 18px; cursor: pointer; border-left: 2px solid transparent; color: var(--text-dim); }
|
|
215
176
|
.nav-item.active { border-left-color: var(--accent); background: var(--surface); color: var(--accent); }
|
|
216
|
-
.method-badge { font-family: 'DM Mono', monospace; font-size: 9px; font-weight: 600; padding: 2px 6px; border-radius: 4px; min-width: 52px; text-align: center;
|
|
217
|
-
.GET { background: #1a3a22; color: #6ba05a; } .POST { background: #1a2e3a; color: #5a86c0; } .PUT
|
|
177
|
+
.method-badge { font-family: 'DM Mono', monospace; font-size: 9px; font-weight: 600; padding: 2px 6px; border-radius: 4px; min-width: 52px; text-align: center; }
|
|
178
|
+
.GET { background: #1a3a22; color: #6ba05a; } .POST { background: #1a2e3a; color: #5a86c0; } .PUT { background: #3a2e10; color: #e8a838; } .DELETE { background: #3a1a14; color: #d45c3c; }
|
|
218
179
|
.nav-label { font-size: 12px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
219
180
|
main { overflow-y: auto; padding: 32px; background: #0e0c09; }
|
|
220
181
|
.endpoint-title { font-family: 'Playfair Display', serif; font-size: 24px; color: var(--accent); margin-bottom: 8px; }
|
|
221
182
|
.endpoint-path { font-family: 'DM Mono', monospace; font-size: 13px; color: var(--text-dim); margin-bottom: 24px; display: flex; align-items: center; gap: 8px; }
|
|
222
183
|
.endpoint-desc { color: var(--text-dim); font-size: 13px; line-height: 1.6; margin-bottom: 24px; border-left: 2px solid var(--border); padding-left: 12px; }
|
|
223
184
|
.form-section { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; margin-bottom: 20px; }
|
|
224
|
-
.form-section-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim);
|
|
185
|
+
.form-section-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim); text-transform: uppercase; margin-bottom: 16px; border-bottom: 1px solid var(--border); padding-bottom: 6px; }
|
|
225
186
|
.field-row { display: grid; grid-template-columns: 150px 1fr; align-items: center; gap: 16px; margin-bottom: 14px; }
|
|
226
|
-
.field-label { font-family: 'DM Mono', monospace; font-size: 12px; color: var(--text-dim); text-align: right;
|
|
227
|
-
input[type=text], input[type=password], input[type=number]
|
|
228
|
-
input:focus { border-color: var(--accent); }
|
|
187
|
+
.field-label { font-family: 'DM Mono', monospace; font-size: 12px; color: var(--text-dim); text-align: right; }
|
|
188
|
+
input[type=text], input[type=password], input[type=number] { background: var(--surface2); border: 1px solid var(--border); color: var(--text); font-size: 13px; padding: 8px 12px; border-radius: var(--radius); width: 100%; outline: none; }
|
|
229
189
|
.btn-row { margin-top: 24px; display: flex; gap: 12px; }
|
|
230
|
-
.btn { background: var(--accent); color: #0e0c09; border: none; padding: 10px 24px; border-radius: var(--radius); font-size: 13px; font-weight: 500; cursor: pointer;
|
|
231
|
-
.btn
|
|
232
|
-
.response-panel { border-left: 1px solid var(--border); display: flex; flex-direction: column;
|
|
190
|
+
.btn { background: var(--accent); color: #0e0c09; border: none; padding: 10px 24px; border-radius: var(--radius); font-size: 13px; font-weight: 500; cursor: pointer; }
|
|
191
|
+
.btn-secondary { background: var(--surface2); color: var(--text-dim); border: 1px solid var(--border); }
|
|
192
|
+
.response-panel { border-left: 1px solid var(--border); display: flex; flex-direction: column; background: #110e0a; }
|
|
233
193
|
.response-header { padding: 16px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; background: var(--surface); height: 50px; }
|
|
234
|
-
.response-header-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim); text-transform: uppercase;
|
|
235
|
-
.status-badge { font-family: 'DM Mono', monospace; font-size: 12px; margin-left: auto; padding: 2px 8px; border-radius: 4px;
|
|
194
|
+
.response-header-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim); text-transform: uppercase; }
|
|
195
|
+
.status-badge { font-family: 'DM Mono', monospace; font-size: 12px; margin-left: auto; padding: 2px 8px; border-radius: 4px; }
|
|
236
196
|
.status-ok { background: #1a3a22; color: #6ba05a; } .status-err { background: #3a1a14; color: #d45c3c; } .status-idle { background: var(--surface2); color: var(--text-dim); }
|
|
237
|
-
.response-body { flex: 1; overflow-y: auto;
|
|
238
|
-
.response-body.empty { color: var(--text-dim); display: flex; align-items: center; justify-content: center; padding: 20px;
|
|
239
|
-
.json-render-block { display: block; padding: 20px;
|
|
197
|
+
.response-body { flex: 1; overflow-y: auto; padding: 0; background: #0d0b08; }
|
|
198
|
+
.response-body.empty { color: var(--text-dim); display: flex; align-items: center; justify-content: center; padding: 20px; font-size: 13px; }
|
|
199
|
+
.json-render-block { display: block; padding: 20px; font-family: 'DM Mono', monospace; font-size: 12px; line-height: 1.5; white-space: pre; }
|
|
240
200
|
.json-key { color: #e8a838; } .json-str { color: #9ab878; } .json-num { color: #5a86c0; } .json-bool { color: #c47a1e; } .json-null { color: var(--text-dim); }
|
|
241
|
-
#toast { position: fixed; bottom: 24px; right: 24px; background: var(--surface2); border: 1px solid var(--border); padding: 10px 18px; border-radius: var(--radius); opacity: 0; transition: all .25s;
|
|
201
|
+
#toast { position: fixed; bottom: 24px; right: 24px; background: var(--surface2); border: 1px solid var(--border); padding: 10px 18px; border-radius: var(--radius); opacity: 0; transition: all .25s; font-family: 'DM Mono', monospace; font-size: 12px; color: var(--accent); }
|
|
242
202
|
#toast.show { opacity: 1; }
|
|
243
203
|
</style>
|
|
244
204
|
</head>
|
|
245
205
|
<body>
|
|
246
|
-
|
|
247
|
-
<div id="__monkey_data__" data-payload="${safeJsonString}" style="display:none;"></div>
|
|
248
|
-
|
|
206
|
+
<div id="__monkey_data__" data-payload="${endpointsJsonB64}" style="display:none;"></div>
|
|
249
207
|
<header>
|
|
250
|
-
<div class="logo">🐒 Endtester <span>
|
|
208
|
+
<div class="logo">🐒 Endtester <span>Interactive API Hub</span></div>
|
|
251
209
|
<div class="header-right">
|
|
252
|
-
<div class="base-url-wrap">
|
|
253
|
-
|
|
254
|
-
<input id="base-url" type="text" value="">
|
|
255
|
-
</div>
|
|
256
|
-
<div class="jwt-wrap">
|
|
257
|
-
<label>BEARER AUTH</label>
|
|
258
|
-
<input id="jwt-input" type="text" placeholder="Token value...">
|
|
259
|
-
</div>
|
|
210
|
+
<div class="base-url-wrap"><label>HOST</label><input id="base-url" type="text" value=""></div>
|
|
211
|
+
<div class="jwt-wrap"><label>BEARER AUTH</label><input id="jwt-input" type="text" placeholder="Token value..."></div>
|
|
260
212
|
</div>
|
|
261
213
|
</header>
|
|
262
|
-
|
|
263
214
|
<div class="layout">
|
|
264
|
-
<aside id="sidebar-nav">
|
|
265
|
-
<div class="section-label">Discovered Endpoints</div>
|
|
266
|
-
</aside>
|
|
215
|
+
<aside id="sidebar-nav"><div class="section-label">Discovered Endpoints</div></aside>
|
|
267
216
|
<main id="main-panel"></main>
|
|
268
217
|
<div class="response-panel">
|
|
269
|
-
<div class="response-header">
|
|
270
|
-
<span class="response-header-title">Response Output</span>
|
|
271
|
-
<span id="status-badge" class="status-badge status-idle">—</span>
|
|
272
|
-
</div>
|
|
218
|
+
<div class="response-header"><span class="response-header-title">Response Output</span><span id="status-badge" class="status-badge status-idle">—</span></div>
|
|
273
219
|
<div class="response-body empty" id="response-body">Execute a request row to generate feedback data</div>
|
|
274
220
|
</div>
|
|
275
221
|
</div>
|
|
276
|
-
|
|
277
222
|
<div id="toast"></div>
|
|
278
|
-
|
|
279
|
-
<script>
|
|
280
|
-
// Clean, flawless evaluation structure
|
|
281
|
-
(${runtimeClientSandbox.toString()})();
|
|
282
|
-
</script>
|
|
223
|
+
<script>(${runtimeClientSandbox.toString()})();</script>
|
|
283
224
|
</body>
|
|
284
|
-
</html
|
|
285
|
-
|
|
225
|
+
</html>`,
|
|
226
|
+
|
|
227
|
+
login: () => `<!DOCTYPE html><html><head><title>Sign In</title><link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet">
|
|
228
|
+
<style>body { background: #0e0c09; color: #f0e8d8; font-family: 'DM Sans', sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
|
|
229
|
+
.card { background: #181510; border: 1px solid #3a3020; padding: 40px; border-radius: 12px; width: 340px; } h2 { color: #e8a838; margin: 0 0 24px; text-align: center; }
|
|
230
|
+
.field { margin-bottom: 20px; } label { display: block; font-size: 11px; color: #9a8c78; text-transform: uppercase; margin-bottom: 8px; }
|
|
231
|
+
input { background: #221d14; border: 1px solid #3a3020; color: #f0e8d8; padding: 12px; width: 100%; box-sizing: border-box; border-radius: 6px; outline: none; }
|
|
232
|
+
button { background: #e8a838; color: #0e0c09; border: none; padding: 12px; width: 100%; border-radius: 6px; font-weight: 600; cursor: pointer; margin-top: 10px; }
|
|
233
|
+
.footer { text-align: center; margin-top: 20px; font-size: 13px; color: #9a8c78; } a { color: #e8a838; text-decoration: none; }</style></head>
|
|
234
|
+
<body><div class="card"><h2>Sign In</h2><div id="err" style="color:#d45c3c; font-size:13px; margin-bottom:15px; text-align:center;"></div>
|
|
235
|
+
<div class="field"><label>Email</label><input type="email" id="email" value="customer@bakery.com"></div>
|
|
236
|
+
<div class="field"><label>Password</label><input type="password" id="password" value="secret123"></div>
|
|
237
|
+
<button onclick="handleLogin()">Log In</button><div class="footer">Need an account? <a href="/signup">Sign up</a></div></div>
|
|
238
|
+
<script>async function handleLogin() {
|
|
239
|
+
const email = document.getElementById('email').value;
|
|
240
|
+
const password = document.getElementById('password').value;
|
|
241
|
+
const res = await fetch('/api/v1/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) });
|
|
242
|
+
const data = await res.json();
|
|
243
|
+
if (res.ok && data.token) {
|
|
244
|
+
localStorage.setItem('__auth_token__', data.token);
|
|
245
|
+
window.location.href = '/dashboard';
|
|
246
|
+
} else { document.getElementById('err').textContent = data.error || 'Login failed'; }
|
|
247
|
+
}</script></body></html>`,
|
|
248
|
+
|
|
249
|
+
signup: () => `<!DOCTYPE html><html><head><title>Create Account</title><link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet">
|
|
250
|
+
<style>body { background: #0e0c09; color: #f0e8d8; font-family: 'DM Sans', sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; }
|
|
251
|
+
.card { background: #181510; border: 1px solid #3a3020; padding: 40px; border-radius: 12px; width: 340px; } h2 { color: #e8a838; margin: 0 0 24px; text-align: center; }
|
|
252
|
+
.field { margin-bottom: 20px; } label { display: block; font-size: 11px; color: #9a8c78; text-transform: uppercase; margin-bottom: 8px; }
|
|
253
|
+
input { background: #221d14; border: 1px solid #3a3020; color: #f0e8d8; padding: 12px; width: 100%; box-sizing: border-box; border-radius: 6px; outline: none; }
|
|
254
|
+
button { background: #e8a838; color: #0e0c09; border: none; padding: 12px; width: 100%; border-radius: 6px; font-weight: 600; cursor: pointer; margin-top: 10px; }
|
|
255
|
+
.footer { text-align: center; margin-top: 20px; font-size: 13px; color: #9a8c78; } a { color: #e8a838; text-decoration: none; }</style></head>
|
|
256
|
+
<body><div class="card"><h2>Sign Up</h2><div id="msg" style="font-size:13px; margin-bottom:15px; text-align:center;"></div>
|
|
257
|
+
<div class="field"><label>Username</label><input type="text" id="username" placeholder="Username"></div>
|
|
258
|
+
<div class="field"><label>Email Address</label><input type="email" id="email" placeholder="Email"></div>
|
|
259
|
+
<div class="field"><label>Password</label><input type="password" id="password"></div>
|
|
260
|
+
<button onclick="handleRegister()">Register Account</button><div class="footer">Have an account? <a href="/login">Sign In</a></div></div>
|
|
261
|
+
<script>async function handleRegister() {
|
|
262
|
+
const username = document.getElementById('username').value;
|
|
263
|
+
const email = document.getElementById('email').value;
|
|
264
|
+
const password = document.getElementById('password').value;
|
|
265
|
+
const msgDiv = document.getElementById('msg');
|
|
266
|
+
const res = await fetch('/api/v1/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, email, password }) });
|
|
267
|
+
const data = await res.json();
|
|
268
|
+
if(res.ok) {
|
|
269
|
+
msgDiv.style.color = '#6ba05a'; msgDiv.textContent = 'Registration complete! Redirecting...';
|
|
270
|
+
setTimeout(() => window.location.href = '/login', 1200);
|
|
271
|
+
} else { msgDiv.style.color = '#d45c3c'; msgDiv.textContent = data.error || 'Registration failed'; }
|
|
272
|
+
}</script></body></html>`,
|
|
273
|
+
|
|
274
|
+
// DYNAMIC CRUD DASHBOARD — Reads fields and paths from runtime ENDPOINTS definition instead of static products paths
|
|
275
|
+
dashboard: (endpointsJsonB64) => `<!DOCTYPE html><html><head><title>Dynamic Admin Dashboard</title>
|
|
276
|
+
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet">
|
|
277
|
+
<style>
|
|
278
|
+
:root { --bg: #0e0c09; --surface: #181510; --surface2: #221d14; --border: #3a3020; --accent: #e8a838; --text: #f0e8d8; --text-dim: #9a8c78; --red: #d45c3c; --green: #6ba05a; }
|
|
279
|
+
body { background: var(--bg); color: var(--text); font-family: 'DM Sans', sans-serif; padding: 40px; margin: 0; }
|
|
280
|
+
header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); padding-bottom: 20px; margin-bottom: 30px; }
|
|
281
|
+
h1 { color: var(--accent); margin: 0; font-size: 24px; }
|
|
282
|
+
.nav-links { display: flex; gap: 16px; align-items: center; }
|
|
283
|
+
.nav-links a { color: var(--text-dim); text-decoration: none; font-size: 14px; }
|
|
284
|
+
.nav-links a:hover { color: var(--accent); }
|
|
285
|
+
.logout-btn { background: #3a1a14; color: var(--red); border: 1px solid #5a2014; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-weight:600; }
|
|
286
|
+
.selector-banner { background: var(--surface); border: 1px solid var(--border); padding: 12px 20px; border-radius: 8px; margin-bottom: 24px; display: flex; align-items: center; gap: 12px; }
|
|
287
|
+
select { background: var(--surface2); border: 1px solid var(--border); color: var(--text); padding: 8px 12px; border-radius: 6px; outline: none; font-weight: 500; }
|
|
288
|
+
.grid { display: grid; grid-template-columns: 360px 1fr; gap: 30px; }
|
|
289
|
+
.panel { background: var(--surface); border: 1px solid var(--border); padding: 24px; border-radius: 8px; height: fit-content; }
|
|
290
|
+
h3 { color: var(--accent); margin-top: 0; margin-bottom: 20px; border-bottom: 1px solid var(--border); padding-bottom: 8px; font-size:14px; text-transform:uppercase; letter-spacing:0.5px; }
|
|
291
|
+
.field { margin-bottom: 16px; }
|
|
292
|
+
label { display: block; font-size: 11px; color: var(--text-dim); text-transform: uppercase; margin-bottom: 6px; font-family: monospace; }
|
|
293
|
+
input { background: var(--surface2); border: 1px solid var(--border); color: var(--text); padding: 10px; width: 100%; box-sizing: border-box; border-radius: 6px; outline: none; }
|
|
294
|
+
.btn { background: var(--accent); color: #0e0c09; border: none; padding: 11px; width: 100%; border-radius: 6px; font-weight: 700; cursor: pointer; }
|
|
295
|
+
.btn-cancel { background: var(--surface2); color: var(--text-dim); border: 1px solid var(--border); margin-top: 8px; }
|
|
296
|
+
.table-wrap { overflow-x: auto; background: var(--surface); border-radius: 8px; border: 1px solid var(--border); }
|
|
297
|
+
table { width: 100%; border-collapse: collapse; }
|
|
298
|
+
th { color: var(--text-dim); text-align: left; padding: 14px; border-bottom: 2px solid var(--border); font-size: 11px; text-transform: uppercase; background:#13100b; }
|
|
299
|
+
td { padding: 14px; border-bottom: 1px solid var(--surface2); font-size: 13px; font-family: monospace; }
|
|
300
|
+
.actions-cell { display: flex; gap: 8px; justify-content: flex-end; }
|
|
301
|
+
.btn-sm { padding: 4px 8px; border-radius: 4px; border: none; font-weight: 600; font-size: 11px; cursor: pointer; }
|
|
302
|
+
.btn-edit { background: #1a2e3a; color: #5a86c0; border: 1px solid #224054; }
|
|
303
|
+
.btn-del { background: #3a1a14; color: var(--red); border: 1px solid #5a2014; }
|
|
304
|
+
</style>
|
|
305
|
+
</head>
|
|
306
|
+
<body>
|
|
307
|
+
<div id="__monkey_data__" data-payload="${endpointsJsonB64}" style="display:none;"></div>
|
|
308
|
+
<header>
|
|
309
|
+
<h1>🐒 Universal Management Dashboard</h1>
|
|
310
|
+
<div class="nav-links">
|
|
311
|
+
<a href="/api/tester" target="_blank">🛠 Open Tester Sandbox</a>
|
|
312
|
+
<button class="logout-btn" onclick="localStorage.removeItem('__auth_token__'); window.location.href='/login'">Log Out</button>
|
|
313
|
+
</div>
|
|
314
|
+
</header>
|
|
315
|
+
|
|
316
|
+
<div class="selector-banner">
|
|
317
|
+
<label style="margin:0;">Target Data Resource Collection:</label>
|
|
318
|
+
<select id="route-selector" onchange="switchCollection()"></select>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<div class="grid">
|
|
322
|
+
<div class="panel">
|
|
323
|
+
<h3 id="form-title">Add Entry</h3>
|
|
324
|
+
<div id="dynamic-fields-container"></div>
|
|
325
|
+
<button class="btn" id="btn-submit" onclick="submitDataForm()">Execute Submission</button>
|
|
326
|
+
<button class="btn btn-sm btn-cancel" id="btn-cancel" style="display:none; margin-top:10px;" onclick="resetDataForm()">Cancel Action</button>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<div class="table-wrap">
|
|
330
|
+
<table id="dynamic-table">
|
|
331
|
+
<thead id="table-head"></thead>
|
|
332
|
+
<tbody id="table-body"></tbody>
|
|
333
|
+
</table>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
<script>
|
|
338
|
+
const ENDPOINTS = JSON.parse(atob(document.getElementById('__monkey_data__').getAttribute('data-payload')));
|
|
339
|
+
const token = localStorage.getItem('__auth_token__');
|
|
340
|
+
if (!token) window.location.href = '/login';
|
|
341
|
+
|
|
342
|
+
let dynamicCollections = {};
|
|
343
|
+
let activeCollectionPath = '';
|
|
344
|
+
let activeEditId = null;
|
|
345
|
+
|
|
346
|
+
// Group discovery metadata into unified conceptual resource targets
|
|
347
|
+
function resolveRoutes() {
|
|
348
|
+
Object.values(ENDPOINTS).forEach(ep => {
|
|
349
|
+
// Identify root paths that are collection containers (e.g., /api/v1/products, /api/v1/users)
|
|
350
|
+
if (!ep.path.includes(':')) {
|
|
351
|
+
if (!dynamicCollections[ep.path]) {
|
|
352
|
+
dynamicCollections[ep.path] = { get: null, post: null, put: null, del: null, modelFields: ep.fields || [] };
|
|
353
|
+
}
|
|
354
|
+
if (ep.method === 'GET') dynamicCollections[ep.path].get = ep.path;
|
|
355
|
+
if (ep.method === 'POST') {
|
|
356
|
+
dynamicCollections[ep.path].post = ep.path;
|
|
357
|
+
if (ep.fields && ep.fields.length) dynamicCollections[ep.path].modelFields = ep.fields;
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
// Match corresponding parameterized items to complete the sub-resource route setup (e.g., /api/v1/products/:id)
|
|
361
|
+
const basePath = ep.path.split('/:')[0];
|
|
362
|
+
if (!dynamicCollections[basePath]) {
|
|
363
|
+
dynamicCollections[basePath] = { get: null, post: null, put: null, del: null, modelFields: [] };
|
|
364
|
+
}
|
|
365
|
+
if (ep.method === 'PUT') dynamicCollections[basePath].put = ep.path;
|
|
366
|
+
if (ep.method === 'DELETE') dynamicCollections[basePath].del = ep.path;
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Clean out incomplete route profiles from selector options
|
|
371
|
+
const selector = document.getElementById('route-selector');
|
|
372
|
+
Object.keys(dynamicCollections).forEach(path => {
|
|
373
|
+
if (dynamicCollections[path].get) {
|
|
374
|
+
const opt = document.createElement('option');
|
|
375
|
+
opt.value = path;
|
|
376
|
+
opt.textContent = path + " (Dynamic Dataset)";
|
|
377
|
+
selector.appendChild(opt);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
if (selector.options.length > 0) {
|
|
382
|
+
switchCollection(selector.options[0].value);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function switchCollection(targetPath) {
|
|
387
|
+
activeCollectionPath = targetPath || document.getElementById('route-selector').value;
|
|
388
|
+
activeEditId = null;
|
|
389
|
+
resetDataForm();
|
|
390
|
+
renderFormFields();
|
|
391
|
+
fetchData();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function renderFormFields() {
|
|
395
|
+
const container = document.getElementById('dynamic-fields-container');
|
|
396
|
+
container.innerHTML = '';
|
|
397
|
+
const fields = dynamicCollections[activeCollectionPath].modelFields;
|
|
398
|
+
|
|
399
|
+
if (!fields || fields.length === 0) {
|
|
400
|
+
container.innerHTML = '<p style="font-size:12px; color:var(--text-dim);">No input properties found for this resource schema.</p>';
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
fields.forEach(f => {
|
|
405
|
+
container.innerHTML += \`
|
|
406
|
+
<div class="field">
|
|
407
|
+
<label>\${f.label}</label>
|
|
408
|
+
<input type="\${f.type || 'text'}" id="input-\${f.name}" placeholder="\${f.placeholder || ''}">
|
|
409
|
+
</div>
|
|
410
|
+
\`;
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async function fetchData() {
|
|
415
|
+
const head = document.getElementById('table-head');
|
|
416
|
+
const body = document.getElementById('table-body');
|
|
417
|
+
head.innerHTML = '';
|
|
418
|
+
body.innerHTML = '<tr><td style="padding:20px; color:var(--text-dim)">Loading live system schemas...</td></tr>';
|
|
419
|
+
|
|
420
|
+
try {
|
|
421
|
+
const res = await fetch(activeCollectionPath, { headers: { 'Authorization': 'Bearer ' + token } });
|
|
422
|
+
const rawData = await res.json();
|
|
423
|
+
|
|
424
|
+
// Flatten standard array wrapping logic safely
|
|
425
|
+
let list = [];
|
|
426
|
+
if (Array.isArray(rawData)) list = rawData;
|
|
427
|
+
else if (rawData && typeof rawData === 'object') {
|
|
428
|
+
const arrayKey = Object.keys(rawData).find(k => Array.isArray(rawData[k]));
|
|
429
|
+
list = arrayKey ? rawData[arrayKey] : [rawData];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (!list || list.length === 0 || list[0] === null) {
|
|
433
|
+
body.innerHTML = '<tr><td style="padding:30px; color:var(--text-dim)">No records found inside this live API container.</td></tr>';
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Gather unique keys from database entities dynamically
|
|
438
|
+
let keys = Object.keys(list[0]).filter(k => typeof list[0][k] !== 'object');
|
|
439
|
+
|
|
440
|
+
// Generate headers
|
|
441
|
+
let trHead = '<tr>';
|
|
442
|
+
keys.forEach(k => trHead += \`<th>\${k}</th>\`);
|
|
443
|
+
trHead += '<th style="text-align:right; padding-right:20px;">Actions</th></tr>';
|
|
444
|
+
head.innerHTML = trHead;
|
|
445
|
+
|
|
446
|
+
// Populate row fields
|
|
447
|
+
body.innerHTML = '';
|
|
448
|
+
list.forEach(row => {
|
|
449
|
+
let trBody = '<tr>';
|
|
450
|
+
keys.forEach(k => {
|
|
451
|
+
const val = row[k] !== undefined ? row[k] : '';
|
|
452
|
+
trBody += \`<td>\${val}</td>\`;
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const targetId = row.id || row._id || list.indexOf(row);
|
|
456
|
+
const rowJson = btoa(JSON.stringify(row));
|
|
457
|
+
|
|
458
|
+
trBody += \`
|
|
459
|
+
<td class="actions-cell" style="padding-right:20px;">
|
|
460
|
+
\${dynamicCollections[activeCollectionPath].put ? \`<button class="btn-sm btn-edit" onclick="startRowEdit('\${targetId}', '\${rowJson}')">Edit</button>\` : ''}
|
|
461
|
+
\${dynamicCollections[activeCollectionPath].del ? \`<button class="btn-sm btn-del" onclick="deleteRow('\${targetId}')">Delete</button>\` : ''}
|
|
462
|
+
</td>
|
|
463
|
+
</tr>\`;
|
|
464
|
+
body.innerHTML += trBody;
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
} catch (e) {
|
|
468
|
+
body.innerHTML = '<tr><td style="padding:20px; color:var(--red)">Failed processing remote endpoint structure data.</td></tr>';
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function submitDataForm() {
|
|
473
|
+
const fields = dynamicCollections[activeCollectionPath].modelFields;
|
|
474
|
+
const payload = {};
|
|
475
|
+
|
|
476
|
+
fields.forEach(f => {
|
|
477
|
+
const el = document.getElementById(\`input-\${f.name}\`);
|
|
478
|
+
if (el) {
|
|
479
|
+
payload[f.name] = f.type === 'number' ? Number(el.value) : el.value;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
let url = activeCollectionPath;
|
|
484
|
+
let method = 'POST';
|
|
485
|
+
|
|
486
|
+
if (activeEditId !== null) {
|
|
487
|
+
const putTemplate = dynamicCollections[activeCollectionPath].put;
|
|
488
|
+
const paramName = putTemplate.split('/:')[1];
|
|
489
|
+
url = putTemplate.replace(\`:\${paramName}\`, activeEditId);
|
|
490
|
+
method = 'PUT';
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const res = await fetch(url, {
|
|
494
|
+
method: method,
|
|
495
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
|
|
496
|
+
body: JSON.stringify(payload)
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
if (res.ok) { resetDataForm(); fetchData(); } else { alert('Submission declined by target stack route operations.'); }
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
async function deleteRow(id) {
|
|
503
|
+
if (!confirm('Execute atomic entry removal?')) return;
|
|
504
|
+
const delTemplate = dynamicCollections[activeCollectionPath].del;
|
|
505
|
+
const paramName = delTemplate.split('/:')[1];
|
|
506
|
+
const url = delTemplate.replace(\`:\${paramName}\`, id);
|
|
507
|
+
|
|
508
|
+
const res = await fetch(url, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + token } });
|
|
509
|
+
if (res.ok) fetchData(); else alert('Delete request blocked by resource server.');
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function startRowEdit(id, encodedJson) {
|
|
513
|
+
activeEditId = id;
|
|
514
|
+
const data = JSON.parse(atob(encodedJson));
|
|
515
|
+
document.getElementById('form-title').textContent = \`Update Row #\${id}\`;
|
|
516
|
+
document.getElementById('btn-submit').textContent = 'Commit Changes';
|
|
517
|
+
document.getElementById('btn-cancel').style.display = 'block';
|
|
518
|
+
|
|
519
|
+
const fields = dynamicCollections[activeCollectionPath].modelFields;
|
|
520
|
+
fields.forEach(f => {
|
|
521
|
+
const el = document.getElementById(\`input-\${f.name}\`);
|
|
522
|
+
if (el && data[f.name] !== undefined) el.value = data[f.name];
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function resetDataForm() {
|
|
527
|
+
activeEditId = null;
|
|
528
|
+
document.getElementById('form-title').textContent = 'Add Entry';
|
|
529
|
+
document.getElementById('btn-submit').textContent = 'Execute Submission';
|
|
530
|
+
document.getElementById('btn-cancel').style.display = 'none';
|
|
531
|
+
const fields = dynamicCollections[activeCollectionPath]?.modelFields || [];
|
|
532
|
+
fields.forEach(f => {
|
|
533
|
+
const el = document.getElementById(\`input-\${f.name}\`);
|
|
534
|
+
if (el) el.value = '';
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
resolveRoutes();
|
|
539
|
+
</script>
|
|
540
|
+
</body></html>`
|
|
541
|
+
};
|
|
286
542
|
|
|
287
|
-
export {
|
|
543
|
+
export { UI };
|