beanbagdb-components 0.2.7 → 0.2.8
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/package.json +1 -1
- package/dist/app.js +0 -134
- package/dist/app.style.css +0 -51
- package/dist/data.html +0 -1086
- package/dist/doc.html +0 -61
- package/dist/index.html +0 -28
- package/dist/local_cache.js +0 -210
- package/dist/settings.html +0 -39
- package/dist/test.html +0 -34
package/dist/data.html
DELETED
|
@@ -1,1086 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en" data-bs-theme="dark">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
-
<!-- <script type="module" src="../dist/main.js"></script> -->
|
|
7
|
-
|
|
8
|
-
<title>BBDB-Database</title>
|
|
9
|
-
<link
|
|
10
|
-
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"
|
|
11
|
-
rel="stylesheet"
|
|
12
|
-
integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"
|
|
13
|
-
crossorigin="anonymous"
|
|
14
|
-
/>
|
|
15
|
-
<link
|
|
16
|
-
href="https://unpkg.com/tabulator-tables@6.3.1/dist/css/tabulator.min.css"
|
|
17
|
-
rel="stylesheet"
|
|
18
|
-
/>
|
|
19
|
-
<link
|
|
20
|
-
href="https://unpkg.com/tabulator-tables@6.3.1/dist/css/tabulator_site_dark.min.css"
|
|
21
|
-
rel="stylesheet"
|
|
22
|
-
/>
|
|
23
|
-
|
|
24
|
-
<link rel="stylesheet" href="app.style.css" />
|
|
25
|
-
|
|
26
|
-
<script
|
|
27
|
-
type="text/javascript"
|
|
28
|
-
src="https://unpkg.com/tabulator-tables@6.3.1/dist/js/tabulator.min.js"
|
|
29
|
-
></script>
|
|
30
|
-
|
|
31
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/split.js/1.6.0/split.min.js"></script>
|
|
32
|
-
|
|
33
|
-
<script src="app.js"></script>
|
|
34
|
-
<script src="local_cache.js"></script>
|
|
35
|
-
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
|
36
|
-
<script type="module">
|
|
37
|
-
import { BBDB } from "../dist/main.js";
|
|
38
|
-
const { createApp, ref, onMounted, nextTick } = Vue;
|
|
39
|
-
let DB;
|
|
40
|
-
let DB_cache;
|
|
41
|
-
let dataTable;
|
|
42
|
-
|
|
43
|
-
function linkEditor(cell, onRendered, success, cancel, editorParams) {
|
|
44
|
-
const editor = document.createElement("input");
|
|
45
|
-
editor.type = "text";
|
|
46
|
-
editor.value = cell.getValue() || "";
|
|
47
|
-
editor.style.padding = "3px";
|
|
48
|
-
editor.style.width = "100%";
|
|
49
|
-
editor.style.boxSizing = "border-box";
|
|
50
|
-
|
|
51
|
-
const oldValue = cell.getOldValue();
|
|
52
|
-
const rowData = cell.getRow().getData();
|
|
53
|
-
const rowId = rowData._id;
|
|
54
|
-
|
|
55
|
-
console.log("Row ID:", rowId); // Debug: verify ID is correct
|
|
56
|
-
|
|
57
|
-
onRendered(() => {
|
|
58
|
-
editor.focus();
|
|
59
|
-
editor.select();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
function attemptCommit() {
|
|
63
|
-
const newValue = editor.value.trim();
|
|
64
|
-
|
|
65
|
-
if (newValue === oldValue) {
|
|
66
|
-
success(newValue);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (!/^[a-zA-Z0-9]+$/.test(newValue)) {
|
|
71
|
-
cancel();
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
editor.disabled = true;
|
|
76
|
-
editor.style.opacity = "0.6";
|
|
77
|
-
|
|
78
|
-
DB.update({
|
|
79
|
-
criteria: { _id: rowId },
|
|
80
|
-
updates: {
|
|
81
|
-
meta: {
|
|
82
|
-
link: newValue,
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
})
|
|
86
|
-
.then((response) => {
|
|
87
|
-
console.log(response);
|
|
88
|
-
success(newValue);
|
|
89
|
-
// if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
90
|
-
// return response.json();
|
|
91
|
-
})
|
|
92
|
-
// .then((data) => {
|
|
93
|
-
// if (data.success) {
|
|
94
|
-
// success(newValue);
|
|
95
|
-
// } else {
|
|
96
|
-
// throw new Error(data.error || "API rejected");
|
|
97
|
-
// }
|
|
98
|
-
// })
|
|
99
|
-
.catch((error) => {
|
|
100
|
-
console.error("Update failed:", error);
|
|
101
|
-
//cell.restoreOldValue();
|
|
102
|
-
cancel();
|
|
103
|
-
showMessage("error", error.message);
|
|
104
|
-
})
|
|
105
|
-
.finally(() => {
|
|
106
|
-
editor.disabled = false;
|
|
107
|
-
editor.style.opacity = "1";
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
editor.addEventListener("change", attemptCommit);
|
|
112
|
-
editor.addEventListener("blur", attemptCommit);
|
|
113
|
-
editor.addEventListener("keydown", (e) => {
|
|
114
|
-
if (e.key === "Enter") attemptCommit();
|
|
115
|
-
if (e.key === "Escape") cancel();
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
return editor;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function titleEditor(cell, onRendered, success, cancel, editorParams) {
|
|
122
|
-
const editor = document.createElement("input");
|
|
123
|
-
editor.type = "text";
|
|
124
|
-
editor.value = cell.getValue() || "";
|
|
125
|
-
editor.style.padding = "3px";
|
|
126
|
-
editor.style.width = "100%";
|
|
127
|
-
editor.style.boxSizing = "border-box";
|
|
128
|
-
|
|
129
|
-
const oldValue = cell.getOldValue();
|
|
130
|
-
const rowData = cell.getRow().getData();
|
|
131
|
-
const rowId = rowData._id;
|
|
132
|
-
|
|
133
|
-
console.log("Row ID:", rowId); // Debug: verify ID is correct
|
|
134
|
-
|
|
135
|
-
onRendered(() => {
|
|
136
|
-
editor.focus();
|
|
137
|
-
editor.select();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
function attemptCommit() {
|
|
141
|
-
const newValue = editor.value.trim();
|
|
142
|
-
|
|
143
|
-
if (newValue === oldValue) {
|
|
144
|
-
success(newValue);
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Title validation: alphanumeric + spaces, dashes, underscores (1-100 chars)
|
|
149
|
-
if (!/^[a-zA-Z0-9\s\-_]{1,100}$/.test(newValue)) {
|
|
150
|
-
cancel();
|
|
151
|
-
showMessage(
|
|
152
|
-
"error",
|
|
153
|
-
"Title must be 1-100 chars (letters, numbers, spaces, -, _)"
|
|
154
|
-
);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
editor.disabled = true;
|
|
159
|
-
editor.style.opacity = "0.6";
|
|
160
|
-
|
|
161
|
-
DB.update({
|
|
162
|
-
criteria: { _id: rowId },
|
|
163
|
-
updates: {
|
|
164
|
-
meta: {
|
|
165
|
-
title: newValue,
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
})
|
|
169
|
-
.then((response) => {
|
|
170
|
-
console.log(response);
|
|
171
|
-
success(newValue);
|
|
172
|
-
})
|
|
173
|
-
.catch((error) => {
|
|
174
|
-
console.error("Update failed:", error);
|
|
175
|
-
cancel();
|
|
176
|
-
showMessage("error", error.message);
|
|
177
|
-
})
|
|
178
|
-
.finally(() => {
|
|
179
|
-
editor.disabled = false;
|
|
180
|
-
editor.style.opacity = "1";
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
editor.addEventListener("change", attemptCommit);
|
|
185
|
-
editor.addEventListener("blur", attemptCommit);
|
|
186
|
-
editor.addEventListener("keydown", (e) => {
|
|
187
|
-
if (e.key === "Enter") attemptCommit();
|
|
188
|
-
if (e.key === "Escape") cancel();
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
return editor;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function tagsEditor(cell, onRendered, success, cancel, editorParams) {
|
|
195
|
-
const editor = document.createElement("input");
|
|
196
|
-
editor.type = "text";
|
|
197
|
-
editor.value = (cell.getValue() || []).join(", ");
|
|
198
|
-
editor.style.padding = "3px";
|
|
199
|
-
editor.style.width = "100%";
|
|
200
|
-
editor.style.boxSizing = "border-box";
|
|
201
|
-
editor.placeholder = "Enter tags separated by commas";
|
|
202
|
-
|
|
203
|
-
const oldValue = cell.getOldValue() || [];
|
|
204
|
-
const rowData = cell.getRow().getData();
|
|
205
|
-
const rowId = rowData._id;
|
|
206
|
-
|
|
207
|
-
console.log("Row ID:", rowId);
|
|
208
|
-
|
|
209
|
-
onRendered(() => {
|
|
210
|
-
editor.focus();
|
|
211
|
-
editor.select();
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
function attemptCommit() {
|
|
215
|
-
const inputValue = editor.value.trim();
|
|
216
|
-
|
|
217
|
-
if (
|
|
218
|
-
inputValue ===
|
|
219
|
-
(Array.isArray(oldValue) ? oldValue.join(", ") : oldValue)
|
|
220
|
-
) {
|
|
221
|
-
success(oldValue);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const newTags = inputValue
|
|
226
|
-
? inputValue
|
|
227
|
-
.split(",")
|
|
228
|
-
.map((tag) => tag.trim())
|
|
229
|
-
.filter((tag) => tag.length > 0)
|
|
230
|
-
: [];
|
|
231
|
-
|
|
232
|
-
if (newTags.length === 0 && inputValue !== "") {
|
|
233
|
-
cancel();
|
|
234
|
-
showMessage("error", "At least one tag required or clear all tags");
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const invalidTag = newTags.find(
|
|
239
|
-
(tag) => !/^[a-zA-Z0-9\-_\s]{1,50}$/.test(tag)
|
|
240
|
-
);
|
|
241
|
-
if (invalidTag) {
|
|
242
|
-
cancel();
|
|
243
|
-
showMessage(
|
|
244
|
-
"error",
|
|
245
|
-
`Invalid tag: "${invalidTag}". Use letters, numbers, -, _, spaces`
|
|
246
|
-
);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
editor.disabled = true;
|
|
251
|
-
editor.style.opacity = "0.6";
|
|
252
|
-
|
|
253
|
-
DB.update({
|
|
254
|
-
criteria: { _id: rowId },
|
|
255
|
-
updates: {
|
|
256
|
-
meta: {
|
|
257
|
-
tags: newTags,
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
})
|
|
261
|
-
.then((response) => {
|
|
262
|
-
console.log("Tags updated:", newTags, response);
|
|
263
|
-
success(newTags);
|
|
264
|
-
})
|
|
265
|
-
.catch((error) => {
|
|
266
|
-
console.error("Update failed:", error);
|
|
267
|
-
cell.restoreOldValue();
|
|
268
|
-
cancel();
|
|
269
|
-
showMessage("error", error.message);
|
|
270
|
-
})
|
|
271
|
-
.finally(() => {
|
|
272
|
-
editor.disabled = false;
|
|
273
|
-
editor.style.opacity = "1";
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
editor.addEventListener("change", attemptCommit);
|
|
278
|
-
editor.addEventListener("blur", attemptCommit);
|
|
279
|
-
editor.addEventListener("keydown", (e) => {
|
|
280
|
-
if (e.key === "Enter") attemptCommit();
|
|
281
|
-
if (e.key === "Escape") cancel();
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
return editor;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async function search_ready(db_details, db) {
|
|
288
|
-
//console.log(APP_CACHE);
|
|
289
|
-
|
|
290
|
-
const fetch_schemas = async () => {
|
|
291
|
-
let query = await db.search({
|
|
292
|
-
selector: { schema: "schema" },
|
|
293
|
-
fields: [],
|
|
294
|
-
});
|
|
295
|
-
let schemas = [];
|
|
296
|
-
|
|
297
|
-
query?.docs.map((itm) => {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
let schema_fields = ["schema","_id","meta.tags","meta.link","meta.created_on","meta.title"]
|
|
301
|
-
let col_names = []
|
|
302
|
-
Object.keys(itm.data.schema.properties).map((ky) => {
|
|
303
|
-
schema_fields.push(`data.${ky}`)
|
|
304
|
-
col_names.push({
|
|
305
|
-
field: `data_${ky}`,
|
|
306
|
-
title: ky
|
|
307
|
-
})
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
schemas.push({
|
|
312
|
-
title: `${itm?.data?.title}`,
|
|
313
|
-
columns: col_names,
|
|
314
|
-
criteria: {
|
|
315
|
-
selector:{
|
|
316
|
-
schema: itm?.data?.name
|
|
317
|
-
},
|
|
318
|
-
fields:schema_fields
|
|
319
|
-
},
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
return schemas;
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
const fetch_tags = async () => {
|
|
326
|
-
const query = await db.search({
|
|
327
|
-
selector: {},
|
|
328
|
-
limit: 2000,
|
|
329
|
-
fields: ["meta.tags"],
|
|
330
|
-
});
|
|
331
|
-
const tagSet = new Set();
|
|
332
|
-
query.docs.forEach((doc) => {
|
|
333
|
-
if (Array.isArray(doc.meta?.tags)) {
|
|
334
|
-
doc.meta.tags.forEach((tag) => tagSet.add(tag));
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
return Array.from(tagSet).map((tag) => ({
|
|
338
|
-
title: `#${tag}`,
|
|
339
|
-
criteria: {
|
|
340
|
-
"meta.tags": {
|
|
341
|
-
$in: [tag],
|
|
342
|
-
},
|
|
343
|
-
},
|
|
344
|
-
}));
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
// CACHE + STORE FOR REFRESH
|
|
348
|
-
const schemaKey = `${db_details.name}_schemas`;
|
|
349
|
-
const tagsKey = `${db_details.name}_tags`;
|
|
350
|
-
|
|
351
|
-
const filters = {
|
|
352
|
-
schema: await APP_CACHE.load(schemaKey, fetch_schemas),
|
|
353
|
-
tags: await APP_CACHE.load(tagsKey, fetch_tags),
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
// STORE GLOBALS FOR REFRESH BUTTONS + VUE
|
|
357
|
-
window.db_details = db_details;
|
|
358
|
-
window.refreshFunctions = {
|
|
359
|
-
[schemaKey]: fetch_schemas,
|
|
360
|
-
[tagsKey]: fetch_tags,
|
|
361
|
-
};
|
|
362
|
-
window.filters = filters;
|
|
363
|
-
|
|
364
|
-
console.log("Filters loaded:", filters);
|
|
365
|
-
return filters;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
document.addEventListener("DOMContentLoaded", async () => {
|
|
369
|
-
try {
|
|
370
|
-
const db_details = await initPage();
|
|
371
|
-
DB = await BBDB(db_details.dbObj);
|
|
372
|
-
await DB.ready();
|
|
373
|
-
|
|
374
|
-
await search_ready(db_details, DB);
|
|
375
|
-
//DB_cache = new JsonCache(db_details.name)
|
|
376
|
-
//await DB_cache.init()
|
|
377
|
-
|
|
378
|
-
//console.log(await DB_cache.keys())
|
|
379
|
-
|
|
380
|
-
//console.log(DB);
|
|
381
|
-
//console.log(db_details);
|
|
382
|
-
//showMessage("success", `Loaded database: ${db_details.name}`);
|
|
383
|
-
|
|
384
|
-
createApp({
|
|
385
|
-
setup() {
|
|
386
|
-
const tableRef = ref(null);
|
|
387
|
-
const table = ref(null);
|
|
388
|
-
const loading = ref(false);
|
|
389
|
-
const recordCount = ref(0);
|
|
390
|
-
|
|
391
|
-
// FILTERS REFS
|
|
392
|
-
const schemas = ref([]);
|
|
393
|
-
const tags = ref([]);
|
|
394
|
-
|
|
395
|
-
// Transform records for table
|
|
396
|
-
const transformRecords = (docs) => {
|
|
397
|
-
console.log(docs)
|
|
398
|
-
return docs.map((doc) => {
|
|
399
|
-
const row = {
|
|
400
|
-
schema: doc.schema || "unknown",
|
|
401
|
-
_id: doc._id,
|
|
402
|
-
_rev: doc._rev,
|
|
403
|
-
};
|
|
404
|
-
Object.keys(doc.meta).map((ky) => {
|
|
405
|
-
row[`meta_${ky}`] = doc.meta[ky];
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
Object.keys(doc.data).map((ky) => {
|
|
409
|
-
row[`data_${ky}`] = doc.data[ky];
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
// if (doc.data) {
|
|
413
|
-
// Object.assign(row, doc.data);
|
|
414
|
-
// }
|
|
415
|
-
return row;
|
|
416
|
-
});
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
const generateColumns = (additional=[]) => {
|
|
420
|
-
let columns = [
|
|
421
|
-
{
|
|
422
|
-
title: "Title",
|
|
423
|
-
field: "meta_title",
|
|
424
|
-
sorter: "string",
|
|
425
|
-
editor: titleEditor,
|
|
426
|
-
},
|
|
427
|
-
{
|
|
428
|
-
title: "Schema",
|
|
429
|
-
field: "schema",
|
|
430
|
-
sorter: "string",
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
title: "Link",
|
|
434
|
-
field: "meta_link",
|
|
435
|
-
sorter: "string",
|
|
436
|
-
editor: linkEditor,
|
|
437
|
-
},
|
|
438
|
-
// {
|
|
439
|
-
// title: "Document ID",
|
|
440
|
-
// field: "_id",
|
|
441
|
-
// sorter: "string",
|
|
442
|
-
// },
|
|
443
|
-
// {
|
|
444
|
-
// title: "Revision ID",
|
|
445
|
-
// field: "_rev",
|
|
446
|
-
// sorter: "string",
|
|
447
|
-
// },
|
|
448
|
-
...additional,
|
|
449
|
-
{
|
|
450
|
-
title: "Created On",
|
|
451
|
-
field: "meta_created_on",
|
|
452
|
-
formatter: function (cell) {
|
|
453
|
-
const ts = Number(cell.getValue());
|
|
454
|
-
if (!ts || isNaN(ts)) return "Invalid Date";
|
|
455
|
-
|
|
456
|
-
// Adjust multiplier based on your epoch unit:
|
|
457
|
-
// - Use *1000 if seconds → ms (common for Unix)
|
|
458
|
-
// - Use *1 if already ms (JavaScript Date expects ms)
|
|
459
|
-
const date = new Date(ts * 1000);
|
|
460
|
-
|
|
461
|
-
// Format as YYYY-MM-DD HH:mm:ss (customize as needed)
|
|
462
|
-
const yyyy = date.getFullYear();
|
|
463
|
-
const mm = String(date.getMonth() + 1).padStart(2, "0");
|
|
464
|
-
const dd = String(date.getDate()).padStart(2, "0");
|
|
465
|
-
const hh = String(date.getHours()).padStart(2, "0");
|
|
466
|
-
const min = String(date.getMinutes()).padStart(2, "0");
|
|
467
|
-
const ss = String(date.getSeconds()).padStart(2, "0");
|
|
468
|
-
|
|
469
|
-
return `${yyyy}-${mm}-${dd} ${hh}:${min}`;
|
|
470
|
-
},
|
|
471
|
-
},
|
|
472
|
-
{
|
|
473
|
-
title: "Tags",
|
|
474
|
-
field: "meta_tags",
|
|
475
|
-
//sorter: "array",
|
|
476
|
-
editor: tagsEditor,
|
|
477
|
-
formatter: function (cell) {
|
|
478
|
-
const tags = cell.getValue() || [];
|
|
479
|
-
return tags.length ? tags.join(", ") : "";
|
|
480
|
-
},
|
|
481
|
-
},
|
|
482
|
-
{
|
|
483
|
-
title: "Actions",
|
|
484
|
-
field: "actions",
|
|
485
|
-
width: 75,
|
|
486
|
-
minWidth: 60,
|
|
487
|
-
hozAlign: "center",
|
|
488
|
-
headerSort: false,
|
|
489
|
-
formatter: function (cell) {
|
|
490
|
-
return "..."; // Empty cell, just trigger for cellClick
|
|
491
|
-
},
|
|
492
|
-
cellClick: function (e, cell) {
|
|
493
|
-
const rowData = cell.getRow().getData();
|
|
494
|
-
const menu = document.createElement("div");
|
|
495
|
-
menu.className =
|
|
496
|
-
"position-absolute bg-dark border rounded shadow p-2";
|
|
497
|
-
menu.style.zIndex = "1000";
|
|
498
|
-
menu.innerHTML = `
|
|
499
|
-
<button class="btn btn-sm btn-outline-light w-100 mb-1 copy-id" data-id="${
|
|
500
|
-
rowData._id
|
|
501
|
-
}">Copy ID</button>
|
|
502
|
-
${
|
|
503
|
-
rowData.meta_link
|
|
504
|
-
? `<button class="btn btn-sm btn-outline-light w-100 mb-1 copy-link" data-link="${rowData.meta_link}">Copy Link</button>`
|
|
505
|
-
: ""
|
|
506
|
-
}
|
|
507
|
-
<button class="btn btn-sm btn-outline-light w-100 mb-1 related-btn" data-id="${
|
|
508
|
-
rowData._id
|
|
509
|
-
}"> Related docs</button>
|
|
510
|
-
<button class="btn btn-sm btn-outline-light w-100 mb-1 download-btn" data-id="${
|
|
511
|
-
rowData._id
|
|
512
|
-
}">Download doc</button>
|
|
513
|
-
<button class="btn btn-sm btn-primary w-100 edit-btn" data-id="${
|
|
514
|
-
rowData._id
|
|
515
|
-
}">Edit</button>
|
|
516
|
-
|
|
517
|
-
<div class="dropdown-divider"></div>
|
|
518
|
-
|
|
519
|
-
<button class="btn btn-sm btn-danger w-100 delete-btn" data-id="${
|
|
520
|
-
rowData._id
|
|
521
|
-
}">Delete</button>
|
|
522
|
-
`;
|
|
523
|
-
|
|
524
|
-
// Position menu near click
|
|
525
|
-
const rect = cell.getElement().getBoundingClientRect();
|
|
526
|
-
menu.style.left = rect.left + "px";
|
|
527
|
-
menu.style.top = rect.bottom + 5 + "px";
|
|
528
|
-
|
|
529
|
-
document.body.appendChild(menu);
|
|
530
|
-
|
|
531
|
-
// Single click handlers
|
|
532
|
-
menu
|
|
533
|
-
.querySelector(".copy-id")
|
|
534
|
-
?.addEventListener("click", (e) => {
|
|
535
|
-
navigator.clipboard.writeText(rowData._id);
|
|
536
|
-
showMessage("success", `Copied ID: ${rowData._id}`);
|
|
537
|
-
menu.remove();
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
menu
|
|
541
|
-
.querySelector(".copy-link")
|
|
542
|
-
?.addEventListener("click", (e) => {
|
|
543
|
-
navigator.clipboard.writeText(rowData.meta_link);
|
|
544
|
-
showMessage("success", `Copied Link`);
|
|
545
|
-
menu.remove();
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
menu
|
|
549
|
-
.querySelector(".edit-btn")
|
|
550
|
-
?.addEventListener("click", (e) => {
|
|
551
|
-
showMessage("info", `Edit: ${rowData._id}`);
|
|
552
|
-
console.log("Edit:", rowData._id); // Load into pane 3 here
|
|
553
|
-
menu.remove();
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
menu
|
|
557
|
-
.querySelector(".related-btn")
|
|
558
|
-
?.addEventListener("click", () => {
|
|
559
|
-
if (
|
|
560
|
-
confirm(
|
|
561
|
-
`Are you sure you want to delete:\n"${
|
|
562
|
-
rowData.meta_title || "No title"
|
|
563
|
-
}"\nID: ${rowData._id}?`
|
|
564
|
-
)
|
|
565
|
-
) {
|
|
566
|
-
cell.getRow().delete(); // Tabulator's built-in row delete
|
|
567
|
-
showMessage("success", `Deleted: ${rowData._id}`);
|
|
568
|
-
}
|
|
569
|
-
menu.remove();
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
menu
|
|
573
|
-
.querySelector(".delete-btn")
|
|
574
|
-
?.addEventListener("click", () => {
|
|
575
|
-
if (
|
|
576
|
-
confirm(
|
|
577
|
-
`Are you sure you want to delete:\n"${
|
|
578
|
-
rowData.meta_title || "No title"
|
|
579
|
-
}"\nID: ${rowData._id}?`
|
|
580
|
-
)
|
|
581
|
-
) {
|
|
582
|
-
cell.getRow().delete();
|
|
583
|
-
showMessage("success", `Deleted: ${rowData._id}`);
|
|
584
|
-
}
|
|
585
|
-
menu.remove();
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
// Remove menu on outside click
|
|
589
|
-
setTimeout(() => {
|
|
590
|
-
const removeMenu = (e) => {
|
|
591
|
-
if (!menu.contains(e.target)) {
|
|
592
|
-
menu.remove();
|
|
593
|
-
document.removeEventListener("click", removeMenu);
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
document.addEventListener("click", removeMenu);
|
|
597
|
-
}, 100);
|
|
598
|
-
},
|
|
599
|
-
},
|
|
600
|
-
];
|
|
601
|
-
return columns;
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
const loadData = async (criteria = {}) => {
|
|
605
|
-
try {
|
|
606
|
-
loading.value = true;
|
|
607
|
-
const result = await DB.search({ selector: criteria });
|
|
608
|
-
recordCount.value = result.docs.length;
|
|
609
|
-
|
|
610
|
-
//const allFields = getAllFields(result.docs);
|
|
611
|
-
//console.log(allFields)
|
|
612
|
-
const tableData = transformRecords(result.docs);
|
|
613
|
-
//console.log(tableData)
|
|
614
|
-
const columns = generateColumns();
|
|
615
|
-
|
|
616
|
-
if (table.value) {
|
|
617
|
-
table.value.destroy();
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
await nextTick();
|
|
621
|
-
|
|
622
|
-
table.value = new Tabulator(tableRef.value, {
|
|
623
|
-
data: tableData,
|
|
624
|
-
//autoColumns:true,
|
|
625
|
-
columns: columns,
|
|
626
|
-
layout: "fitColumns",
|
|
627
|
-
responsiveLayout: "hide",
|
|
628
|
-
pagination: true,
|
|
629
|
-
paginationSize: 25,
|
|
630
|
-
paginationSizeSelector: [10, 25, 50, 100],
|
|
631
|
-
groupBy: "schema",
|
|
632
|
-
groupStartOpen: true,
|
|
633
|
-
download: true,
|
|
634
|
-
downloadConfig: {
|
|
635
|
-
csv: { downloadButton: true },
|
|
636
|
-
excel: { downloadButton: true },
|
|
637
|
-
},
|
|
638
|
-
placeholder: "No data - click Search to load",
|
|
639
|
-
});
|
|
640
|
-
} catch (error) {
|
|
641
|
-
console.error("Load failed:", error);
|
|
642
|
-
} finally {
|
|
643
|
-
loading.value = false;
|
|
644
|
-
}
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
// FILTER CLICK HANDLERS
|
|
648
|
-
const applyFilter = async (filter) => {
|
|
649
|
-
console.log(filter);
|
|
650
|
-
if (table.value) {
|
|
651
|
-
loading.value = true;
|
|
652
|
-
try {
|
|
653
|
-
const result = await DB.search(filter.criteria );
|
|
654
|
-
let newColumns = generateColumns(filter.columns)
|
|
655
|
-
table.value.setColumns(newColumns)
|
|
656
|
-
const tableData = transformRecords(result.docs);
|
|
657
|
-
table.value.replaceData(tableData); // Refresh table
|
|
658
|
-
recordCount.value = tableData.length;
|
|
659
|
-
} catch (error) {
|
|
660
|
-
console.error("Filter failed:", error);
|
|
661
|
-
} finally {
|
|
662
|
-
loading.value = false;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
const refreshSchemas = async () => {
|
|
668
|
-
const key = `${window.db_details.name}_schemas`;
|
|
669
|
-
await APP_CACHE.refresh(key, window.refreshFunctions[key]);
|
|
670
|
-
window.filters.schema = await APP_CACHE.get(key);
|
|
671
|
-
schemas.value = window.filters.schema || [];
|
|
672
|
-
};
|
|
673
|
-
|
|
674
|
-
const refreshTags = async () => {
|
|
675
|
-
const key = `${window.db_details.name}_tags`;
|
|
676
|
-
await APP_CACHE.refresh(key, window.refreshFunctions[key]);
|
|
677
|
-
window.filters.tags = await APP_CACHE.get(key);
|
|
678
|
-
tags.value = window.filters.tags || [];
|
|
679
|
-
};
|
|
680
|
-
|
|
681
|
-
onMounted(async () => {
|
|
682
|
-
loadData({});
|
|
683
|
-
|
|
684
|
-
// Load filters into Vue
|
|
685
|
-
await search_ready(db_details, DB);
|
|
686
|
-
schemas.value = window.filters.schema || [];
|
|
687
|
-
tags.value = window.filters.tags || [];
|
|
688
|
-
|
|
689
|
-
// loadData({});
|
|
690
|
-
Split(["#pane1", "#pane2", "#pane3"], {
|
|
691
|
-
sizes: [0, 80, 20],
|
|
692
|
-
minSize: [175, 300, 100], // Minimum sizes for each pane
|
|
693
|
-
gutterSize: 2, // Size of the draggable gutter
|
|
694
|
-
snapOffset: 30, // Snap to edge if dragged within 30px
|
|
695
|
-
direction: "horizontal", // Explicitly set horizontal direction
|
|
696
|
-
cursor: "col-resize", // Cursor style for dragging
|
|
697
|
-
});
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
const jsonQuery = ref('{"selector":{},"limit":1000}');
|
|
701
|
-
const queryResultCount = ref(0);
|
|
702
|
-
const queryError = ref("");
|
|
703
|
-
const jsonQueryValid = ref(true);
|
|
704
|
-
|
|
705
|
-
// Add these methods to setup()
|
|
706
|
-
const validateJsonQuery = () => {
|
|
707
|
-
try {
|
|
708
|
-
JSON.parse(jsonQuery.value);
|
|
709
|
-
queryError.value = "";
|
|
710
|
-
jsonQueryValid.value = true;
|
|
711
|
-
showMessage("success", "Valid JSON");
|
|
712
|
-
} catch (e) {
|
|
713
|
-
queryError.value = `Invalid JSON: ${e.message}`;
|
|
714
|
-
jsonQueryValid.value = false;
|
|
715
|
-
}
|
|
716
|
-
};
|
|
717
|
-
|
|
718
|
-
const runJsonQuery = async () => {
|
|
719
|
-
if (!jsonQueryValid.value) {
|
|
720
|
-
showMessage("error", "Fix JSON first");
|
|
721
|
-
return;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
try {
|
|
725
|
-
loading.value = true;
|
|
726
|
-
const queryObj = JSON.parse(jsonQuery.value);
|
|
727
|
-
const result = await DB.search(queryObj);
|
|
728
|
-
|
|
729
|
-
queryResultCount.value = result.docs.length;
|
|
730
|
-
queryError.value = "";
|
|
731
|
-
|
|
732
|
-
// Update table if valid result
|
|
733
|
-
if (result.docs && result.docs.length > 0) {
|
|
734
|
-
const tableData = transformRecords(result.docs);
|
|
735
|
-
if (table.value) {
|
|
736
|
-
table.value.replaceData(tableData);
|
|
737
|
-
recordCount.value = tableData.length;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
// showMessage('success', `${result.docs.length} docs loaded`);
|
|
742
|
-
} catch (error) {
|
|
743
|
-
queryError.value = `Query failed: ${error.message}`;
|
|
744
|
-
console.error("Query error:", error);
|
|
745
|
-
showMessage("error", error.message);
|
|
746
|
-
} finally {
|
|
747
|
-
loading.value = false;
|
|
748
|
-
}
|
|
749
|
-
};
|
|
750
|
-
|
|
751
|
-
return {
|
|
752
|
-
tableRef,
|
|
753
|
-
table,
|
|
754
|
-
loading,
|
|
755
|
-
recordCount,
|
|
756
|
-
loadData,
|
|
757
|
-
schemas,
|
|
758
|
-
tags,
|
|
759
|
-
applyFilter,
|
|
760
|
-
refreshSchemas,
|
|
761
|
-
refreshTags,
|
|
762
|
-
jsonQuery,
|
|
763
|
-
queryResultCount,
|
|
764
|
-
queryError,
|
|
765
|
-
jsonQueryValid,
|
|
766
|
-
validateJsonQuery,
|
|
767
|
-
runJsonQuery,
|
|
768
|
-
};
|
|
769
|
-
},
|
|
770
|
-
}).mount("#app");
|
|
771
|
-
} catch (error) {
|
|
772
|
-
showMessage("error", error.message);
|
|
773
|
-
}
|
|
774
|
-
});
|
|
775
|
-
</script>
|
|
776
|
-
</head>
|
|
777
|
-
<body>
|
|
778
|
-
<!-- REPLACE YOUR BODY CONTENT WITH THIS - Uses your existing Bootstrap 5 -->
|
|
779
|
-
<div id="app" class="d-flex h-100 overflow-hidden">
|
|
780
|
-
<!-- PANE 1: Query (10%) -->
|
|
781
|
-
<div id="pane1" class="d-flex flex-column border-end">
|
|
782
|
-
<nav class="navbar bg-body-tertiary">
|
|
783
|
-
<div class="container-fluid">
|
|
784
|
-
<a class="navbar-brand"> Search</a>
|
|
785
|
-
<div class="d-flex">
|
|
786
|
-
<!-- <button class="btn btn-sm">
|
|
787
|
-
<svg
|
|
788
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
789
|
-
width="16"
|
|
790
|
-
height="16"
|
|
791
|
-
fill="currentColor"
|
|
792
|
-
class="bi bi-arrow-clockwise"
|
|
793
|
-
viewBox="0 0 16 16"
|
|
794
|
-
>
|
|
795
|
-
<path
|
|
796
|
-
fill-rule="evenodd"
|
|
797
|
-
d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"
|
|
798
|
-
/>
|
|
799
|
-
<path
|
|
800
|
-
d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"
|
|
801
|
-
/>
|
|
802
|
-
</svg>
|
|
803
|
-
</button> -->
|
|
804
|
-
<!-- <small class="badge bg-secondary">{{ recordCount }} records</small> -->
|
|
805
|
-
<!-- <button class="btn btn-sm" @click="loadData">
|
|
806
|
-
<svg
|
|
807
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
808
|
-
width="16"
|
|
809
|
-
height="16"
|
|
810
|
-
fill="currentColor"
|
|
811
|
-
class="bi bi-caret-right-fill"
|
|
812
|
-
viewBox="0 0 16 16"
|
|
813
|
-
>
|
|
814
|
-
<path
|
|
815
|
-
d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"
|
|
816
|
-
/>
|
|
817
|
-
</svg>
|
|
818
|
-
</button> -->
|
|
819
|
-
</div>
|
|
820
|
-
</div>
|
|
821
|
-
</nav>
|
|
822
|
-
<div>
|
|
823
|
-
<div class="accordion accordion-flush" id="accordionFlushExample">
|
|
824
|
-
<div class="accordion-item">
|
|
825
|
-
<h2 class="accordion-header">
|
|
826
|
-
<button
|
|
827
|
-
class="accordion-button collapsed"
|
|
828
|
-
type="button"
|
|
829
|
-
data-bs-toggle="collapse"
|
|
830
|
-
data-bs-target="#flush-collapseOne"
|
|
831
|
-
aria-expanded="false"
|
|
832
|
-
aria-controls="flush-collapseOne"
|
|
833
|
-
>
|
|
834
|
-
Schemas
|
|
835
|
-
</button>
|
|
836
|
-
</h2>
|
|
837
|
-
<div
|
|
838
|
-
id="flush-collapseOne"
|
|
839
|
-
class="accordion-collapse collapse"
|
|
840
|
-
data-bs-parent="#accordionFlushExample"
|
|
841
|
-
>
|
|
842
|
-
<div class="accordion-body p-0">
|
|
843
|
-
<div
|
|
844
|
-
v-if="schemas.length"
|
|
845
|
-
class="list-group list-group-flush"
|
|
846
|
-
>
|
|
847
|
-
<a
|
|
848
|
-
v-for="schema in schemas"
|
|
849
|
-
:key="schema.criteria.schema"
|
|
850
|
-
class="list-group-item list-group-item-action p-2 small"
|
|
851
|
-
@click="applyFilter(schema)"
|
|
852
|
-
style="cursor: pointer"
|
|
853
|
-
>
|
|
854
|
-
{{ schema.title }}
|
|
855
|
-
</a>
|
|
856
|
-
</div>
|
|
857
|
-
<div v-else class="text-muted small p-2">
|
|
858
|
-
Loading schemas...
|
|
859
|
-
</div>
|
|
860
|
-
<div class="border-top pt-1">
|
|
861
|
-
<button
|
|
862
|
-
class="btn btn-sm btn-outline-secondary m-2"
|
|
863
|
-
@click="refreshSchemas"
|
|
864
|
-
>
|
|
865
|
-
Refresh Schemas
|
|
866
|
-
</button>
|
|
867
|
-
</div>
|
|
868
|
-
</div>
|
|
869
|
-
</div>
|
|
870
|
-
</div>
|
|
871
|
-
<div class="accordion-item">
|
|
872
|
-
<h2 class="accordion-header">
|
|
873
|
-
<button
|
|
874
|
-
class="accordion-button collapsed"
|
|
875
|
-
type="button"
|
|
876
|
-
data-bs-toggle="collapse"
|
|
877
|
-
data-bs-target="#flush-collapseTwo"
|
|
878
|
-
aria-expanded="false"
|
|
879
|
-
aria-controls="flush-collapseTwo"
|
|
880
|
-
>
|
|
881
|
-
JSON Query
|
|
882
|
-
</button>
|
|
883
|
-
</h2>
|
|
884
|
-
<div
|
|
885
|
-
id="flush-collapseTwo"
|
|
886
|
-
class="accordion-collapse collapse"
|
|
887
|
-
data-bs-parent="#accordionFlushExample"
|
|
888
|
-
>
|
|
889
|
-
<div class="accordion-body p-1">
|
|
890
|
-
<!-- JSON Editor -->
|
|
891
|
-
<div class="mb-2">
|
|
892
|
-
<textarea
|
|
893
|
-
v-model="jsonQuery"
|
|
894
|
-
class="form-control form-control-sm"
|
|
895
|
-
rows="6"
|
|
896
|
-
placeholder='{ "selector": {}, "limit": 1000 }'
|
|
897
|
-
></textarea>
|
|
898
|
-
</div>
|
|
899
|
-
|
|
900
|
-
<!-- Validate + Run buttons -->
|
|
901
|
-
<div class="d-flex gap-1 mb-2">
|
|
902
|
-
<button
|
|
903
|
-
class="btn btn-sm btn-outline-secondary flex-grow-1"
|
|
904
|
-
@click="validateJsonQuery"
|
|
905
|
-
>
|
|
906
|
-
Validate
|
|
907
|
-
</button>
|
|
908
|
-
<button
|
|
909
|
-
class="btn btn-sm btn-primary flex-grow-1"
|
|
910
|
-
@click="runJsonQuery"
|
|
911
|
-
:disabled="loading"
|
|
912
|
-
>
|
|
913
|
-
<span
|
|
914
|
-
v-if="loading"
|
|
915
|
-
class="spinner-border spinner-border-sm me-1"
|
|
916
|
-
></span>
|
|
917
|
-
Run Query
|
|
918
|
-
</button>
|
|
919
|
-
</div>
|
|
920
|
-
|
|
921
|
-
<!-- Error display -->
|
|
922
|
-
<div
|
|
923
|
-
v-if="queryError"
|
|
924
|
-
class="alert alert-danger p-1 small mb-0"
|
|
925
|
-
>
|
|
926
|
-
{{ queryError }}
|
|
927
|
-
</div>
|
|
928
|
-
</div>
|
|
929
|
-
</div>
|
|
930
|
-
</div>
|
|
931
|
-
|
|
932
|
-
<!-- <div class="accordion-item">
|
|
933
|
-
<h2 class="accordion-header">
|
|
934
|
-
<button
|
|
935
|
-
class="accordion-button collapsed"
|
|
936
|
-
type="button"
|
|
937
|
-
data-bs-toggle="collapse"
|
|
938
|
-
data-bs-target="#flush-collapseThree"
|
|
939
|
-
aria-expanded="false"
|
|
940
|
-
aria-controls="flush-collapseThree"
|
|
941
|
-
>
|
|
942
|
-
Custom Scripts
|
|
943
|
-
</button>
|
|
944
|
-
</h2>
|
|
945
|
-
<div
|
|
946
|
-
id="flush-collapseThree"
|
|
947
|
-
class="accordion-collapse collapse"
|
|
948
|
-
data-bs-parent="#accordionFlushExample"
|
|
949
|
-
>
|
|
950
|
-
<div class="accordion-body">
|
|
951
|
-
|
|
952
|
-
</div>
|
|
953
|
-
</div>
|
|
954
|
-
</div> -->
|
|
955
|
-
|
|
956
|
-
<div class="accordion-item">
|
|
957
|
-
<h2 class="accordion-header">
|
|
958
|
-
<button
|
|
959
|
-
class="accordion-button collapsed"
|
|
960
|
-
type="button"
|
|
961
|
-
data-bs-toggle="collapse"
|
|
962
|
-
data-bs-target="#flush-collapseTags"
|
|
963
|
-
>
|
|
964
|
-
Tags
|
|
965
|
-
</button>
|
|
966
|
-
</h2>
|
|
967
|
-
<div
|
|
968
|
-
id="flush-collapseTags"
|
|
969
|
-
class="accordion-collapse collapse"
|
|
970
|
-
data-bs-parent="#accordionFlushExample"
|
|
971
|
-
>
|
|
972
|
-
<div class="accordion-body p-0">
|
|
973
|
-
<div
|
|
974
|
-
v-if="tags.length"
|
|
975
|
-
class="list-group list-group-flush max-h-200px overflow-auto"
|
|
976
|
-
>
|
|
977
|
-
<a
|
|
978
|
-
v-for="tag in tags"
|
|
979
|
-
:key="tag.criteria['meta.tags']"
|
|
980
|
-
class="list-group-item list-group-item-action p-2 small"
|
|
981
|
-
@click="applyFilter(tag)"
|
|
982
|
-
style="cursor: pointer"
|
|
983
|
-
>
|
|
984
|
-
{{ tag.title }}
|
|
985
|
-
</a>
|
|
986
|
-
</div>
|
|
987
|
-
<div v-else class="text-muted small p-2">Loading tags...</div>
|
|
988
|
-
<div class="border-top pt-1">
|
|
989
|
-
<button
|
|
990
|
-
class="btn btn-sm btn-outline-secondary m-2"
|
|
991
|
-
@click="refreshTags"
|
|
992
|
-
>
|
|
993
|
-
Refresh Tags
|
|
994
|
-
</button>
|
|
995
|
-
</div>
|
|
996
|
-
</div>
|
|
997
|
-
</div>
|
|
998
|
-
</div>
|
|
999
|
-
</div>
|
|
1000
|
-
<!-- <button
|
|
1001
|
-
class="btn btn-primary btn-lg px-4 py-2 shadow"
|
|
1002
|
-
@click="loadData"
|
|
1003
|
-
>
|
|
1004
|
-
<span
|
|
1005
|
-
v-if="loading"
|
|
1006
|
-
class="spinner-border spinner-border-sm me-2"
|
|
1007
|
-
></span>
|
|
1008
|
-
{{ loading ? 'Loading...' : 'Search' }}
|
|
1009
|
-
</button> -->
|
|
1010
|
-
</div>
|
|
1011
|
-
</div>
|
|
1012
|
-
|
|
1013
|
-
<!-- PANE 2: Results (65%) -->
|
|
1014
|
-
<div id="pane2" class="flex-grow-1 d-flex flex-column border-end">
|
|
1015
|
-
<nav class="navbar bg-body-tertiary">
|
|
1016
|
-
<div class="container-fluid">
|
|
1017
|
-
<a class="navbar-brand"> Results</a>
|
|
1018
|
-
<div class="d-flex">
|
|
1019
|
-
<small class="badge bg-secondary"
|
|
1020
|
-
>{{ recordCount }} records</small
|
|
1021
|
-
>
|
|
1022
|
-
</div>
|
|
1023
|
-
</div>
|
|
1024
|
-
</nav>
|
|
1025
|
-
<div ref="tableRef" class="flex-grow-1 tabulator overflow-hidden"></div>
|
|
1026
|
-
</div>
|
|
1027
|
-
|
|
1028
|
-
<!-- PANE 3: Editor (25%) -->
|
|
1029
|
-
<div id="pane3" class="d-flex flex-column" style="min-width: 280px">
|
|
1030
|
-
<nav class="navbar bg-body-tertiary">
|
|
1031
|
-
<div class="container-fluid">
|
|
1032
|
-
<a class="navbar-brand"> Doc</a>
|
|
1033
|
-
<div class="d-flex">
|
|
1034
|
-
<input
|
|
1035
|
-
class="form-control me-2 form-control-sm"
|
|
1036
|
-
type="search"
|
|
1037
|
-
placeholder="Search"
|
|
1038
|
-
aria-label="Search"
|
|
1039
|
-
/>
|
|
1040
|
-
<button class="btn btn-sm">
|
|
1041
|
-
<svg
|
|
1042
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
1043
|
-
width="15"
|
|
1044
|
-
height="15"
|
|
1045
|
-
fill="currentColor"
|
|
1046
|
-
class="bi bi-search"
|
|
1047
|
-
viewBox="0 0 16 16"
|
|
1048
|
-
>
|
|
1049
|
-
<path
|
|
1050
|
-
d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"
|
|
1051
|
-
/>
|
|
1052
|
-
</svg>
|
|
1053
|
-
</button>
|
|
1054
|
-
</div>
|
|
1055
|
-
</div>
|
|
1056
|
-
</nav>
|
|
1057
|
-
<!-- <div class="p-3 border-bottom bg-dark-subtle">
|
|
1058
|
-
<h6 class="mb-0 fw-bold text-white small">Editor</h6>
|
|
1059
|
-
</div> -->
|
|
1060
|
-
|
|
1061
|
-
<!-- <div
|
|
1062
|
-
class="flex-grow-1 p-4 d-flex align-items-center justify-content-center"
|
|
1063
|
-
>
|
|
1064
|
-
<div
|
|
1065
|
-
class="card border-0 shadow-sm w-100 h-100 d-flex align-items-center justify-content-center"
|
|
1066
|
-
>
|
|
1067
|
-
|
|
1068
|
-
<div class="text-center text-muted px-3">
|
|
1069
|
-
<div class="h4 mb-2"><svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="currentColor" class="bi bi-file-text" viewBox="0 0 16 16">
|
|
1070
|
-
<path d="M5 4a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1zm-.5 2.5A.5.5 0 0 1 5 6h6a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5M5 8a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1zm0 2a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1z"/>
|
|
1071
|
-
<path d="M2 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2zm10-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1"/>
|
|
1072
|
-
</svg></div>
|
|
1073
|
-
<div class="fw-medium mb-1">Select a doc</div>
|
|
1074
|
-
<small>to start editing</small>
|
|
1075
|
-
</div>
|
|
1076
|
-
</div>
|
|
1077
|
-
</div> -->
|
|
1078
|
-
</div>
|
|
1079
|
-
</div>
|
|
1080
|
-
<script
|
|
1081
|
-
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
|
|
1082
|
-
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
|
|
1083
|
-
crossorigin="anonymous"
|
|
1084
|
-
></script>
|
|
1085
|
-
</body>
|
|
1086
|
-
</html>
|