quar 1.2.4 → 1.2.6
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/README.md +1 -1
- package/package.json +1 -1
- package/public/images/icon.png +0 -0
- package/public/scripts/insertTab.js +155 -0
- package/public/scripts/keyboardCommands.js +16 -0
- package/public/scripts/main.js +365 -0
- package/public/scripts/popup.js +26 -0
- package/public/styles/insertTab.css +74 -0
- package/public/styles/popup.css +71 -0
- package/public/styles/style.css +342 -0
- package/public/styles/variables.css +9 -0
- package/views/pages/index.zare +1 -1
package/README.md
CHANGED
package/package.json
CHANGED
|
Binary file
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
async function toggleInsertTab() {
|
|
2
|
+
const modelName = content.dataset?.modelName;
|
|
3
|
+
if (!modelName) {
|
|
4
|
+
showModal('error', 'Error Occurred!', 'No model selected.');
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const insertTab = document.querySelector('.insert-tab');
|
|
9
|
+
insertTab.classList.toggle('active');
|
|
10
|
+
|
|
11
|
+
if (insertTab.innerHTML.trim() !== '') return;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(`/schema/${modelName}`);
|
|
15
|
+
if (!res.ok) throw new Error();
|
|
16
|
+
|
|
17
|
+
const schema = await res.json();
|
|
18
|
+
|
|
19
|
+
const form = document.createElement('form');
|
|
20
|
+
form.id = 'insert-form';
|
|
21
|
+
|
|
22
|
+
form.addEventListener('submit', async (e) => {
|
|
23
|
+
e.preventDefault();
|
|
24
|
+
|
|
25
|
+
const formData = new FormData(e.target);
|
|
26
|
+
const data = Object.fromEntries(formData.entries());
|
|
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
|
+
|
|
40
|
+
const res = await fetch(`/insert/${modelName}`, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: { 'Content-Type': 'application/json' },
|
|
43
|
+
body: JSON.stringify(data)
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
const error = await res.json();
|
|
48
|
+
showModal('error', 'Error Occurred!', error.error || 'Something went wrong, please try again.');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
loadDocuments();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
for (const key in schema) {
|
|
57
|
+
|
|
58
|
+
if (/\.\$\*$/.test(key)) continue;
|
|
59
|
+
const field = schema[key];
|
|
60
|
+
let inputType = null;
|
|
61
|
+
|
|
62
|
+
const label = document.createElement('label');
|
|
63
|
+
label.innerHTML = `${key}:`;
|
|
64
|
+
label.className = 'label';
|
|
65
|
+
label.htmlFor = key;
|
|
66
|
+
|
|
67
|
+
if (field.type === 'ObjectId') {
|
|
68
|
+
const idRes = await fetch(`/id/${field.ref}`);
|
|
69
|
+
if (!idRes.ok) {
|
|
70
|
+
const error = await idRes.json();
|
|
71
|
+
showModal('error', 'Error Occurred!', error.error || 'Something went wrong, please try again.');
|
|
72
|
+
return;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const ids = await idRes.json();
|
|
76
|
+
const select = document.createElement('select');
|
|
77
|
+
select.id = key;
|
|
78
|
+
select.name = key;
|
|
79
|
+
select.className = 'input';
|
|
80
|
+
|
|
81
|
+
ids.forEach(id => {
|
|
82
|
+
const option = document.createElement('option');
|
|
83
|
+
option.value = id;
|
|
84
|
+
option.innerText = id;
|
|
85
|
+
select.appendChild(option);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
form.append(label, select);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
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
|
+
|
|
128
|
+
const input = document.createElement('input');
|
|
129
|
+
input.type = inputType || 'text';
|
|
130
|
+
input.placeholder = field.type;
|
|
131
|
+
input.id = key;
|
|
132
|
+
input.name = key;
|
|
133
|
+
input.className = 'input';
|
|
134
|
+
input.required = isRequired(field.require);
|
|
135
|
+
if (field.default !== undefined) input.value = field.default;
|
|
136
|
+
|
|
137
|
+
form.append(label, input);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const submitBtn = document.createElement('button');
|
|
141
|
+
submitBtn.type = 'submit';
|
|
142
|
+
submitBtn.innerHTML = 'Submit';
|
|
143
|
+
submitBtn.className = 'btn';
|
|
144
|
+
form.appendChild(submitBtn);
|
|
145
|
+
|
|
146
|
+
insertTab.appendChild(form);
|
|
147
|
+
|
|
148
|
+
} catch (error) {
|
|
149
|
+
showModal('error', 'Error Occurred!', error.message || 'Something went wrong, please try again.');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function isRequired(require) {
|
|
154
|
+
return Array.isArray(require) ? require[0] : require;
|
|
155
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
document.addEventListener("keydown", (e) => {
|
|
2
|
+
|
|
3
|
+
if (e.ctrlKey && e.key === 'o') {
|
|
4
|
+
e.preventDefault();
|
|
5
|
+
toggleInsertTab();
|
|
6
|
+
} else if (e.ctrlKey && e.key == "r") {
|
|
7
|
+
e.preventDefault()
|
|
8
|
+
loadDocuments();
|
|
9
|
+
} else if (e.ctrlKey && e.key == ".") {
|
|
10
|
+
e.preventDefault()
|
|
11
|
+
gotoNextPage();
|
|
12
|
+
} else if (e.ctrlKey && e.key == ",") {
|
|
13
|
+
e.preventDefault()
|
|
14
|
+
gotoPreviousPage();
|
|
15
|
+
}
|
|
16
|
+
})
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
const tabsContainer = document.getElementById("tabs");
|
|
2
|
+
const content = document.getElementById("content");
|
|
3
|
+
let currentModelName = "";
|
|
4
|
+
let loadedDocuments = [];
|
|
5
|
+
const openTabs = {};
|
|
6
|
+
|
|
7
|
+
function openModel(modelName) {
|
|
8
|
+
|
|
9
|
+
if (!openTabs[modelName]) {
|
|
10
|
+
// Create a new tab
|
|
11
|
+
const tab = document.createElement("div");
|
|
12
|
+
tab.className = "tab";
|
|
13
|
+
tab.id = "tab-" + modelName;
|
|
14
|
+
tab.draggable = true;
|
|
15
|
+
tab.innerHTML = modelName + '<span class="close" onclick="closeTab(event, \'' + modelName + '\')">×</span>';
|
|
16
|
+
tab.onclick = () => activateTab(modelName);
|
|
17
|
+
tabsContainer.appendChild(tab);
|
|
18
|
+
|
|
19
|
+
tab.addEventListener("dragstart", dragStart);
|
|
20
|
+
tab.addEventListener("dragover", dragOver);
|
|
21
|
+
tab.addEventListener("drop", drop);
|
|
22
|
+
tab.addEventListener("dragend", dragEnd);
|
|
23
|
+
|
|
24
|
+
openTabs[modelName] = tab;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
activateTab(modelName);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function activateTab(modelName) {
|
|
32
|
+
|
|
33
|
+
// Prevent if tab is already active
|
|
34
|
+
if (content.dataset?.modelName == modelName) return
|
|
35
|
+
|
|
36
|
+
content.dataset.modelName = modelName;
|
|
37
|
+
document.getElementById("page-value").innerText = 1;
|
|
38
|
+
document.querySelectorAll(".operations .btn")?.forEach( btn => btn.disabled = false )
|
|
39
|
+
document.querySelector(".operations select").disabled = false;
|
|
40
|
+
|
|
41
|
+
const insertTab = document.querySelector('.insert-tab');
|
|
42
|
+
insertTab.classList.remove('active');
|
|
43
|
+
insertTab.innerHTML = "";
|
|
44
|
+
// Remove active class from all tabs
|
|
45
|
+
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
|
|
46
|
+
|
|
47
|
+
// Set current tab active
|
|
48
|
+
const currentTab = document.getElementById("tab-" + modelName);
|
|
49
|
+
currentTab.classList.add("active");
|
|
50
|
+
|
|
51
|
+
// Update content
|
|
52
|
+
await loadDocuments();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function closeTab(event, modelName) {
|
|
56
|
+
event.stopPropagation(); // prevent tab click
|
|
57
|
+
const tab = document.getElementById("tab-" + modelName);
|
|
58
|
+
tab.remove();
|
|
59
|
+
delete openTabs[modelName];
|
|
60
|
+
|
|
61
|
+
// Clear content if that tab was active and check if other tabs are open to be active
|
|
62
|
+
if (tab.classList.contains("active")) {
|
|
63
|
+
content.innerHTML = "";
|
|
64
|
+
const keys = Object.keys(openTabs);
|
|
65
|
+
const modelName = keys[keys.length - 1] || null;
|
|
66
|
+
if (modelName) activateTab(modelName);
|
|
67
|
+
else disableAllOptions();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
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
|
+
|
|
79
|
+
async function loadDocuments() {
|
|
80
|
+
|
|
81
|
+
content.innerHTML = "";
|
|
82
|
+
const limit = document.getElementById("limit").value;
|
|
83
|
+
const page = document.getElementById("page-value").innerText;
|
|
84
|
+
const modelName = content.dataset?.modelName;
|
|
85
|
+
|
|
86
|
+
if (!modelName) {
|
|
87
|
+
showModal('error', 'Error Occurred!', 'No model selected.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const response = await fetch(`/models/${modelName}?limit=${limit}&page=${page}`);
|
|
92
|
+
if (response.status !== 200) {
|
|
93
|
+
showModal('error', 'Error Occurred!', 'Something went wrong, please try again.');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const data = await response.json();
|
|
97
|
+
|
|
98
|
+
document.getElementById("document-count").innerText = data.count;
|
|
99
|
+
document.getElementById("page-value").dataset.totalPages = data.totalPages;
|
|
100
|
+
document.getElementById(`${modelName}-doc-count`).innerText = data.totalCount;
|
|
101
|
+
|
|
102
|
+
if (Number(page) >= Number(data.totalPages)) document.getElementById("next-page").disabled = true;
|
|
103
|
+
else document.getElementById("next-page").disabled = false;
|
|
104
|
+
|
|
105
|
+
if (Number(page) <= 1) document.getElementById("previous-page").disabled = true;
|
|
106
|
+
else document.getElementById("previous-page").disabled = false;
|
|
107
|
+
|
|
108
|
+
currentModelName = modelName;
|
|
109
|
+
loadedDocuments = data.documents;
|
|
110
|
+
// Render each array element as its own tree
|
|
111
|
+
data.documents.forEach((doc, index) => {
|
|
112
|
+
const wrapper = document.createElement('div');
|
|
113
|
+
const divDocument = document.createElement('div');
|
|
114
|
+
divDocument.innerHTML += Object.entries(doc).map(([key, value]) => renderData(key, value, index)).join('');
|
|
115
|
+
|
|
116
|
+
divDocument.classList.add('document');
|
|
117
|
+
|
|
118
|
+
wrapper.appendChild(divDocument);
|
|
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>`;
|
|
120
|
+
|
|
121
|
+
content.appendChild(wrapper);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function toggleEditMode(id) {
|
|
126
|
+
const htmlData = document.getElementById(`${id}-html-data`);
|
|
127
|
+
const jsonData = document.getElementById(`${id}-json-data`);
|
|
128
|
+
|
|
129
|
+
htmlData.classList.toggle('hidden');
|
|
130
|
+
jsonData.classList.toggle('hidden');
|
|
131
|
+
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function gotoNextPage() {
|
|
135
|
+
const page = document.getElementById("page-value");
|
|
136
|
+
page.innerText = Number(page.innerText) + 1;
|
|
137
|
+
|
|
138
|
+
if (Number(page.innerText) > Number(page.dataset.totalPages)) {
|
|
139
|
+
page.innerText = Number(page.dataset.totalPages);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
loadDocuments();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function gotoPreviousPage() {
|
|
147
|
+
const page = document.getElementById("page-value").innerText;
|
|
148
|
+
document.getElementById("page-value").innerText = Number(page) - 1;
|
|
149
|
+
|
|
150
|
+
if (Number(page) - 1 < 1) {
|
|
151
|
+
document.getElementById("page-value").innerText = 1;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
loadDocuments();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function renderData(key, value, index, level = "root.", parentDataType = "object") {
|
|
159
|
+
const type = typeof value;
|
|
160
|
+
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>`;
|
|
161
|
+
|
|
162
|
+
if (Array.isArray(value)) {
|
|
163
|
+
return `
|
|
164
|
+
<details class="field">
|
|
165
|
+
<summary ><span class="data-key">${key}</span><span class="data-type"> (Array - ${value.length})</span></summary>
|
|
166
|
+
<div class="data-value">
|
|
167
|
+
${value.map((v, i) => renderData(`[${i}]`, v, index, level + key, "array")).join('')}
|
|
168
|
+
</div>
|
|
169
|
+
</details>
|
|
170
|
+
`;
|
|
171
|
+
} else if (type === 'object') {
|
|
172
|
+
return `
|
|
173
|
+
<details class="field">
|
|
174
|
+
<summary><span class="data-key">${key}</span><span class="data-type"> (Object)</span></summary>
|
|
175
|
+
<div class="data-value">
|
|
176
|
+
${Object.entries(value).map(([k, v]) => renderData(k, v, index, level + key, "object")).join('')}
|
|
177
|
+
</div>
|
|
178
|
+
</details>
|
|
179
|
+
`;
|
|
180
|
+
} else if (typeof value === 'string' && /^[a-f\d]{24}$/i.test(value)) {
|
|
181
|
+
|
|
182
|
+
return `<div class="field"><span class="data-key">${key}</span>: <span class="ObjectId">${value}</span></div>`;
|
|
183
|
+
} else {
|
|
184
|
+
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>`;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Handle collapsibles
|
|
189
|
+
document.addEventListener('click', function (e) {
|
|
190
|
+
if (e.target.classList.contains('collapsible')) {
|
|
191
|
+
e.target.classList.toggle("caret-down");
|
|
192
|
+
const nested = e.target.nextElementSibling;
|
|
193
|
+
if (nested) {
|
|
194
|
+
nested.classList.toggle("active");
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
function setNestedValue(obj, path, value, parentDataType) {
|
|
200
|
+
const keys = path.split(".");
|
|
201
|
+
let current = obj;
|
|
202
|
+
|
|
203
|
+
keys.forEach((key, index) => {
|
|
204
|
+
if (index === keys.length - 1) {
|
|
205
|
+
const arrayIndexMatch = key.match(/\[(\d+)\]/);
|
|
206
|
+
if (arrayIndexMatch) {
|
|
207
|
+
const idx = parseInt(arrayIndexMatch[1], 10);
|
|
208
|
+
current[idx] = value;
|
|
209
|
+
} else {
|
|
210
|
+
current[key] = value;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
} else {
|
|
214
|
+
if (parentDataType === "array" && !Array.isArray(current[key])) {
|
|
215
|
+
current[key] = [];
|
|
216
|
+
} else if (parentDataType === "object" && typeof current[key] !== "object") {
|
|
217
|
+
current[key] = {};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
current = current[key]; // Go deeper
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function updateDocument(index) {
|
|
226
|
+
|
|
227
|
+
const confirmUpdate = confirm("Are you sure you want to update this document?");
|
|
228
|
+
if (!confirmUpdate) return;
|
|
229
|
+
|
|
230
|
+
const doc = loadedDocuments[index];
|
|
231
|
+
const id = doc._id;
|
|
232
|
+
|
|
233
|
+
let updatedDoc = {};
|
|
234
|
+
const values = document.querySelectorAll(`[data-index="${index}"]`);
|
|
235
|
+
|
|
236
|
+
values.forEach(el => {
|
|
237
|
+
const key = el.previousElementSibling?.textContent?.trim();
|
|
238
|
+
const level = el.dataset.level;
|
|
239
|
+
|
|
240
|
+
if (key) {
|
|
241
|
+
if (level && level !== "root") {
|
|
242
|
+
// Build the full path
|
|
243
|
+
const fullPath = `${level.replace("root.", "")}.${key}`;
|
|
244
|
+
setNestedValue(updatedDoc, fullPath, el.textContent, el.dataset.parentDataType);
|
|
245
|
+
} else {
|
|
246
|
+
updatedDoc[key] = el.textContent;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
updatedDoc = { ...updatedDoc, ...updatedDoc[""] }
|
|
252
|
+
|
|
253
|
+
const res = await fetch(`/update/${currentModelName}/${id}`, {
|
|
254
|
+
method: "PUT",
|
|
255
|
+
headers: { "Content-Type": "application/json" },
|
|
256
|
+
body: JSON.stringify(updatedDoc),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (res.ok) {
|
|
260
|
+
showModal('info', 'Document Updated', 'Document updated successfully.');
|
|
261
|
+
loadDocuments();
|
|
262
|
+
} else {
|
|
263
|
+
const data = await res.json();
|
|
264
|
+
showModal('error', 'Update Failed', data.error || 'Update failed, please try again.');
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function deleteDoc(modelName, id) {
|
|
269
|
+
const confirmDelete = confirm("Are you sure you want to delete this document?");
|
|
270
|
+
if (!confirmDelete) return;
|
|
271
|
+
|
|
272
|
+
const res = await fetch(`/delete/${modelName}/${id}`, {
|
|
273
|
+
method: "DELETE"
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
if (res.ok) {
|
|
277
|
+
showModal('info', 'Document Deleted', 'Document deleted successfully.');
|
|
278
|
+
openModel(modelName);
|
|
279
|
+
const data = await res.json();
|
|
280
|
+
document.getElementById(`${modelName}-doc-count`).innerText = data.count || 0;
|
|
281
|
+
loadDocuments()
|
|
282
|
+
} else {
|
|
283
|
+
showModal('error', 'Delete Failed', await res.json().error || 'Delete failed, please try again.');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function deleteAllDocs() {
|
|
288
|
+
try {
|
|
289
|
+
const confirmDelete = confirm("Are you sure you want to delete all documents?");
|
|
290
|
+
if (!confirmDelete) return;
|
|
291
|
+
|
|
292
|
+
const modelName = content.dataset?.modelName;
|
|
293
|
+
if (!modelName) {
|
|
294
|
+
showModal('error', 'Error Occurred!', 'No model selected.');
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const res = await fetch(`/delete-all/${modelName}`, { method: "DELETE" });
|
|
299
|
+
if (res.ok) {
|
|
300
|
+
showModal('info', 'All Documents Deleted', 'All documents deleted successfully.');
|
|
301
|
+
loadDocuments();
|
|
302
|
+
} else {
|
|
303
|
+
showModal('error', 'Delete Failed', await res.json().error || 'Delete failed, please try again.');
|
|
304
|
+
}
|
|
305
|
+
} catch (error) {
|
|
306
|
+
showModal('error', 'Delete Failed', error.message || 'Delete failed, please try again.');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function dragStart(e) {
|
|
311
|
+
dragSrc = this;
|
|
312
|
+
e.dataTransfer.effectAllowed = "move";
|
|
313
|
+
this.classList.add("dragging");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function dragOver(e) {
|
|
317
|
+
e.preventDefault();
|
|
318
|
+
e.dataTransfer.dropEffect = "move";
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function drop(e) {
|
|
322
|
+
e.preventDefault();
|
|
323
|
+
if (dragSrc !== this) {
|
|
324
|
+
// Swap positions
|
|
325
|
+
const draggedIndex = Array.from(tabsContainer.children).indexOf(dragSrc);
|
|
326
|
+
const targetIndex = Array.from(tabsContainer.children).indexOf(this);
|
|
327
|
+
|
|
328
|
+
if (draggedIndex < targetIndex) {
|
|
329
|
+
tabsContainer.insertBefore(dragSrc, this.nextSibling);
|
|
330
|
+
} else {
|
|
331
|
+
tabsContainer.insertBefore(dragSrc, this);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function dragEnd() {
|
|
337
|
+
this.classList.remove("dragging");
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function refreshSideBar() {
|
|
341
|
+
try {
|
|
342
|
+
const modelWrapper = document.querySelector('.model-wrapper');
|
|
343
|
+
|
|
344
|
+
const res = await fetch("/models");
|
|
345
|
+
|
|
346
|
+
if (!res.ok) {
|
|
347
|
+
const error = await res.json();
|
|
348
|
+
showModal('error', 'Error Occurred!', error.error || 'Something went wrong, please try again.');
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
modelWrapper.innerHTML = "";
|
|
353
|
+
const data = await res.json();
|
|
354
|
+
|
|
355
|
+
data.models.forEach(model => {
|
|
356
|
+
const modelDiv = document.createElement('div');
|
|
357
|
+
modelDiv.classList.add('model');
|
|
358
|
+
modelDiv.onclick = () => openModel(model.name);
|
|
359
|
+
modelDiv.innerHTML = `${model.name} <span id="${model.name}-doc-count">${model.count}</span>`;
|
|
360
|
+
modelWrapper.appendChild(modelDiv);
|
|
361
|
+
});
|
|
362
|
+
} catch (error) {
|
|
363
|
+
showModal('error', 'Error Occurred!', error.message || 'Something went wrong, please try again.');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function showModal(type, title, message) {
|
|
2
|
+
const popup = document.getElementById('popup');
|
|
3
|
+
const box = document.getElementById('popupBox');
|
|
4
|
+
const titleEl = document.getElementById('popupTitle');
|
|
5
|
+
const msgEl = document.getElementById('popupMessage');
|
|
6
|
+
|
|
7
|
+
box.className = 'popup-box';
|
|
8
|
+
if (type === 'warning') box.classList.add('warning');
|
|
9
|
+
else if (type === 'info') box.classList.add('info');
|
|
10
|
+
|
|
11
|
+
titleEl.textContent = title;
|
|
12
|
+
msgEl.textContent = message;
|
|
13
|
+
|
|
14
|
+
popup.style.display = 'flex';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function closePopup() {
|
|
18
|
+
document.getElementById('popup').style.display = 'none';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
window.onclick = function (e) {
|
|
22
|
+
const popup = document.getElementById('popup');
|
|
23
|
+
if (e.target === popup) {
|
|
24
|
+
closePopup();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
@import './variables.css';
|
|
2
|
+
|
|
3
|
+
.insert-tab {
|
|
4
|
+
width: 40%;
|
|
5
|
+
max-width: 400px;
|
|
6
|
+
height: 100%;
|
|
7
|
+
background-color: var(--bg-color);
|
|
8
|
+
position: fixed;
|
|
9
|
+
top: 0;
|
|
10
|
+
right: -100%;
|
|
11
|
+
z-index: 1000;
|
|
12
|
+
overflow-y: auto;
|
|
13
|
+
padding: 20px;
|
|
14
|
+
border-left: 2px solid var(--muted-color);
|
|
15
|
+
transition: right 0.3s ease-in-out;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.insert-tab.active {
|
|
19
|
+
right: 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#insert-form {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
gap: 10px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#insert-form label {
|
|
29
|
+
font-size: 16px;
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
color: var(--text-color);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#insert-form input,
|
|
35
|
+
select,
|
|
36
|
+
textarea {
|
|
37
|
+
width: 100%;
|
|
38
|
+
padding: 10px;
|
|
39
|
+
border: 1px solid var(--muted-color);
|
|
40
|
+
border-radius: 5px;
|
|
41
|
+
background-color: var(--bg-color);
|
|
42
|
+
color: var(--text-color);
|
|
43
|
+
}
|
|
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
|
+
|
|
60
|
+
#insert-form .btn {
|
|
61
|
+
background: transparent;
|
|
62
|
+
color: var(--text-color);
|
|
63
|
+
border: none;
|
|
64
|
+
padding: 5px 10px;
|
|
65
|
+
border: 1px solid var(--muted-color);
|
|
66
|
+
border-radius: 5px;
|
|
67
|
+
transition: background 0.1s ease-in-out;
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
|
|
70
|
+
&:hover {
|
|
71
|
+
background: var(--blue-color);
|
|
72
|
+
color: var(--bg-color);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
@import './variables.css';
|
|
2
|
+
|
|
3
|
+
.popup-overlay {
|
|
4
|
+
display: none;
|
|
5
|
+
position: fixed;
|
|
6
|
+
top: 0;
|
|
7
|
+
left: 0;
|
|
8
|
+
width: 100vw;
|
|
9
|
+
height: 100vh;
|
|
10
|
+
background: rgba(0, 0, 0, 0.5);
|
|
11
|
+
justify-content: center;
|
|
12
|
+
align-items: center;
|
|
13
|
+
z-index: 9999;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.popup-box {
|
|
17
|
+
background: var(--bg-color);
|
|
18
|
+
padding: 20px 25px;
|
|
19
|
+
border-radius: 10px;
|
|
20
|
+
max-width: 400px;
|
|
21
|
+
text-align: center;
|
|
22
|
+
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
|
23
|
+
animation: slideIn 0.4s ease;
|
|
24
|
+
position: relative;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.popup-box h2 {
|
|
28
|
+
margin-top: 0;
|
|
29
|
+
color: var(--red-color);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.popup-box.warning h2 {
|
|
33
|
+
color: var(--yellow-color);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.popup-box.info h2 {
|
|
37
|
+
color: var(--green-color);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.popup-box p {
|
|
41
|
+
margin: 10px 0 20px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.close-btn {
|
|
45
|
+
background: var(--red-color);
|
|
46
|
+
color: white;
|
|
47
|
+
border: none;
|
|
48
|
+
padding: 8px 16px;
|
|
49
|
+
border-radius: 5px;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.popup-box.warning .close-btn {
|
|
54
|
+
background: var(--yellow-color);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.popup-box.info .close-btn {
|
|
58
|
+
background: var(--green-color);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@keyframes slideIn {
|
|
62
|
+
from {
|
|
63
|
+
transform: translateY(-30px);
|
|
64
|
+
opacity: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
to {
|
|
68
|
+
transform: translateY(0);
|
|
69
|
+
opacity: 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
|
|
2
|
+
|
|
3
|
+
* {
|
|
4
|
+
margin: 0;
|
|
5
|
+
padding: 0;
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
font-family: 'Inter', sans-serif;
|
|
11
|
+
font-size: 14px;
|
|
12
|
+
display: flex;
|
|
13
|
+
background: var(--bg-color);
|
|
14
|
+
color: #E0E0E0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.sidebar {
|
|
18
|
+
position: fixed;
|
|
19
|
+
top: 0;
|
|
20
|
+
width: 15vw;
|
|
21
|
+
background: #282b30;
|
|
22
|
+
border-right: 1px solid var(--muted-color);
|
|
23
|
+
height: 100vh;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.sidebar-header {
|
|
27
|
+
font-size: 13px;
|
|
28
|
+
font-weight: 600;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: space-between;
|
|
32
|
+
background: #1e2124;
|
|
33
|
+
padding: 10px;
|
|
34
|
+
border-bottom: 1px solid var(--muted-color);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.sidebar-header .btn {
|
|
38
|
+
color: #E0E0E0;
|
|
39
|
+
background-color: transparent;
|
|
40
|
+
border: none;
|
|
41
|
+
font-size: 12px;
|
|
42
|
+
padding: 0 4px;
|
|
43
|
+
border-radius: 5px;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
transition: background 0.1s ease-in-out;
|
|
46
|
+
|
|
47
|
+
&:hover {
|
|
48
|
+
background: #2E3A59;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.model-wrapper {
|
|
53
|
+
padding: 10px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.model {
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
display: flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
justify-content: space-between;
|
|
61
|
+
padding: 5px;
|
|
62
|
+
margin: 5px 0;
|
|
63
|
+
border-radius: 5px;
|
|
64
|
+
color: #E0E0E0;
|
|
65
|
+
|
|
66
|
+
&:hover {
|
|
67
|
+
background: #2E3A59;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.model span {
|
|
72
|
+
color: var(--muted-color);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.main {
|
|
76
|
+
margin-left: auto;
|
|
77
|
+
height: 100vh;
|
|
78
|
+
width: 85vw;
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.tabs {
|
|
84
|
+
display: flex;
|
|
85
|
+
border-bottom: 1px solid var(--muted-color);
|
|
86
|
+
background: #1e2124;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.tab {
|
|
90
|
+
padding: 5px 10px;
|
|
91
|
+
display: flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
color: var(--muted-color);
|
|
94
|
+
border-left: 1px solid var(--muted-color);
|
|
95
|
+
border-right: 1px solid var(--muted-color);
|
|
96
|
+
position: relative;
|
|
97
|
+
margin: 1px 0;
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
transition: color 0.2s ease-in-out;
|
|
100
|
+
|
|
101
|
+
&:hover {
|
|
102
|
+
color: white;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
.tab.active {
|
|
108
|
+
color: white;
|
|
109
|
+
background-color: #2E3A59;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.tab .close {
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
font-weight: bold;
|
|
115
|
+
padding: 0 5px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.operations {
|
|
119
|
+
display: flex;
|
|
120
|
+
background: #1e2124;
|
|
121
|
+
align-items: center;
|
|
122
|
+
padding: 8px 5px;
|
|
123
|
+
gap: 10px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.operations .btn {
|
|
127
|
+
background: var(--muted-color);
|
|
128
|
+
color: #E0E0E0;
|
|
129
|
+
border: none;
|
|
130
|
+
padding: 4px 8px;
|
|
131
|
+
border-radius: 5px;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
transition: background 0.1s ease-in-out;
|
|
134
|
+
|
|
135
|
+
&:hover {
|
|
136
|
+
background: #2E3A59;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.operations .btn:disabled {
|
|
141
|
+
background-color: #3a3f4b;
|
|
142
|
+
color: #888;
|
|
143
|
+
cursor: not-allowed;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.operations .limit-wrapper,
|
|
147
|
+
.count-wrapper,
|
|
148
|
+
.page-wrapper {
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
padding: 2px 5px;
|
|
152
|
+
gap: 5px;
|
|
153
|
+
border-radius: 5px;
|
|
154
|
+
background-color: var(--muted-color);
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
|
|
157
|
+
&:hover {
|
|
158
|
+
background: #2E3A59;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.operations .limit-wrapper:hover #limit {
|
|
163
|
+
background: #2E3A59;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.operations .text {
|
|
167
|
+
padding: 2px 5px;
|
|
168
|
+
width: 100%;
|
|
169
|
+
height: 100%;
|
|
170
|
+
font-size: 12px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.operations .limit-div,
|
|
174
|
+
.count-div,
|
|
175
|
+
.page-div {
|
|
176
|
+
width: fit-content;
|
|
177
|
+
border-left: 2px solid #424549;
|
|
178
|
+
height: 100%;
|
|
179
|
+
color: #E0E0E0;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.operations .count-div,
|
|
183
|
+
.page-div {
|
|
184
|
+
padding: 2px 5px;
|
|
185
|
+
font-size: 12px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.operations .limit-wrapper #limit {
|
|
189
|
+
background-color: var(--muted-color);
|
|
190
|
+
border: none;
|
|
191
|
+
color: #E0E0E0;
|
|
192
|
+
padding: 2px 5px;
|
|
193
|
+
width: fit-content;
|
|
194
|
+
height: 100%;
|
|
195
|
+
font-size: 12px;
|
|
196
|
+
|
|
197
|
+
&:hover {
|
|
198
|
+
background: #2E3A59;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.content {
|
|
203
|
+
padding: 10px;
|
|
204
|
+
flex: 1;
|
|
205
|
+
border-top: 1px solid var(--muted-color);
|
|
206
|
+
background: #23272a;
|
|
207
|
+
overflow-y: scroll;
|
|
208
|
+
-ms-overflow-style: none;
|
|
209
|
+
scrollbar-width: none;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.content::-webkit-scrollbar {
|
|
213
|
+
display: none;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.hidden {
|
|
217
|
+
display: none;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
summary::marker{
|
|
221
|
+
color: var(--muted-color);
|
|
222
|
+
font-size: 10px;
|
|
223
|
+
margin-right: 10px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.document {
|
|
227
|
+
padding: 10px;
|
|
228
|
+
border: 1px solid var(--muted-color);
|
|
229
|
+
background: #1e2124;
|
|
230
|
+
border-radius: 10px;
|
|
231
|
+
margin-top: 10px;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.field {
|
|
235
|
+
padding: 5px 20px;
|
|
236
|
+
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.field .string {
|
|
240
|
+
color: var(--green-color);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.field .string::after {
|
|
244
|
+
content: '"';
|
|
245
|
+
color: var(--green-color);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.field .string::before {
|
|
249
|
+
content: '"';
|
|
250
|
+
color: var(--green-color);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.field .number {
|
|
254
|
+
color: var(--blue-color);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.field .ObjectId {
|
|
258
|
+
color: var(--red-color);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.field .ObjectId::after {
|
|
262
|
+
content: ' )';
|
|
263
|
+
color: var(--red-color);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.field .ObjectId::before {
|
|
267
|
+
content: 'ObjectId( ';
|
|
268
|
+
color: var(--red-color);
|
|
269
|
+
}
|
|
270
|
+
|
|
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;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.document-actions {
|
|
284
|
+
display: flex;
|
|
285
|
+
justify-content: space-between;
|
|
286
|
+
padding: 10px;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.document-actions .updateDocument {
|
|
290
|
+
background: transparent;
|
|
291
|
+
color: #E0E0E0;
|
|
292
|
+
border: none;
|
|
293
|
+
padding: 5px 10px;
|
|
294
|
+
border: 1px solid var(--muted-color);
|
|
295
|
+
border-radius: 5px;
|
|
296
|
+
transition: background 0.1s ease-in-out;
|
|
297
|
+
cursor: pointer;
|
|
298
|
+
|
|
299
|
+
&:hover {
|
|
300
|
+
background: var(--green-color);
|
|
301
|
+
color: var(--bg-color);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.document-actions .deleteDocument {
|
|
306
|
+
background: transparent;
|
|
307
|
+
color: #E0E0E0;
|
|
308
|
+
border: none;
|
|
309
|
+
padding: 5px 10px;
|
|
310
|
+
border: 1px solid var(--muted-color);
|
|
311
|
+
border-radius: 5px;
|
|
312
|
+
transition: background 0.1s ease-in-out;
|
|
313
|
+
cursor: pointer;
|
|
314
|
+
|
|
315
|
+
&:hover {
|
|
316
|
+
background: var(--red-color);
|
|
317
|
+
color: var(--bg-color);
|
|
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
|
+
}
|
|
342
|
+
}
|
package/views/pages/index.zare
CHANGED
|
@@ -19,7 +19,7 @@ serve (
|
|
|
19
19
|
|
|
20
20
|
<div class="main">
|
|
21
21
|
<div class="tabs" id="tabs">
|
|
22
|
-
<div class="tab" id="tab-quar-studio"><img src="/
|
|
22
|
+
<div class="tab" id="tab-quar-studio"><img src="/images/icon.png" alt="uv dex icon" width="24px"/></div>
|
|
23
23
|
</div>
|
|
24
24
|
<div class="operations">
|
|
25
25
|
<button class="refreshModel btn" onclick="loadDocuments()">⭮</button>
|