quar 1.2.0 → 1.2.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/index.js CHANGED
@@ -51,9 +51,11 @@ try {
51
51
  (async () => {
52
52
 
53
53
  await mongoose.connect(`${args.uri || "mongodb://localhost:27017/"}${args.db}`)
54
+ console.log( chalk.blue.bold('[INFO]'), chalk.gray("Quar: Database connection established"))
55
+
54
56
  app.listen(app.get('port'), () => {
55
57
  const url = `http://127.0.0.1:${app.get('port')}`;
56
- console.log(chalk.green.bold('[SUCCESS]') + ` Server is running on ${chalk.underline(url)}`);
58
+ console.log(chalk.green.bold('[SUCCESS]') + chalk.gray(` Server is running on ${chalk.underline(url)}`));
57
59
  open(url);
58
60
  });
59
61
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quar",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
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",
@@ -8,7 +8,8 @@
8
8
  "quar": "./index.js"
9
9
  },
10
10
  "scripts": {
11
- "start": "node index.js"
11
+ "start": "node index.js",
12
+ "dev": "node .\\index.js --model .\\models\\ --db shopDB"
12
13
  },
13
14
  "author": "@IsmailBinMujeeb (IsmailBinMujeeb@gmail.com)",
14
15
  "license": "MIT",
@@ -25,6 +25,18 @@ async function toggleInsertTab() {
25
25
  const formData = new FormData(e.target);
26
26
  const data = Object.fromEntries(formData.entries());
27
27
 
28
+ const checkboxes = e.target.querySelectorAll('input[type="checkbox"]');
29
+ checkboxes.forEach(cb => {
30
+ data[cb.name] = cb.checked;
31
+ });
32
+
33
+ const textareas = e.target.querySelectorAll('textarea');
34
+ textareas.forEach(async tarea => {
35
+
36
+ data[tarea.name] = JSON.parse(tarea.value)
37
+
38
+ })
39
+
28
40
  const res = await fetch(`/insert/${modelName}`, {
29
41
  method: 'POST',
30
42
  headers: { 'Content-Type': 'application/json' },
@@ -40,8 +52,12 @@ async function toggleInsertTab() {
40
52
  loadDocuments();
41
53
  });
42
54
 
55
+
43
56
  for (const key in schema) {
57
+
58
+ if (/\.\$\*$/.test(key)) continue;
44
59
  const field = schema[key];
60
+ let inputType = null;
45
61
 
46
62
  const label = document.createElement('label');
47
63
  label.innerHTML = `${key}:`;
@@ -73,8 +89,44 @@ async function toggleInsertTab() {
73
89
  continue;
74
90
  }
75
91
 
92
+ if (field.type === 'String') inputType = 'text';
93
+ else if (field.type === 'Number') inputType = 'number';
94
+ else if (field.type === 'Boolean') inputType = 'checkbox';
95
+ else if (field.type === 'Date') inputType = 'date';
96
+ else if (field.type === 'Array' || field.type === "Map" || field.type === "Object" || field.type == "Mixed" || field.type === "Buffer") {
97
+ const input = document.createElement('textarea');
98
+
99
+ input.placeholder = field.type;
100
+ input.id = key;
101
+ input.name = key;
102
+ input.className = 'input';
103
+ field.type === "Array" ? input.textContent = "[ ]" : input.textContent = "{ }";
104
+ input.required = isRequired(field.require);
105
+ if (field.default !== undefined) input.value = field.default;
106
+
107
+ form.append(label, input);
108
+ continue;
109
+ };
110
+
111
+ if (field.enum) {
112
+ const select = document.createElement('select');
113
+ select.id = key;
114
+ select.name = key;
115
+ select.className = 'input';
116
+
117
+ field.enum.forEach(id => {
118
+ const option = document.createElement('option');
119
+ option.value = id;
120
+ option.innerText = id;
121
+ select.appendChild(option);
122
+ });
123
+
124
+ form.append(label, select);
125
+ continue;
126
+ }
127
+
76
128
  const input = document.createElement('input');
77
- input.type = field.type || 'text';
129
+ input.type = inputType || 'text';
78
130
  input.placeholder = field.type;
79
131
  input.id = key;
80
132
  input.name = key;
@@ -35,6 +35,8 @@ async function activateTab(modelName) {
35
35
 
36
36
  content.dataset.modelName = modelName;
37
37
  document.getElementById("page-value").innerText = 1;
38
+ document.querySelectorAll(".operations .btn")?.forEach( btn => btn.disabled = false )
39
+ document.querySelector(".operations select").disabled = false;
38
40
 
39
41
  const insertTab = document.querySelector('.insert-tab');
40
42
  insertTab.classList.remove('active');
@@ -62,9 +64,18 @@ function closeTab(event, modelName) {
62
64
  const keys = Object.keys(openTabs);
63
65
  const modelName = keys[keys.length - 1] || null;
64
66
  if (modelName) activateTab(modelName);
67
+ else disableAllOptions();
65
68
  }
66
69
  }
67
70
 
71
+ function disableAllOptions() {
72
+ content.dataset.modelName = '';
73
+
74
+ document.querySelectorAll(".operations .btn")?.forEach( btn => btn.disabled = true );
75
+ document.getElementById("document-count").innerText = 0;
76
+ document.querySelector(".operations select").disabled = true;
77
+ }
78
+
68
79
  async function loadDocuments() {
69
80
 
70
81
  content.innerHTML = "";
@@ -100,12 +111,9 @@ async function loadDocuments() {
100
111
  data.documents.forEach((doc, index) => {
101
112
  const wrapper = document.createElement('div');
102
113
  const divDocument = document.createElement('div');
103
- wrapper.innerHTML += `<div class="top-container"><button class="editDocument" onclick="toggleEditMode('${doc._id}')">Edit</button></div>`;
104
114
  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
115
 
107
116
  divDocument.classList.add('document');
108
- divDocument.id = `${doc._id}-html-data`;
109
117
 
110
118
  wrapper.appendChild(divDocument);
111
119
  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>`;
@@ -191,7 +199,6 @@ document.addEventListener('click', function (e) {
191
199
  function setNestedValue(obj, path, value, parentDataType) {
192
200
  const keys = path.split(".");
193
201
  let current = obj;
194
- console.log(value)
195
202
 
196
203
  keys.forEach((key, index) => {
197
204
  if (index === keys.length - 1) {
@@ -243,8 +250,6 @@ async function updateDocument(index) {
243
250
 
244
251
  updatedDoc = { ...updatedDoc, ...updatedDoc[""] }
245
252
 
246
- console.log(updatedDoc)
247
-
248
253
  const res = await fetch(`/update/${currentModelName}/${id}`, {
249
254
  method: "PUT",
250
255
  headers: { "Content-Type": "application/json" },
@@ -273,6 +278,7 @@ async function deleteDoc(modelName, id) {
273
278
  openModel(modelName);
274
279
  const data = await res.json();
275
280
  document.getElementById(`${modelName}-doc-count`).innerText = data.count || 0;
281
+ loadDocuments()
276
282
  } else {
277
283
  showModal('error', 'Delete Failed', await res.json().error || 'Delete failed, please try again.');
278
284
  }
@@ -356,9 +362,4 @@ async function refreshSideBar() {
356
362
  } catch (error) {
357
363
  showModal('error', 'Error Occurred!', error.message || 'Something went wrong, please try again.');
358
364
  }
359
- }
360
-
361
- function autoResize(textarea) {
362
- textarea.style.height = 'auto'; // Reset the height
363
- textarea.style.height = textarea.scrollHeight + 'px'; // Set to scrollHeight
364
365
  }
@@ -31,7 +31,9 @@
31
31
  color: var(--text-color);
32
32
  }
33
33
 
34
- #insert-form input, select {
34
+ #insert-form input,
35
+ select,
36
+ textarea {
35
37
  width: 100%;
36
38
  padding: 10px;
37
39
  border: 1px solid var(--muted-color);
@@ -40,6 +42,21 @@
40
42
  color: var(--text-color);
41
43
  }
42
44
 
45
+ #insert-form textarea {
46
+ resize: vertical;
47
+ min-height: fit-content;
48
+ }
49
+
50
+ #insert-form input[type="checkbox"] {
51
+ width: 16px;
52
+ height: 16px;
53
+ accent-color: var(--green-color);
54
+ background-color: var(--bg-color);
55
+ border: 1px solid var(--muted-color);
56
+ border-radius: 10px;
57
+ cursor: pointer;
58
+ }
59
+
43
60
  #insert-form .btn {
44
61
  background: transparent;
45
62
  color: var(--text-color);
@@ -54,4 +71,4 @@
54
71
  background: var(--blue-color);
55
72
  color: var(--bg-color);
56
73
  }
57
- }
74
+ }
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
- import seedUsers from './seeds/user.seed.js';
5
+
6
6
  const app = express();
7
7
  const PORT = 8319;
8
8
 
@@ -108,6 +108,7 @@ app.get("/schema/:modelName", async (req, res) => {
108
108
  }
109
109
  }
110
110
  getSchema(paths)
111
+
111
112
  res.status(200).json(schema);
112
113
  } catch (error) {
113
114
  res.status(500).json({ error: error.message || "Intenral Server Error" })
@@ -22,4 +22,6 @@ export default (modelPath) => {
22
22
  await import(moduleUrl.href);
23
23
  }
24
24
  });
25
+
26
+ console.log(chalk.blue.bold('[INFO]'), chalk.gray("Models loaded from: " + modelPath))
25
27
  }
@@ -37,12 +37,12 @@ serve (
37
37
  <div class="text">Count</div>
38
38
  <div class="count-div" id="document-count">0</div>
39
39
  </div>
40
- <button class="btn" id="previous-page" onclick="gotoPreviousPage()">Previous</button>
40
+ <button class="btn" id="previous-page" onclick="gotoPreviousPage()" disabled>Previous</button>
41
41
  <div class="page-wrapper" id="page" data-page="1">
42
42
  <div class="text">Page</div>
43
43
  <div class="page-div" id="page-value">1</div>
44
44
  </div>
45
- <button class="btn" id="next-page" onclick="gotoNextPage()">Next</button>
45
+ <button class="btn" id="next-page" onclick="gotoNextPage()" disabled>Next</button>
46
46
  <button class="btn" onclick="toggleInsertTab()">Add Record</button>
47
47
  <button class="btn" onclick="deleteAllDocs()">🗑</button>
48
48
  </div>
@@ -1,71 +0,0 @@
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;
@@ -1,68 +0,0 @@
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
-