quar 1.1.0 → 1.2.1
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 +21 -0
- package/package.json +1 -1
- package/public/scripts/main.js +69 -43
- package/public/styles/style.css +48 -47
- package/server.js +1 -0
package/LICENSE
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ismail Bin Mujeeb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
CHANGED
package/public/scripts/main.js
CHANGED
|
@@ -99,12 +99,27 @@ async function loadDocuments() {
|
|
|
99
99
|
// Render each array element as its own tree
|
|
100
100
|
data.documents.forEach((doc, index) => {
|
|
101
101
|
const wrapper = document.createElement('div');
|
|
102
|
-
|
|
102
|
+
const divDocument = document.createElement('div');
|
|
103
|
+
divDocument.innerHTML += Object.entries(doc).map(([key, value]) => renderData(key, value, index)).join('');
|
|
104
|
+
|
|
105
|
+
divDocument.classList.add('document');
|
|
106
|
+
|
|
107
|
+
wrapper.appendChild(divDocument);
|
|
103
108
|
wrapper.innerHTML += `<div class="document-actions"><button class="updateDocument" onclick="updateDocument(${index})">Update</button><button class="deleteDocument" onclick="deleteDoc('${modelName}', '${doc._id}')">Delete</button></div>`;
|
|
104
|
-
|
|
109
|
+
|
|
110
|
+
content.appendChild(wrapper);
|
|
105
111
|
});
|
|
106
112
|
}
|
|
107
113
|
|
|
114
|
+
function toggleEditMode(id) {
|
|
115
|
+
const htmlData = document.getElementById(`${id}-html-data`);
|
|
116
|
+
const jsonData = document.getElementById(`${id}-json-data`);
|
|
117
|
+
|
|
118
|
+
htmlData.classList.toggle('hidden');
|
|
119
|
+
jsonData.classList.toggle('hidden');
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
108
123
|
function gotoNextPage() {
|
|
109
124
|
const page = document.getElementById("page-value");
|
|
110
125
|
page.innerText = Number(page.innerText) + 1;
|
|
@@ -129,33 +144,34 @@ function gotoPreviousPage() {
|
|
|
129
144
|
loadDocuments();
|
|
130
145
|
}
|
|
131
146
|
|
|
132
|
-
function
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
147
|
+
function renderData(key, value, index, level = "root.", parentDataType = "object") {
|
|
148
|
+
const type = typeof value;
|
|
149
|
+
if (value === null) return `<div class="field"><span class="data-key">${key}</span>: <span class="null" data-level="${level}" data-index="${index}">null</span><span class="data-type">(null)</span></div>`;
|
|
150
|
+
|
|
151
|
+
if (Array.isArray(value)) {
|
|
152
|
+
return `
|
|
153
|
+
<details class="field">
|
|
154
|
+
<summary ><span class="data-key">${key}</span><span class="data-type"> (Array - ${value.length})</span></summary>
|
|
155
|
+
<div class="data-value">
|
|
156
|
+
${value.map((v, i) => renderData(`[${i}]`, v, index, level + key, "array")).join('')}
|
|
157
|
+
</div>
|
|
158
|
+
</details>
|
|
159
|
+
`;
|
|
160
|
+
} else if (type === 'object') {
|
|
161
|
+
return `
|
|
162
|
+
<details class="field">
|
|
163
|
+
<summary><span class="data-key">${key}</span><span class="data-type"> (Object)</span></summary>
|
|
164
|
+
<div class="data-value">
|
|
165
|
+
${Object.entries(value).map(([k, v]) => renderData(k, v, index, level + key, "object")).join('')}
|
|
166
|
+
</div>
|
|
167
|
+
</details>
|
|
168
|
+
`;
|
|
169
|
+
} else if (typeof value === 'string' && /^[a-f\d]{24}$/i.test(value)) {
|
|
170
|
+
|
|
171
|
+
return `<div class="field"><span class="data-key">${key}</span>: <span class="ObjectId">${value}</span></div>`;
|
|
172
|
+
} else {
|
|
173
|
+
return `<div class="field"><span class="data-key">${key}</span>: <span class="${type}" data-level="${level}" data-index="${index}" data-parent-data-type="${parentDataType}" contenteditable >${value}</span></div>`;
|
|
155
174
|
}
|
|
156
|
-
|
|
157
|
-
moduleDocument.appendChild(container);
|
|
158
|
-
return moduleDocument;
|
|
159
175
|
}
|
|
160
176
|
|
|
161
177
|
// Handle collapsibles
|
|
@@ -169,22 +185,30 @@ document.addEventListener('click', function (e) {
|
|
|
169
185
|
}
|
|
170
186
|
});
|
|
171
187
|
|
|
172
|
-
function setNestedValue(obj, path, value) {
|
|
188
|
+
function setNestedValue(obj, path, value, parentDataType) {
|
|
173
189
|
const keys = path.split(".");
|
|
174
190
|
let current = obj;
|
|
175
191
|
|
|
176
192
|
keys.forEach((key, index) => {
|
|
177
193
|
if (index === keys.length - 1) {
|
|
178
|
-
|
|
194
|
+
const arrayIndexMatch = key.match(/\[(\d+)\]/);
|
|
195
|
+
if (arrayIndexMatch) {
|
|
196
|
+
const idx = parseInt(arrayIndexMatch[1], 10);
|
|
197
|
+
current[idx] = value;
|
|
198
|
+
} else {
|
|
199
|
+
current[key] = value;
|
|
200
|
+
}
|
|
201
|
+
|
|
179
202
|
} else {
|
|
180
|
-
if (
|
|
181
|
-
current[key] =
|
|
203
|
+
if (parentDataType === "array" && !Array.isArray(current[key])) {
|
|
204
|
+
current[key] = [];
|
|
205
|
+
} else if (parentDataType === "object" && typeof current[key] !== "object") {
|
|
206
|
+
current[key] = {};
|
|
182
207
|
}
|
|
208
|
+
|
|
183
209
|
current = current[key]; // Go deeper
|
|
184
210
|
}
|
|
185
211
|
});
|
|
186
|
-
|
|
187
|
-
return obj; // Optional: return the updated object
|
|
188
212
|
}
|
|
189
213
|
|
|
190
214
|
async function updateDocument(index) {
|
|
@@ -199,19 +223,22 @@ async function updateDocument(index) {
|
|
|
199
223
|
const values = document.querySelectorAll(`[data-index="${index}"]`);
|
|
200
224
|
|
|
201
225
|
values.forEach(el => {
|
|
202
|
-
const key = el.previousElementSibling?.
|
|
226
|
+
const key = el.previousElementSibling?.textContent?.trim();
|
|
203
227
|
const level = el.dataset.level;
|
|
228
|
+
|
|
204
229
|
if (key) {
|
|
205
230
|
if (level && level !== "root") {
|
|
206
231
|
// Build the full path
|
|
207
232
|
const fullPath = `${level.replace("root.", "")}.${key}`;
|
|
208
|
-
setNestedValue(updatedDoc, fullPath, el.
|
|
233
|
+
setNestedValue(updatedDoc, fullPath, el.textContent, el.dataset.parentDataType);
|
|
209
234
|
} else {
|
|
210
|
-
updatedDoc[key] = el.
|
|
235
|
+
updatedDoc[key] = el.textContent;
|
|
211
236
|
}
|
|
212
237
|
}
|
|
213
238
|
});
|
|
214
239
|
|
|
240
|
+
updatedDoc = { ...updatedDoc, ...updatedDoc[""] }
|
|
241
|
+
|
|
215
242
|
const res = await fetch(`/update/${currentModelName}/${id}`, {
|
|
216
243
|
method: "PUT",
|
|
217
244
|
headers: { "Content-Type": "application/json" },
|
|
@@ -301,18 +328,18 @@ function dragEnd() {
|
|
|
301
328
|
async function refreshSideBar() {
|
|
302
329
|
try {
|
|
303
330
|
const modelWrapper = document.querySelector('.model-wrapper');
|
|
304
|
-
|
|
331
|
+
|
|
305
332
|
const res = await fetch("/models");
|
|
306
|
-
|
|
333
|
+
|
|
307
334
|
if (!res.ok) {
|
|
308
335
|
const error = await res.json();
|
|
309
336
|
showModal('error', 'Error Occurred!', error.error || 'Something went wrong, please try again.');
|
|
310
337
|
return;
|
|
311
338
|
}
|
|
312
|
-
|
|
339
|
+
|
|
313
340
|
modelWrapper.innerHTML = "";
|
|
314
341
|
const data = await res.json();
|
|
315
|
-
|
|
342
|
+
|
|
316
343
|
data.models.forEach(model => {
|
|
317
344
|
const modelDiv = document.createElement('div');
|
|
318
345
|
modelDiv.classList.add('model');
|
|
@@ -323,5 +350,4 @@ async function refreshSideBar() {
|
|
|
323
350
|
} catch (error) {
|
|
324
351
|
showModal('error', 'Error Occurred!', error.message || 'Something went wrong, please try again.');
|
|
325
352
|
}
|
|
326
|
-
}
|
|
327
|
-
|
|
353
|
+
}
|
package/public/styles/style.css
CHANGED
|
@@ -217,89 +217,67 @@ body {
|
|
|
217
217
|
display: none;
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
summary::marker{
|
|
221
|
+
color: var(--muted-color);
|
|
222
|
+
font-size: 10px;
|
|
223
|
+
margin-right: 10px;
|
|
224
|
+
}
|
|
225
|
+
|
|
220
226
|
.document {
|
|
221
227
|
padding: 10px;
|
|
222
228
|
border: 1px solid var(--muted-color);
|
|
223
|
-
/* background: var(--bg-color); */
|
|
224
229
|
background: #1e2124;
|
|
225
|
-
max-height: 80vh;
|
|
226
230
|
border-radius: 10px;
|
|
227
231
|
margin-top: 10px;
|
|
228
232
|
}
|
|
229
233
|
|
|
230
|
-
.
|
|
231
|
-
padding
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
.tree li {
|
|
235
|
-
list-style-type: none;
|
|
236
|
-
padding: 5px 5px;
|
|
237
|
-
font-size: 14px;
|
|
238
|
-
border-radius: 5px;
|
|
239
|
-
transition: background 0.1s ease-in-out;
|
|
234
|
+
.field {
|
|
235
|
+
padding: 5px 20px;
|
|
240
236
|
|
|
241
|
-
&:hover {
|
|
242
|
-
background: #2E3A59;
|
|
243
|
-
}
|
|
244
237
|
}
|
|
245
238
|
|
|
246
|
-
.
|
|
239
|
+
.field .string {
|
|
247
240
|
color: var(--green-color);
|
|
248
241
|
}
|
|
249
242
|
|
|
250
|
-
.
|
|
243
|
+
.field .string::after {
|
|
251
244
|
content: '"';
|
|
252
245
|
color: var(--green-color);
|
|
253
246
|
}
|
|
254
247
|
|
|
255
|
-
.
|
|
248
|
+
.field .string::before {
|
|
256
249
|
content: '"';
|
|
257
250
|
color: var(--green-color);
|
|
258
251
|
}
|
|
259
252
|
|
|
260
|
-
.
|
|
253
|
+
.field .number {
|
|
261
254
|
color: var(--blue-color);
|
|
262
255
|
}
|
|
263
256
|
|
|
264
|
-
.
|
|
257
|
+
.field .ObjectId {
|
|
265
258
|
color: var(--red-color);
|
|
266
259
|
}
|
|
267
260
|
|
|
268
|
-
.
|
|
261
|
+
.field .ObjectId::after {
|
|
269
262
|
content: ' )';
|
|
270
263
|
color: var(--red-color);
|
|
271
264
|
}
|
|
272
265
|
|
|
273
|
-
.
|
|
266
|
+
.field .ObjectId::before {
|
|
274
267
|
content: 'ObjectId( ';
|
|
275
268
|
color: var(--red-color);
|
|
276
269
|
}
|
|
277
270
|
|
|
278
|
-
.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
display: block;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
.caret::before {
|
|
292
|
-
content: "▶";
|
|
293
|
-
color: #555;
|
|
294
|
-
display: inline-block;
|
|
295
|
-
font-size: 10px;
|
|
296
|
-
margin-right: 6px;
|
|
297
|
-
transform: rotate(0deg);
|
|
298
|
-
transition: transform 0.3s ease;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
.caret-down::before {
|
|
302
|
-
transform: rotate(90deg);
|
|
271
|
+
.json-data {
|
|
272
|
+
width: 100%;
|
|
273
|
+
height: auto;
|
|
274
|
+
margin-top: 10px;
|
|
275
|
+
background: #1e2124;
|
|
276
|
+
border: none;
|
|
277
|
+
color: #E0E0E0;
|
|
278
|
+
padding: 10px;
|
|
279
|
+
border-radius: 10px;
|
|
280
|
+
resize: none;
|
|
303
281
|
}
|
|
304
282
|
|
|
305
283
|
.document-actions {
|
|
@@ -338,4 +316,27 @@ body {
|
|
|
338
316
|
background: var(--red-color);
|
|
339
317
|
color: var(--bg-color);
|
|
340
318
|
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.top-container {
|
|
322
|
+
display: flex;
|
|
323
|
+
justify-content: left;
|
|
324
|
+
width: 100%;
|
|
325
|
+
padding: 0 10px;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.top-container .editDocument {
|
|
329
|
+
background: transparent;
|
|
330
|
+
color: #E0E0E0;
|
|
331
|
+
border: none;
|
|
332
|
+
padding: 5px 10px;
|
|
333
|
+
border: 1px solid var(--muted-color);
|
|
334
|
+
border-radius: 5px;
|
|
335
|
+
transition: background 0.1s ease-in-out;
|
|
336
|
+
cursor: pointer;
|
|
337
|
+
|
|
338
|
+
&:hover {
|
|
339
|
+
background: var(--blue-color);
|
|
340
|
+
color: var(--bg-color);
|
|
341
|
+
}
|
|
341
342
|
}
|
package/server.js
CHANGED
|
@@ -198,6 +198,7 @@ app.put("/update/:modelName/:id", async (req, res) => {
|
|
|
198
198
|
const { modelName, id } = req.params;
|
|
199
199
|
const model = mongoose.model(modelName);
|
|
200
200
|
const updatedDoc = req.body;
|
|
201
|
+
|
|
201
202
|
delete updatedDoc._id;
|
|
202
203
|
const result = await model.findByIdAndUpdate(id, updatedDoc, { new: true });
|
|
203
204
|
|