quar 1.1.0 → 1.2.0

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 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.
@@ -0,0 +1,71 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const PlanetSchema = new mongoose.Schema({
4
+ name: String,
5
+ description: String,
6
+ });
7
+
8
+ const userSchema = new mongoose.Schema({
9
+ // String types
10
+ name: String,
11
+ email: { type: String, required: true, unique: true },
12
+ password: { type: String, required: true },
13
+
14
+ // Number types
15
+ age: Number,
16
+ score: { type: Number, default: 0 },
17
+
18
+ // Date types
19
+ birthDate: Date,
20
+ createdAt: { type: Date, default: Date.now },
21
+
22
+ // Boolean type
23
+ isActive: { type: Boolean, default: true },
24
+
25
+ // Array types
26
+ interests: [String],
27
+ scores: [Number],
28
+
29
+ planet: { type: PlanetSchema, required: true },
30
+
31
+ // Nested object
32
+ address: {
33
+ street: String,
34
+ city: String,
35
+ country: String,
36
+ zipCode: String
37
+ },
38
+
39
+ // ObjectId reference
40
+ friends: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
41
+
42
+ // Mixed type (any data)
43
+ additionalInfo: mongoose.Schema.Types.Mixed,
44
+
45
+ // Buffer type (for binary data)
46
+ profilePicture: Buffer,
47
+
48
+ // Map type
49
+ metadata: {
50
+ type: Map,
51
+ of: String
52
+ }
53
+ });
54
+
55
+ // Add indexes
56
+ userSchema.index({ email: 1 });
57
+ userSchema.index({ name: 1, age: -1 });
58
+
59
+ // Add a virtual property
60
+ userSchema.virtual('fullName').get(function () {
61
+ return `${this.firstName} ${this.lastName}`;
62
+ });
63
+
64
+ // Add an instance method
65
+ userSchema.methods.getAge = function () {
66
+ return Math.floor((Date.now() - this.birthDate.getTime()) / 31557600000);
67
+ };
68
+
69
+ const User = mongoose.model('User', userSchema);
70
+
71
+ export default User;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quar",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "This will load all Mongoose models from the folder and start a local web UI to Create, view, update, and delete documents.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -99,12 +99,30 @@ 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
- wrapper.appendChild(createTree(doc, data.schema, index));
102
+ const divDocument = document.createElement('div');
103
+ wrapper.innerHTML += `<div class="top-container"><button class="editDocument" onclick="toggleEditMode('${doc._id}')">Edit</button></div>`;
104
+ divDocument.innerHTML += Object.entries(doc).map(([key, value]) => renderData(key, value, index)).join('');
105
+ wrapper.innerHTML += `<textarea class="json-data hidden" id="${doc._id}-json-data" oninput="autoResize(this)">${JSON.stringify(doc, null, 2)}</textarea>`
106
+
107
+ divDocument.classList.add('document');
108
+ divDocument.id = `${doc._id}-html-data`;
109
+
110
+ wrapper.appendChild(divDocument);
103
111
  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
- content.innerHTML += wrapper.innerHTML;
112
+
113
+ content.appendChild(wrapper);
105
114
  });
106
115
  }
107
116
 
