adminforth 1.0.29 → 1.0.31
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/dist/index.js +88 -25
- package/dist/modules/codeInjector.js +37 -15
- package/dist/spa/spa/package-lock.json +108 -1
- package/dist/spa/spa/package.json +4 -1
- package/dist/spa/spa/src/App.vue +6 -2
- package/dist/spa/spa/src/components/AcceptModal.vue +1 -1
- package/dist/spa/spa/src/components/CustomRangePicker.vue +143 -0
- package/dist/spa/spa/src/components/Dropdown.vue +1 -1
- package/dist/spa/spa/src/components/Filters.vue +54 -21
- package/dist/spa/spa/src/components/ResourceForm.vue +38 -4
- package/dist/spa/spa/src/router/index.ts +3 -1
- package/dist/spa/spa/src/stores/core.ts +1 -14
- package/dist/spa/spa/src/views/ListView.vue +4 -1
- package/dist/spa/spa/src/views/LoginView.vue +1 -1
- package/index.ts +65 -0
- package/modules/codeInjector.ts +45 -18
- package/package.json +3 -1
- package/spa/package-lock.json +108 -1
- package/spa/package.json +4 -1
- package/spa/src/App.vue +6 -2
- package/spa/src/components/AcceptModal.vue +1 -1
- package/spa/src/components/CustomRangePicker.vue +143 -0
- package/spa/src/components/Dropdown.vue +1 -1
- package/spa/src/components/Filters.vue +54 -21
- package/spa/src/components/ResourceForm.vue +38 -4
- package/spa/src/router/index.ts +3 -1
- package/spa/src/stores/core.ts +1 -14
- package/spa/src/views/ListView.vue +4 -1
- package/spa/src/views/LoginView.vue +1 -1
package/dist/index.js
CHANGED
|
@@ -455,10 +455,73 @@ class AdminForth {
|
|
|
455
455
|
return Object.assign(Object.assign({}, data), { options: resource === null || resource === void 0 ? void 0 : resource.options });
|
|
456
456
|
}),
|
|
457
457
|
});
|
|
458
|
+
server.endpoint({
|
|
459
|
+
method: 'POST',
|
|
460
|
+
path: '/get_resource_foreign_data',
|
|
461
|
+
handler: (_h) => __awaiter(this, [_h], void 0, function* ({ body, adminUser }) {
|
|
462
|
+
var _j, _k, _l, _m;
|
|
463
|
+
const { resourceId, column } = body;
|
|
464
|
+
if (!this.statuses.dbDiscover) {
|
|
465
|
+
return { error: 'Database discovery not started' };
|
|
466
|
+
}
|
|
467
|
+
if (this.statuses.dbDiscover !== 'done') {
|
|
468
|
+
return { error: 'Database discovery is still in progress, please try later' };
|
|
469
|
+
}
|
|
470
|
+
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
471
|
+
if (!resource) {
|
|
472
|
+
return { error: `Resource ${resourceId} not found` };
|
|
473
|
+
}
|
|
474
|
+
const columnConfig = resource.columns.find((col) => col.name == column);
|
|
475
|
+
if (!columnConfig.foreignResource) {
|
|
476
|
+
return { error: `Column ${column} in resource ${resourceId} is not a foreign key` };
|
|
477
|
+
}
|
|
478
|
+
const targetResourceId = columnConfig.foreignResource.resourceId;
|
|
479
|
+
const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
|
|
480
|
+
if ((_j = columnConfig.foreignResource.hooks) === null || _j === void 0 ? void 0 : _j.beforeDatasourceRequest) {
|
|
481
|
+
const resp = yield ((_k = column.foreignResource.hooks) === null || _k === void 0 ? void 0 : _k.beforeDatasourceRequest({ query: body, adminUser }));
|
|
482
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
483
|
+
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
484
|
+
}
|
|
485
|
+
if (resp.error) {
|
|
486
|
+
return { error: resp.error };
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const { limit, offset, filters, sort } = body;
|
|
490
|
+
const dbDataItems = yield this.connectors[targetResource.dataSource].getData({
|
|
491
|
+
resource: targetResource,
|
|
492
|
+
limit,
|
|
493
|
+
offset,
|
|
494
|
+
filters: filters || [],
|
|
495
|
+
sort: sort || [],
|
|
496
|
+
});
|
|
497
|
+
const items = dbDataItems.data.map((item) => {
|
|
498
|
+
const pk = item[targetResource.columns.find((col) => col.primaryKey).name];
|
|
499
|
+
const labler = targetResource.itemLabel || ((record) => `${targetResource.label} ${pk}`);
|
|
500
|
+
return {
|
|
501
|
+
value: pk,
|
|
502
|
+
label: labler(item),
|
|
503
|
+
_item: item, // user might need it in hook to form new label
|
|
504
|
+
};
|
|
505
|
+
});
|
|
506
|
+
const response = {
|
|
507
|
+
items
|
|
508
|
+
};
|
|
509
|
+
if ((_l = columnConfig.foreignResource.hooks) === null || _l === void 0 ? void 0 : _l.afterDatasourceRequest) {
|
|
510
|
+
const resp = yield ((_m = column.foreignResource.hooks) === null || _m === void 0 ? void 0 : _m.afterDatasourceRequest({ response, adminUser }));
|
|
511
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
512
|
+
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
513
|
+
}
|
|
514
|
+
if (resp.error) {
|
|
515
|
+
return { error: resp.error };
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return response;
|
|
519
|
+
}),
|
|
520
|
+
});
|
|
458
521
|
server.endpoint({
|
|
459
522
|
method: 'POST',
|
|
460
523
|
path: '/get_min_max_for_columns',
|
|
461
|
-
handler: (
|
|
524
|
+
handler: (_o) => __awaiter(this, [_o], void 0, function* ({ body }) {
|
|
462
525
|
const { resourceId } = body;
|
|
463
526
|
if (!this.statuses.dbDiscover) {
|
|
464
527
|
return { error: 'Database discovery not started' };
|
|
@@ -487,8 +550,8 @@ class AdminForth {
|
|
|
487
550
|
server.endpoint({
|
|
488
551
|
method: 'POST',
|
|
489
552
|
path: '/get_record',
|
|
490
|
-
handler: (
|
|
491
|
-
var
|
|
553
|
+
handler: (_p) => __awaiter(this, [_p], void 0, function* ({ body, adminUser }) {
|
|
554
|
+
var _q, _r;
|
|
492
555
|
const { resourceId, primaryKey } = body;
|
|
493
556
|
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
494
557
|
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
|
|
@@ -498,8 +561,8 @@ class AdminForth {
|
|
|
498
561
|
return { error: `Record with ${primaryKeyColumn.name} ${primaryKey} not found` };
|
|
499
562
|
}
|
|
500
563
|
// execute hook if needed
|
|
501
|
-
if ((
|
|
502
|
-
const resp = yield ((
|
|
564
|
+
if ((_q = resource.hooks) === null || _q === void 0 ? void 0 : _q.show) {
|
|
565
|
+
const resp = yield ((_r = resource.hooks) === null || _r === void 0 ? void 0 : _r.show({ resource, record, adminUser }));
|
|
503
566
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
504
567
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
505
568
|
}
|
|
@@ -516,8 +579,8 @@ class AdminForth {
|
|
|
516
579
|
noAuth: true, // TODO
|
|
517
580
|
method: 'POST',
|
|
518
581
|
path: '/create_record',
|
|
519
|
-
handler: (
|
|
520
|
-
var
|
|
582
|
+
handler: (_s) => __awaiter(this, [_s], void 0, function* ({ body, adminUser }) {
|
|
583
|
+
var _t, _u, _v, _w, _x, _y, _z, _0, _1;
|
|
521
584
|
console.log('create_record', body, this.config.resources);
|
|
522
585
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
523
586
|
if (!resource) {
|
|
@@ -525,8 +588,8 @@ class AdminForth {
|
|
|
525
588
|
}
|
|
526
589
|
const record = body['record'];
|
|
527
590
|
// execute hook if needed
|
|
528
|
-
if ((
|
|
529
|
-
const resp = yield ((
|
|
591
|
+
if ((_u = (_t = resource.hooks) === null || _t === void 0 ? void 0 : _t.create) === null || _u === void 0 ? void 0 : _u.beforeSave) {
|
|
592
|
+
const resp = yield ((_w = (_v = resource.hooks) === null || _v === void 0 ? void 0 : _v.create) === null || _w === void 0 ? void 0 : _w.beforeSave({ resource, record, adminUser }));
|
|
530
593
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
531
594
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
532
595
|
}
|
|
@@ -542,7 +605,7 @@ class AdminForth {
|
|
|
542
605
|
});
|
|
543
606
|
}
|
|
544
607
|
}
|
|
545
|
-
if (((
|
|
608
|
+
if (((_x = column.required) === null || _x === void 0 ? void 0 : _x.create) && body['record'][column.name] === undefined) {
|
|
546
609
|
return { error: `Column '${column.name}' is required` };
|
|
547
610
|
}
|
|
548
611
|
if (column.isUnique) {
|
|
@@ -567,8 +630,8 @@ class AdminForth {
|
|
|
567
630
|
const connector = this.connectors[resource.dataSource];
|
|
568
631
|
yield connector.createRecord({ resource, record });
|
|
569
632
|
// execute hook if needed
|
|
570
|
-
if ((
|
|
571
|
-
const resp = yield ((
|
|
633
|
+
if ((_z = (_y = resource.hooks) === null || _y === void 0 ? void 0 : _y.create) === null || _z === void 0 ? void 0 : _z.afterSave) {
|
|
634
|
+
const resp = yield ((_1 = (_0 = resource.hooks) === null || _0 === void 0 ? void 0 : _0.create) === null || _1 === void 0 ? void 0 : _1.afterSave({ resource, record, adminUser }));
|
|
572
635
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
573
636
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
574
637
|
}
|
|
@@ -585,8 +648,8 @@ class AdminForth {
|
|
|
585
648
|
noAuth: true, // TODO
|
|
586
649
|
method: 'POST',
|
|
587
650
|
path: '/update_record',
|
|
588
|
-
handler: (
|
|
589
|
-
var
|
|
651
|
+
handler: (_2) => __awaiter(this, [_2], void 0, function* ({ body, adminUser }) {
|
|
652
|
+
var _3, _4, _5, _6, _7, _8, _9, _10;
|
|
590
653
|
console.log('update_record', body);
|
|
591
654
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
592
655
|
if (!resource) {
|
|
@@ -601,8 +664,8 @@ class AdminForth {
|
|
|
601
664
|
}
|
|
602
665
|
const record = body['record'];
|
|
603
666
|
// execute hook if needed
|
|
604
|
-
if ((
|
|
605
|
-
const resp = yield ((
|
|
667
|
+
if ((_4 = (_3 = resource.hooks) === null || _3 === void 0 ? void 0 : _3.edit) === null || _4 === void 0 ? void 0 : _4.beforeSave) {
|
|
668
|
+
const resp = yield ((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.edit) === null || _6 === void 0 ? void 0 : _6.beforeSave({ resource, record, adminUser }));
|
|
606
669
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
607
670
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
608
671
|
}
|
|
@@ -620,8 +683,8 @@ class AdminForth {
|
|
|
620
683
|
yield connector.updateRecord({ resource, recordId, record, newValues });
|
|
621
684
|
}
|
|
622
685
|
// execute hook if needed
|
|
623
|
-
if ((
|
|
624
|
-
const resp = yield ((
|
|
686
|
+
if ((_8 = (_7 = resource.hooks) === null || _7 === void 0 ? void 0 : _7.edit) === null || _8 === void 0 ? void 0 : _8.afterSave) {
|
|
687
|
+
const resp = yield ((_10 = (_9 = resource.hooks) === null || _9 === void 0 ? void 0 : _9.edit) === null || _10 === void 0 ? void 0 : _10.afterSave({ resource, record, adminUser }));
|
|
625
688
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
626
689
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
627
690
|
}
|
|
@@ -638,16 +701,16 @@ class AdminForth {
|
|
|
638
701
|
noAuth: true, // TODO
|
|
639
702
|
method: 'POST',
|
|
640
703
|
path: '/delete_record',
|
|
641
|
-
handler: (
|
|
642
|
-
var
|
|
704
|
+
handler: (_11) => __awaiter(this, [_11], void 0, function* ({ body, adminUser }) {
|
|
705
|
+
var _12, _13, _14, _15, _16, _17, _18, _19;
|
|
643
706
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
644
707
|
const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
|
|
645
708
|
if (!resource) {
|
|
646
709
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
647
710
|
}
|
|
648
711
|
// execute hook if needed
|
|
649
|
-
if ((
|
|
650
|
-
const resp = yield ((
|
|
712
|
+
if ((_13 = (_12 = resource.hooks) === null || _12 === void 0 ? void 0 : _12.delete) === null || _13 === void 0 ? void 0 : _13.beforeSave) {
|
|
713
|
+
const resp = yield ((_15 = (_14 = resource.hooks) === null || _14 === void 0 ? void 0 : _14.delete) === null || _15 === void 0 ? void 0 : _15.beforeSave({ resource, record, adminUser }));
|
|
651
714
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
652
715
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
653
716
|
}
|
|
@@ -658,8 +721,8 @@ class AdminForth {
|
|
|
658
721
|
const connector = this.connectors[resource.dataSource];
|
|
659
722
|
yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
|
|
660
723
|
// execute hook if needed
|
|
661
|
-
if ((
|
|
662
|
-
const resp = yield ((
|
|
724
|
+
if ((_17 = (_16 = resource.hooks) === null || _16 === void 0 ? void 0 : _16.delete) === null || _17 === void 0 ? void 0 : _17.afterSave) {
|
|
725
|
+
const resp = yield ((_19 = (_18 = resource.hooks) === null || _18 === void 0 ? void 0 : _18.delete) === null || _19 === void 0 ? void 0 : _19.afterSave({ resource, record, adminUser }));
|
|
663
726
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
664
727
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
665
728
|
}
|
|
@@ -676,7 +739,7 @@ class AdminForth {
|
|
|
676
739
|
noAuth: true, // TODO
|
|
677
740
|
method: 'POST',
|
|
678
741
|
path: '/start_bulk_action',
|
|
679
|
-
handler: (
|
|
742
|
+
handler: (_20) => __awaiter(this, [_20], void 0, function* ({ body }) {
|
|
680
743
|
const { resourceId, actionId, recordIds } = body;
|
|
681
744
|
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
682
745
|
if (!resource) {
|
|
@@ -63,9 +63,8 @@ class CodeInjector {
|
|
|
63
63
|
rmTmpDir() {
|
|
64
64
|
return __awaiter(this, void 0, void 0, function* () {
|
|
65
65
|
// remove spa_tmp folder if it is exists
|
|
66
|
-
const spaTmpPath = CodeInjector.SPA_TMP_PATH;
|
|
67
66
|
try {
|
|
68
|
-
yield fs.promises.rm(
|
|
67
|
+
yield fs.promises.rm(CodeInjector.SPA_TMP_PATH, { recursive: true });
|
|
69
68
|
}
|
|
70
69
|
catch (e) {
|
|
71
70
|
// ignore
|
|
@@ -74,7 +73,7 @@ class CodeInjector {
|
|
|
74
73
|
}
|
|
75
74
|
prepareSources(_a) {
|
|
76
75
|
return __awaiter(this, arguments, void 0, function* ({ filesUpdated, verbose = false }) {
|
|
77
|
-
var _b, _c, _d;
|
|
76
|
+
var _b, _c, _d, _e, _f, _g;
|
|
78
77
|
const spaTmpPath = CodeInjector.SPA_TMP_PATH;
|
|
79
78
|
// check SPA_TMP_PATH exists and create if not
|
|
80
79
|
try {
|
|
@@ -186,23 +185,35 @@ class CodeInjector {
|
|
|
186
185
|
}
|
|
187
186
|
yield fs.promises.writeFile(appVuePath, appVueContent);
|
|
188
187
|
/* generate custom rotes */
|
|
188
|
+
const homepageMenuItem = this.adminforth.config.menu.find((mi) => mi.homepage);
|
|
189
|
+
let childrenHomePageMenuItem = this.adminforth.config.menu.find((mi) => { var _a; return mi.children && ((_a = mi.children) === null || _a === void 0 ? void 0 : _a.find((mi) => mi.homepage)); });
|
|
190
|
+
let childrenHomepage = (_e = childrenHomePageMenuItem === null || childrenHomePageMenuItem === void 0 ? void 0 : childrenHomePageMenuItem.children) === null || _e === void 0 ? void 0 : _e.find((mi) => mi.homepage);
|
|
191
|
+
let homePagePath = (homepageMenuItem === null || homepageMenuItem === void 0 ? void 0 : homepageMenuItem.path) || `resource/${childrenHomepage === null || childrenHomepage === void 0 ? void 0 : childrenHomepage.resourceId}`;
|
|
192
|
+
if (!homePagePath) {
|
|
193
|
+
homePagePath = ((_f = this.adminforth.config.menu.filter((mi) => mi.path)[0]) === null || _f === void 0 ? void 0 : _f.path) || `resource/${(_g = this.adminforth.config.menu.filter((mi) => mi.children)[0]) === null || _g === void 0 ? void 0 : _g.resourceId}`;
|
|
194
|
+
}
|
|
195
|
+
routes += `{
|
|
196
|
+
path: '/',
|
|
197
|
+
name: 'home',
|
|
198
|
+
//redirect to login
|
|
199
|
+
redirect: '/${homePagePath}'
|
|
200
|
+
},\n`;
|
|
189
201
|
const routerVuePath = path.join(spaTmpPath, 'src', 'router', 'index.ts');
|
|
190
202
|
let routerVueContent = yield fs.promises.readFile(routerVuePath, 'utf-8');
|
|
191
203
|
routerVueContent = routerVueContent.replace('/* IMPORTANT:ADMINFORTH ROUTES */', routes);
|
|
192
204
|
yield fs.promises.writeFile(routerVuePath, routerVueContent);
|
|
193
205
|
/* hash checking */
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
const
|
|
206
|
+
const spaPackageLockPath = path.join(spaTmpPath, 'package-lock.json');
|
|
207
|
+
const spaPackageLock = JSON.parse(yield fs.promises.readFile(spaPackageLockPath, 'utf-8'));
|
|
208
|
+
const spaLockHash = hashify(spaPackageLock);
|
|
197
209
|
/* customPackageLock */
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
const customLockHash = hashify(customLock);
|
|
210
|
+
const usersPackagePath = path.join('./package.json');
|
|
211
|
+
const usersPackage = JSON.parse(yield fs.promises.readFile(usersPackagePath, 'utf-8'));
|
|
212
|
+
const usersLockPath = path.join('./package-lock.json');
|
|
213
|
+
const usersLock = JSON.parse(yield fs.promises.readFile(usersLockPath, 'utf-8'));
|
|
214
|
+
const usersLockHash = hashify(usersLock);
|
|
204
215
|
const packagesNamesHash = hashify(packageNames);
|
|
205
|
-
const fullHash =
|
|
216
|
+
const fullHash = `${spaLockHash}::${packagesNamesHash}::${usersLockHash}`;
|
|
206
217
|
const hashPath = path.join(spaTmpPath, 'node_modules', '.adminforth_hash');
|
|
207
218
|
try {
|
|
208
219
|
const existingHash = yield fs.promises.readFile(hashPath, 'utf-8');
|
|
@@ -210,14 +221,25 @@ class CodeInjector {
|
|
|
210
221
|
console.log('Hashes match, skipping npm ci/install');
|
|
211
222
|
return;
|
|
212
223
|
}
|
|
224
|
+
else {
|
|
225
|
+
if (verbose) {
|
|
226
|
+
console.log(`Hashes do not match: existing ${existingHash} new ${fullHash}, proceeding with npm ci/install`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
213
229
|
}
|
|
214
230
|
catch (e) {
|
|
215
231
|
// ignore
|
|
232
|
+
if (verbose) {
|
|
233
|
+
console.log('Hash file does not exist, proceeding with npm ci/install');
|
|
234
|
+
}
|
|
216
235
|
}
|
|
217
236
|
yield this.runNpmShell({ command: 'ci', verbose, cwd: spaTmpPath });
|
|
218
237
|
// get packages with version from customPackage
|
|
219
|
-
const
|
|
220
|
-
|
|
238
|
+
const IGNORE_PACKAGES = ['tsx', 'typescript', 'express', 'nodemon'];
|
|
239
|
+
const customPackgeNames = [...Object.keys(usersPackage.dependencies), ...Object.keys(usersPackage.devDependencies || [])]
|
|
240
|
+
.filter((packageName) => !IGNORE_PACKAGES.includes(packageName))
|
|
241
|
+
.reduce((acc, packageName) => {
|
|
242
|
+
const version = usersLock.packages[`node_modules/${packageName}`].version;
|
|
221
243
|
acc.push(`${packageName}@${version}`);
|
|
222
244
|
return acc;
|
|
223
245
|
}, []);
|
|
@@ -9,12 +9,15 @@
|
|
|
9
9
|
"version": "0.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@iconify-prerendered/vue-flowbite": "^0.23.1714023977",
|
|
12
|
+
"@vueuse/core": "^10.10.0",
|
|
12
13
|
"dayjs": "^1.11.11",
|
|
14
|
+
"debounce": "^2.1.0",
|
|
13
15
|
"flowbite": "^2.3.0",
|
|
14
16
|
"flowbite-datepicker": "^1.2.6",
|
|
15
17
|
"pinia": "^2.1.7",
|
|
16
18
|
"vue": "^3.4.21",
|
|
17
|
-
"vue-router": "^4.3.0"
|
|
19
|
+
"vue-router": "^4.3.0",
|
|
20
|
+
"vue-slider-component": "^4.1.0-beta.7"
|
|
18
21
|
},
|
|
19
22
|
"devDependencies": {
|
|
20
23
|
"@rushstack/eslint-patch": "^1.8.0",
|
|
@@ -950,6 +953,11 @@
|
|
|
950
953
|
"undici-types": "~5.26.4"
|
|
951
954
|
}
|
|
952
955
|
},
|
|
956
|
+
"node_modules/@types/web-bluetooth": {
|
|
957
|
+
"version": "0.0.20",
|
|
958
|
+
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
|
959
|
+
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
|
|
960
|
+
},
|
|
953
961
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
|
954
962
|
"version": "7.9.0",
|
|
955
963
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz",
|
|
@@ -1330,6 +1338,89 @@
|
|
|
1330
1338
|
"integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
|
|
1331
1339
|
"dev": true
|
|
1332
1340
|
},
|
|
1341
|
+
"node_modules/@vueuse/core": {
|
|
1342
|
+
"version": "10.10.0",
|
|
1343
|
+
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.10.0.tgz",
|
|
1344
|
+
"integrity": "sha512-vexJ/YXYs2S42B783rI95lMt3GzEwkxzC8Hb0Ndpd8rD+p+Lk/Za4bd797Ym7yq4jXqdSyj3JLChunF/vyYjUw==",
|
|
1345
|
+
"dependencies": {
|
|
1346
|
+
"@types/web-bluetooth": "^0.0.20",
|
|
1347
|
+
"@vueuse/metadata": "10.10.0",
|
|
1348
|
+
"@vueuse/shared": "10.10.0",
|
|
1349
|
+
"vue-demi": ">=0.14.7"
|
|
1350
|
+
},
|
|
1351
|
+
"funding": {
|
|
1352
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1353
|
+
}
|
|
1354
|
+
},
|
|
1355
|
+
"node_modules/@vueuse/core/node_modules/vue-demi": {
|
|
1356
|
+
"version": "0.14.8",
|
|
1357
|
+
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
|
|
1358
|
+
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
|
|
1359
|
+
"hasInstallScript": true,
|
|
1360
|
+
"bin": {
|
|
1361
|
+
"vue-demi-fix": "bin/vue-demi-fix.js",
|
|
1362
|
+
"vue-demi-switch": "bin/vue-demi-switch.js"
|
|
1363
|
+
},
|
|
1364
|
+
"engines": {
|
|
1365
|
+
"node": ">=12"
|
|
1366
|
+
},
|
|
1367
|
+
"funding": {
|
|
1368
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1369
|
+
},
|
|
1370
|
+
"peerDependencies": {
|
|
1371
|
+
"@vue/composition-api": "^1.0.0-rc.1",
|
|
1372
|
+
"vue": "^3.0.0-0 || ^2.6.0"
|
|
1373
|
+
},
|
|
1374
|
+
"peerDependenciesMeta": {
|
|
1375
|
+
"@vue/composition-api": {
|
|
1376
|
+
"optional": true
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
},
|
|
1380
|
+
"node_modules/@vueuse/metadata": {
|
|
1381
|
+
"version": "10.10.0",
|
|
1382
|
+
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.10.0.tgz",
|
|
1383
|
+
"integrity": "sha512-UNAo2sTCAW5ge6OErPEHb5z7NEAg3XcO9Cj7OK45aZXfLLH1QkexDcZD77HBi5zvEiLOm1An+p/4b5K3Worpug==",
|
|
1384
|
+
"funding": {
|
|
1385
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1386
|
+
}
|
|
1387
|
+
},
|
|
1388
|
+
"node_modules/@vueuse/shared": {
|
|
1389
|
+
"version": "10.10.0",
|
|
1390
|
+
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.10.0.tgz",
|
|
1391
|
+
"integrity": "sha512-2aW33Ac0Uk0U+9yo3Ypg9s5KcR42cuehRWl7vnUHadQyFvCktseyxxEPBi1Eiq4D2yBGACOnqLZpx1eMc7g5Og==",
|
|
1392
|
+
"dependencies": {
|
|
1393
|
+
"vue-demi": ">=0.14.7"
|
|
1394
|
+
},
|
|
1395
|
+
"funding": {
|
|
1396
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1397
|
+
}
|
|
1398
|
+
},
|
|
1399
|
+
"node_modules/@vueuse/shared/node_modules/vue-demi": {
|
|
1400
|
+
"version": "0.14.8",
|
|
1401
|
+
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
|
|
1402
|
+
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
|
|
1403
|
+
"hasInstallScript": true,
|
|
1404
|
+
"bin": {
|
|
1405
|
+
"vue-demi-fix": "bin/vue-demi-fix.js",
|
|
1406
|
+
"vue-demi-switch": "bin/vue-demi-switch.js"
|
|
1407
|
+
},
|
|
1408
|
+
"engines": {
|
|
1409
|
+
"node": ">=12"
|
|
1410
|
+
},
|
|
1411
|
+
"funding": {
|
|
1412
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1413
|
+
},
|
|
1414
|
+
"peerDependencies": {
|
|
1415
|
+
"@vue/composition-api": "^1.0.0-rc.1",
|
|
1416
|
+
"vue": "^3.0.0-0 || ^2.6.0"
|
|
1417
|
+
},
|
|
1418
|
+
"peerDependenciesMeta": {
|
|
1419
|
+
"@vue/composition-api": {
|
|
1420
|
+
"optional": true
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
},
|
|
1333
1424
|
"node_modules/acorn": {
|
|
1334
1425
|
"version": "8.11.3",
|
|
1335
1426
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
|
@@ -1716,6 +1807,17 @@
|
|
|
1716
1807
|
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
|
1717
1808
|
"dev": true
|
|
1718
1809
|
},
|
|
1810
|
+
"node_modules/debounce": {
|
|
1811
|
+
"version": "2.1.0",
|
|
1812
|
+
"resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.0.tgz",
|
|
1813
|
+
"integrity": "sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==",
|
|
1814
|
+
"engines": {
|
|
1815
|
+
"node": ">=18"
|
|
1816
|
+
},
|
|
1817
|
+
"funding": {
|
|
1818
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
1819
|
+
}
|
|
1820
|
+
},
|
|
1719
1821
|
"node_modules/debug": {
|
|
1720
1822
|
"version": "4.3.4",
|
|
1721
1823
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
|
@@ -3970,6 +4072,11 @@
|
|
|
3970
4072
|
"vue": "^3.2.0"
|
|
3971
4073
|
}
|
|
3972
4074
|
},
|
|
4075
|
+
"node_modules/vue-slider-component": {
|
|
4076
|
+
"version": "4.1.0-beta.7",
|
|
4077
|
+
"resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-4.1.0-beta.7.tgz",
|
|
4078
|
+
"integrity": "sha512-Qb7K920ZG7PoQswoF6Ias+i3W2rd3k4fpk04JUl82kEUcN86Yg6et7bVSKWt/7VpQe8a5IT3BqCKSCOZ7AJgCA=="
|
|
4079
|
+
},
|
|
3973
4080
|
"node_modules/vue-template-compiler": {
|
|
3974
4081
|
"version": "2.7.16",
|
|
3975
4082
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
|
|
@@ -13,12 +13,15 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@iconify-prerendered/vue-flowbite": "^0.23.1714023977",
|
|
16
|
+
"@vueuse/core": "^10.10.0",
|
|
16
17
|
"dayjs": "^1.11.11",
|
|
18
|
+
"debounce": "^2.1.0",
|
|
17
19
|
"flowbite": "^2.3.0",
|
|
18
20
|
"flowbite-datepicker": "^1.2.6",
|
|
19
21
|
"pinia": "^2.1.7",
|
|
20
22
|
"vue": "^3.4.21",
|
|
21
|
-
"vue-router": "^4.3.0"
|
|
23
|
+
"vue-router": "^4.3.0",
|
|
24
|
+
"vue-slider-component": "^4.1.0-beta.7"
|
|
22
25
|
},
|
|
23
26
|
"devDependencies": {
|
|
24
27
|
"@rushstack/eslint-patch": "^1.8.0",
|
package/dist/spa/spa/src/App.vue
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
</button>
|
|
34
34
|
</div>
|
|
35
|
-
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600 dark:shadow-
|
|
35
|
+
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:shadow-black dark:bg-gray-700 dark:divide-gray-600 dark:shadow-black" id="dropdown-user">
|
|
36
36
|
<div class="px-4 py-3" role="none">
|
|
37
37
|
<p class="text-sm text-gray-900 dark:text-white" role="none" v-if="coreStore.userFullname">
|
|
38
38
|
{{ coreStore.userFullname }}
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
</div>
|
|
113
113
|
</div>
|
|
114
114
|
|
|
115
|
-
<div v-else>
|
|
115
|
+
<div v-else-if="routerIsReady">
|
|
116
116
|
<RouterView/>
|
|
117
117
|
</div>
|
|
118
118
|
<AcceptModal />
|
|
@@ -141,6 +141,10 @@ const routerIsReady = ref(false);
|
|
|
141
141
|
|
|
142
142
|
const loggedIn = computed(() => route.name !== 'login' && routerIsReady.value);
|
|
143
143
|
|
|
144
|
+
watch(loggedIn, (value) => {
|
|
145
|
+
console.log('🔻🔻🔻 loggedIn', value);
|
|
146
|
+
});
|
|
147
|
+
|
|
144
148
|
const theme = ref('light');
|
|
145
149
|
|
|
146
150
|
function toggleTheme() {
|
|
@@ -16,7 +16,7 @@ const modalStore = useModalStore();
|
|
|
16
16
|
<Teleport to="body">
|
|
17
17
|
<div v-if="modalStore.isOpened" class="bg-gray-900/50 dark:bg-gray-900/80 overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
18
18
|
<div class="relative p-4 w-full max-w-md max-h-full top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 " >
|
|
19
|
-
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
|
19
|
+
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700 dark:shadow-black">
|
|
20
20
|
<button type="button"@click="modalStore.togleModal" class="absolute top-3 end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" >
|
|
21
21
|
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
22
22
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-wrap gap-2">
|
|
3
|
+
<input
|
|
4
|
+
type="number" aria-describedby="helper-text-explanation"
|
|
5
|
+
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
6
|
+
placeholder="From"
|
|
7
|
+
v-model="start"
|
|
8
|
+
>
|
|
9
|
+
|
|
10
|
+
<input
|
|
11
|
+
type="number" aria-describedby="helper-text-explanation"
|
|
12
|
+
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
13
|
+
placeholder="To"
|
|
14
|
+
v-model="end"
|
|
15
|
+
>
|
|
16
|
+
|
|
17
|
+
<button
|
|
18
|
+
v-if="isChanged"
|
|
19
|
+
type="button"
|
|
20
|
+
class="flex items-center p-0.5 ml-auto px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
21
|
+
@click="clear">Clear
|
|
22
|
+
</button>
|
|
23
|
+
|
|
24
|
+
<div class="w-full">
|
|
25
|
+
<vue-slider
|
|
26
|
+
class="custom-slider"
|
|
27
|
+
:dot-size="20"
|
|
28
|
+
height="7.99px"
|
|
29
|
+
:min="minFormatted"
|
|
30
|
+
:max="maxFormatted"
|
|
31
|
+
v-model="sliderValue"
|
|
32
|
+
@update:model-value="updateFromSlider($event)"
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import VueSlider from 'vue-slider-component';
|
|
39
|
+
import 'vue-slider-component/theme/antd.css'
|
|
40
|
+
import {computed, onMounted, ref, watch} from "vue";
|
|
41
|
+
import debounce from 'debounce'
|
|
42
|
+
|
|
43
|
+
const props = defineProps({
|
|
44
|
+
valueStart: {
|
|
45
|
+
default: 0,
|
|
46
|
+
},
|
|
47
|
+
valueEnd: {
|
|
48
|
+
default: 10,
|
|
49
|
+
},
|
|
50
|
+
min: {
|
|
51
|
+
default: 0,
|
|
52
|
+
},
|
|
53
|
+
max: {
|
|
54
|
+
default: 10,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
|
|
59
|
+
|
|
60
|
+
const minFormatted = computed(() => Math.floor(props.min));
|
|
61
|
+
const maxFormatted = computed(() => Math.ceil(props.max));
|
|
62
|
+
|
|
63
|
+
const isChanged = computed(() => {
|
|
64
|
+
return start.value !== minFormatted.value || end.value !== maxFormatted.value;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const start = ref(minFormatted.value);
|
|
68
|
+
const end = ref(maxFormatted.value);
|
|
69
|
+
|
|
70
|
+
const sliderValue = ref([start.value, end.value]);
|
|
71
|
+
|
|
72
|
+
const updateFromSlider =
|
|
73
|
+
debounce((value: [number, number]) => {
|
|
74
|
+
start.value = value[0];
|
|
75
|
+
end.value = value[1];
|
|
76
|
+
}, 500);
|
|
77
|
+
|
|
78
|
+
function updateFromProps() {
|
|
79
|
+
if (props.valueStart || props.valueEnd) {
|
|
80
|
+
setFromProps(props.valueStart, props.valueEnd)
|
|
81
|
+
} else {
|
|
82
|
+
clear();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
onMounted(() => {
|
|
87
|
+
updateFromProps();
|
|
88
|
+
|
|
89
|
+
watch(() => [props.valueStart, props.valueEnd], (value) => {
|
|
90
|
+
updateFromProps();
|
|
91
|
+
});
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
watch(start, () => {
|
|
95
|
+
//console.log('⚡ emit', start.value)
|
|
96
|
+
emit('update:valueStart', start.value)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
watch(end, () => {
|
|
100
|
+
//console.log('⚡ emit', end.value)
|
|
101
|
+
emit('update:valueEnd', end.value);
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const clear = () => {
|
|
105
|
+
start.value = minFormatted.value;
|
|
106
|
+
end.value = maxFormatted.value;
|
|
107
|
+
sliderValue.value = [start.value, end.value]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function setFromProps(startValue: number, endValue: number) {
|
|
111
|
+
start.value = startValue ? startValue : minFormatted.value
|
|
112
|
+
end.value = endValue ? endValue : maxFormatted.value
|
|
113
|
+
sliderValue.value = [start.value, end.value]
|
|
114
|
+
}
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
<style lang="scss" scoped>
|
|
118
|
+
.custom-slider {
|
|
119
|
+
&:deep(.vue-slider-rail) {
|
|
120
|
+
background-color: rgb(229 231 235);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
&:deep(.vue-slider-dot-handle) {
|
|
124
|
+
background-color: #1c64f2;
|
|
125
|
+
border: none;
|
|
126
|
+
box-shadow: none;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
&:deep(.vue-slider-dot-handle:hover) {
|
|
130
|
+
background-color: #1c64f2;
|
|
131
|
+
border: none;
|
|
132
|
+
box-shadow: none;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
&:deep(.vue-slider-process) {
|
|
136
|
+
background-color: rgb(225 239 254);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
&:deep(.vue-slider-process:hover) {
|
|
140
|
+
background-color: rgb(225 239 254);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
</style>
|