117
+ function toggleEditMode(id) {
118
+ const htmlData = document.getElementById(`${id}-html-data`);
119
+ const jsonData = document.getElementById(`${id}-json-data`);
120
+
121
+ htmlData.classList.toggle('hidden');
122
+ jsonData.classList.toggle('hidden');
123
+
124
+ }
125
+
108
126
  function gotoNextPage() {
109
127
  const page = document.getElementById("page-value");
110
128
  page.innerText = Number(page.innerText) + 1;
@@ -129,33 +147,34 @@ function gotoPreviousPage() {
129
147
  loadDocuments();
130
148
  }
131
149
 
132
- function createTree(obj, schema, index, level = "root") {
133
-
134
- const moduleDocument = document.createElement('div');
135
- moduleDocument.classList.add('document');
136
- const container = document.createElement('ul');
137
- container.classList.add('tree');
138
-
139
- for (let key in obj) {
140
- const value = obj[key];
141
- const type = schema[key]?.instance;
142
- const li = document.createElement('li');
143
-
144
- if (typeof value === 'object' && value !== null) {
145
- li.innerHTML = `<span class="caret collapsible">${key} ${type || "Object"}</span>`;
146
- const child = createTree(value, schema[key]?.options?.type?.paths, index, level + "." + key);
147
- child.classList.add('nested');
148
- child.classList.remove('document');
149
- li.appendChild(child);
150
- } else {
151
- li.innerHTML = `<span class="key">${key}</span> : <span class="value ${type}" ${type === "ObjectId" ? "contenteditable='false'" : "contenteditable='true'"} data-level="${level}" data-index="${index}">${value}</span>`;
152
- }
153
-
154
- container.appendChild(li);
150
+ function renderData(key, value, index, level = "root.", parentDataType = "object") {
151
+ const type = typeof value;
152
+ 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>`;
153
+
154
+ if (Array.isArray(value)) {
155
+ return `
156
+ <details class="field">
157
+ <summary ><span class="data-key">${key}</span><span class="data-type"> (Array - ${value.length})</span></summary>
158
+ <div class="data-value">
159
+ ${value.map((v, i) => renderData(`[${i}]`, v, index, level + key, "array")).join('')}
160
+ </div>
161
+ </details>
162
+ `;
163
+ } else if (type === 'object') {
164
+ return `
165
+ <details class="field">
166
+ <summary><span class="data-key">${key}</span><span class="data-type"> (Object)</span></summary>
167
+ <div class="data-value">
168
+ ${Object.entries(value).map(([k, v]) => renderData(k, v, index, level + key, "object")).join('')}
169
+ </div>
170
+ </details>
171
+ `;
172
+ } else if (typeof value === 'string' && /^[a-f\d]{24}$/i.test(value)) {
173
+
174
+ return `<div class="field"><span class="data-key">${key}</span>: <span class="ObjectId">${value}</span></div>`;
175
+ } else {
176
+ 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
177
  }
156
-
157
- moduleDocument.appendChild(container);
158
- return moduleDocument;
159
178
  }
160
179
 
161
180
  // Handle collapsibles
@@ -169,22 +188,31 @@ document.addEventListener('click', function (e) {
169
188
  }
170
189
  });
171
190
 
172
- function setNestedValue(obj, path, value) {
191
+ function setNestedValue(obj, path, value, parentDataType) {
173
192
  const keys = path.split(".");
174
193
  let current = obj;
194
+ console.log(value)
175
195
 
176
196
  keys.forEach((key, index) => {
177
197
  if (index === keys.length - 1) {
178
- current[key] = value; // Set value at last key
198
+ const arrayIndexMatch = key.match(/\[(\d+)\]/);
199
+ if (arrayIndexMatch) {
200
+ const idx = parseInt(arrayIndexMatch[1], 10);
201
+ current[idx] = value;
202
+ } else {
203
+ current[key] = value;
204
+ }
205
+
179
206
  } else {
180
- if (!current[key] || typeof current[key] !== "object") {
181
- current[key] = {}; // Create nested object if it doesn't exist
207
+ if (parentDataType === "array" && !Array.isArray(current[key])) {
208
+ current[key] = [];
209
+ } else if (parentDataType === "object" && typeof current[key] !== "object") {
210
+ current[key] = {};
182
211
  }
212
+
183
213
  current = current[key]; // Go deeper
184
214
  }
185
215
  });
186
-
187
- return obj; // Optional: return the updated object
188
216
  }
189
217
 
190
218
  async function updateDocument(index) {
@@ -199,19 +227,24 @@ async function updateDocument(index) {
199
227
  const values = document.querySelectorAll(`[data-index="${index}"]`);
200
228
 
201
229
  values.forEach(el => {
202
- const key = el.previousElementSibling?.innerText?.trim();
230
+ const key = el.previousElementSibling?.textContent?.trim();
203
231
  const level = el.dataset.level;
232
+
204
233
  if (key) {
205
234
  if (level && level !== "root") {
206
235
  // Build the full path
207
236
  const fullPath = `${level.replace("root.", "")}.${key}`;
208
- setNestedValue(updatedDoc, fullPath, el.innerText);
237
+ setNestedValue(updatedDoc, fullPath, el.textContent, el.dataset.parentDataType);
209
238
  } else {
210
- updatedDoc[key] = el.innerText;
239
+ updatedDoc[key] = el.textContent;
211
240
  }
212
241
  }
213
242
  });
214
243
 
244
+ updatedDoc = { ...updatedDoc, ...updatedDoc[""] }
245
+
246
+ console.log(updatedDoc)
247
+
215
248
  const res = await fetch(`/update/${currentModelName}/${id}`, {
216
249
  method: "PUT",
217
250
  headers: { "Content-Type": "application/json" },
@@ -301,18 +334,18 @@ function dragEnd() {
301
334
  async function refreshSideBar() {
302
335
  try {
303
336
  const modelWrapper = document.querySelector('.model-wrapper');
304
-
337
+
305
338
  const res = await fetch("/models");
306
-
339
+
307
340
  if (!res.ok) {
308
341
  const error = await res.json();
309
342
  showModal('error', 'Error Occurred!', error.error || 'Something went wrong, please try again.');
310
343
  return;
311
344
  }
312
-
345
+
313
346
  modelWrapper.innerHTML = "";
314
347
  const data = await res.json();
315
-
348
+
316
349
  data.models.forEach(model => {
317
350
  const modelDiv = document.createElement('div');
318
351
  modelDiv.classList.add('model');
@@ -325,3 +358,7 @@ async function refreshSideBar() {
325
358
  }
326
359
  }
327
360
 
361
+ function autoResize(textarea) {
362
+ textarea.style.height = 'auto'; // Reset the height
363
+ textarea.style.height = textarea.scrollHeight + 'px'; // Set to scrollHeight
364
+ }
@@ -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
- .tree {
231
- padding-left: 20px;
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
- .tree li .String {
239
+ .field .string {
247
240
  color: var(--green-color);
248
241
  }
249
242
 
250
- .tree li .String::after {
243
+ .field .string::after {
251
244
  content: '"';
252
245
  color: var(--green-color);
253
246
  }
254
247
 
255
- .tree li .String::before {
248
+ .field .string::before {
256
249
  content: '"';
257
250
  color: var(--green-color);
258
251
  }
259
252
 
260
- .tree li .Number {
253
+ .field .number {
261
254
  color: var(--blue-color);
262
255
  }
263
256
 
264
- .tree li .ObjectId {
257
+ .field .ObjectId {
265
258
  color: var(--red-color);
266
259
  }
267
260
 
268
- .tree li .ObjectId::after {
261
+ .field .ObjectId::after {
269
262
  content: ' )';
270
263
  color: var(--red-color);
271
264
  }
272
265
 
273
- .tree li .ObjectId::before {
266
+ .field .ObjectId::before {
274
267
  content: 'ObjectId( ';
275
268
  color: var(--red-color);
276
269
  }
277
270
 
278
- .collapsible {
279
- cursor: pointer;
280
- user-select: none;
281
- }
282
-
283
- .tree li .nested {
284
- display: none;
285
- }
286
-
287
- .tree li .active {
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
  }
@@ -0,0 +1,68 @@
1
+ import User from '../models/user.model.js';
2
+
3
+ ;(async function seedUsers() {
4
+ try {
5
+ // Clear existing users
6
+ await User.deleteMany({});
7
+
8
+ // Create two users
9
+ const user1 = await User.create({
10
+ firstName: 'John',
11
+ lastName: 'Doe',
12
+ email: 'john.doe@example.com',
13
+ password: 'password123',
14
+ birthDate: new Date('1990-01-15'),
15
+ isActive: true,
16
+ interests: ['reading', 'hiking', 'photography'],
17
+ scores: [85, 92, 78],
18
+ planet: {
19
+ name: 'Earth',
20
+ description: 'Our home planet'
21
+ },
22
+ address: {
23
+ street: '123 Main St',
24
+ city: 'Boston',
25
+ country: 'USA',
26
+ zipCode: '02108'
27
+ },
28
+ metadata: new Map([
29
+ ['occupation', 'Software Engineer'],
30
+ ['department', 'Engineering']
31
+ ])
32
+ });
33
+
34
+ const user2 = await User.create({
35
+ firstName: 'Jane',
36
+ lastName: 'Smith',
37
+ email: 'jane.smith@example.com',
38
+ password: 'password456',
39
+ birthDate: new Date('1988-06-22'),
40
+ isActive: true,
41
+ interests: ['painting', 'music', 'travel'],
42
+ scores: [95, 88, 90],
43
+ planet: {
44
+ name: 'Mars',
45
+ description: 'The red planet'
46
+ },
47
+ address: {
48
+ street: '456 Park Ave',
49
+ city: 'New York',
50
+ country: 'USA',
51
+ zipCode: '10022'
52
+ },
53
+ metadata: new Map([
54
+ ['occupation', 'Product Manager'],
55
+ ['department', 'Product']
56
+ ])
57
+ });
58
+
59
+ console.log('Users seeded successfully');
60
+ return [user1, user2];
61
+ } catch (error) {
62
+ console.error('Error seeding users:', error);
63
+ throw error;
64
+ }
65
+ })();
66
+
67
+ export default 1;
68
+
package/server.js CHANGED
@@ -2,7 +2,7 @@ import express from 'express';
2
2
  import mongoose from 'mongoose';
3
3
  import path from "path"
4
4
  import loadModels from './utils/loadModels.js';
5
-
5
+ import seedUsers from './seeds/user.seed.js';
6
6
  const app = express();
7
7
  const PORT = 8319;
8
8
 
@@ -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