adminforth 1.0.45 → 1.0.47

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 CHANGED
@@ -384,6 +384,7 @@ class AdminForth {
384
384
  method: 'GET',
385
385
  path: '/get_base_config',
386
386
  handler: (_e) => __awaiter(this, [_e], void 0, function* ({ input, adminUser, cookies }) {
387
+ var _f;
387
388
  const cookieParsed = this.auth.verify(cookies['adminforth_jwt']);
388
389
  let username = '';
389
390
  let userFullName = '';
@@ -424,6 +425,7 @@ class AdminForth {
424
425
  deleteConfirmation: this.config.deleteConfirmation,
425
426
  auth: this.config.auth,
426
427
  usernameField: this.config.auth.usernameField,
428
+ title: (_f = this.config) === null || _f === void 0 ? void 0 : _f.title,
427
429
  },
428
430
  adminUser,
429
431
  version: ADMINFORTH_VERSION,
@@ -433,7 +435,7 @@ class AdminForth {
433
435
  server.endpoint({
434
436
  method: 'POST',
435
437
  path: '/get_resource_columns',
436
- handler: (_f) => __awaiter(this, [_f], void 0, function* ({ body }) {
438
+ handler: (_g) => __awaiter(this, [_g], void 0, function* ({ body }) {
437
439
  const { resourceId } = body;
438
440
  if (!this.statuses.dbDiscover) {
439
441
  return { error: 'Database discovery not started' };
@@ -451,8 +453,8 @@ class AdminForth {
451
453
  server.endpoint({
452
454
  method: 'POST',
453
455
  path: '/get_resource_data',
454
- handler: (_g) => __awaiter(this, [_g], void 0, function* ({ body, adminUser }) {
455
- var _h, _j, _k, _l, _m, _o, _p, _q;
456
+ handler: (_h) => __awaiter(this, [_h], void 0, function* ({ body, adminUser }) {
457
+ var _j, _k, _l, _m, _o, _p, _q, _r;
456
458
  const { resourceId, source } = body;
457
459
  if (['show', 'list'].includes(source) === false) {
458
460
  return { error: 'Invalid source, should be list or show' };
@@ -467,8 +469,8 @@ class AdminForth {
467
469
  if (!resource) {
468
470
  return { error: `Resource ${resourceId} not found` };
469
471
  }
470
- if ((_j = (_h = resource.hooks) === null || _h === void 0 ? void 0 : _h[source]) === null || _j === void 0 ? void 0 : _j.beforeDatasourceRequest) {
471
- const resp = yield ((_l = (_k = resource.hooks) === null || _k === void 0 ? void 0 : _k[source]) === null || _l === void 0 ? void 0 : _l.beforeDatasourceRequest({ resource, query: body, adminUser }));
472
+ if ((_k = (_j = resource.hooks) === null || _j === void 0 ? void 0 : _j[source]) === null || _k === void 0 ? void 0 : _k.beforeDatasourceRequest) {
473
+ const resp = yield ((_m = (_l = resource.hooks) === null || _l === void 0 ? void 0 : _l[source]) === null || _m === void 0 ? void 0 : _m.beforeDatasourceRequest({ resource, query: body, adminUser }));
472
474
  if (!resp || (!resp.ok && !resp.error)) {
473
475
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
474
476
  }
@@ -537,8 +539,8 @@ class AdminForth {
537
539
  data.data.forEach((item) => {
538
540
  item._label = resource.itemLabel(item);
539
541
  });
540
- if ((_o = (_m = resource.hooks) === null || _m === void 0 ? void 0 : _m[source]) === null || _o === void 0 ? void 0 : _o.afterDatasourceRequest) {
541
- const resp = yield ((_q = (_p = resource.hooks) === null || _p === void 0 ? void 0 : _p[source]) === null || _q === void 0 ? void 0 : _q.afterDatasourceRequest({ resource, response: data.data, adminUser }));
542
+ if ((_p = (_o = resource.hooks) === null || _o === void 0 ? void 0 : _o[source]) === null || _p === void 0 ? void 0 : _p.afterDatasourceRequest) {
543
+ const resp = yield ((_r = (_q = resource.hooks) === null || _q === void 0 ? void 0 : _q[source]) === null || _r === void 0 ? void 0 : _r.afterDatasourceRequest({ resource, response: data.data, adminUser }));
542
544
  if (!resp || (!resp.ok && !resp.error)) {
543
545
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
544
546
  }
@@ -552,8 +554,8 @@ class AdminForth {
552
554
  server.endpoint({
553
555
  method: 'POST',
554
556
  path: '/get_resource_foreign_data',
555
- handler: (_r) => __awaiter(this, [_r], void 0, function* ({ body, adminUser }) {
556
- var _s, _t, _u, _v;
557
+ handler: (_s) => __awaiter(this, [_s], void 0, function* ({ body, adminUser }) {
558
+ var _t, _u, _v, _w;
557
559
  const { resourceId, column } = body;
558
560
  if (!this.statuses.dbDiscover) {
559
561
  return { error: 'Database discovery not started' };
@@ -574,8 +576,8 @@ class AdminForth {
574
576
  }
575
577
  const targetResourceId = columnConfig.foreignResource.resourceId;
576
578
  const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
577
- if ((_s = columnConfig.foreignResource.hooks) === null || _s === void 0 ? void 0 : _s.beforeDatasourceRequest) {
578
- const resp = yield ((_t = column.foreignResource.hooks) === null || _t === void 0 ? void 0 : _t.beforeDatasourceRequest({ query: body, adminUser }));
579
+ if ((_t = columnConfig.foreignResource.hooks) === null || _t === void 0 ? void 0 : _t.beforeDatasourceRequest) {
580
+ const resp = yield ((_u = column.foreignResource.hooks) === null || _u === void 0 ? void 0 : _u.beforeDatasourceRequest({ query: body, adminUser }));
579
581
  if (!resp || (!resp.ok && !resp.error)) {
580
582
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
581
583
  }
@@ -603,8 +605,8 @@ class AdminForth {
603
605
  const response = {
604
606
  items
605
607
  };
606
- if ((_u = columnConfig.foreignResource.hooks) === null || _u === void 0 ? void 0 : _u.afterDatasourceResponse) {
607
- const resp = yield ((_v = column.foreignResource.hooks) === null || _v === void 0 ? void 0 : _v.afterDatasourceResponse({ response, adminUser }));
608
+ if ((_v = columnConfig.foreignResource.hooks) === null || _v === void 0 ? void 0 : _v.afterDatasourceResponse) {
609
+ const resp = yield ((_w = column.foreignResource.hooks) === null || _w === void 0 ? void 0 : _w.afterDatasourceResponse({ response, adminUser }));
608
610
  if (!resp || (!resp.ok && !resp.error)) {
609
611
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
610
612
  }
@@ -618,7 +620,7 @@ class AdminForth {
618
620
  server.endpoint({
619
621
  method: 'POST',
620
622
  path: '/get_min_max_for_columns',
621
- handler: (_w) => __awaiter(this, [_w], void 0, function* ({ body }) {
623
+ handler: (_x) => __awaiter(this, [_x], void 0, function* ({ body }) {
622
624
  const { resourceId } = body;
623
625
  if (!this.statuses.dbDiscover) {
624
626
  return { error: 'Database discovery not started' };
@@ -648,8 +650,8 @@ class AdminForth {
648
650
  noAuth: true, // TODO
649
651
  method: 'POST',
650
652
  path: '/create_record',
651
- handler: (_x) => __awaiter(this, [_x], void 0, function* ({ body, adminUser }) {
652
- var _y, _z, _0, _1, _2, _3, _4, _5, _6;
653
+ handler: (_y) => __awaiter(this, [_y], void 0, function* ({ body, adminUser }) {
654
+ var _z, _0, _1, _2, _3, _4, _5, _6, _7;
653
655
  console.log('create_record', body, this.config.resources);
654
656
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
655
657
  if (!resource) {
@@ -657,8 +659,8 @@ class AdminForth {
657
659
  }
658
660
  const record = body['record'];
659
661
  // execute hook if needed
660
- if ((_z = (_y = resource.hooks) === null || _y === void 0 ? void 0 : _y.create) === null || _z === void 0 ? void 0 : _z.beforeSave) {
661
- const resp = yield ((_1 = (_0 = resource.hooks) === null || _0 === void 0 ? void 0 : _0.create) === null || _1 === void 0 ? void 0 : _1.beforeSave({ resource, record, adminUser }));
662
+ if ((_0 = (_z = resource.hooks) === null || _z === void 0 ? void 0 : _z.create) === null || _0 === void 0 ? void 0 : _0.beforeSave) {
663
+ const resp = yield ((_2 = (_1 = resource.hooks) === null || _1 === void 0 ? void 0 : _1.create) === null || _2 === void 0 ? void 0 : _2.beforeSave({ resource, record, adminUser }));
662
664
  if (!resp || (!resp.ok && !resp.error)) {
663
665
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
664
666
  }
@@ -674,7 +676,7 @@ class AdminForth {
674
676
  });
675
677
  }
676
678
  }
677
- if (((_2 = column.required) === null || _2 === void 0 ? void 0 : _2.create) && body['record'][column.name] === undefined) {
679
+ if (((_3 = column.required) === null || _3 === void 0 ? void 0 : _3.create) && body['record'][column.name] === undefined) {
678
680
  return { error: `Column '${column.name}' is required` };
679
681
  }
680
682
  if (column.isUnique) {
@@ -699,8 +701,8 @@ class AdminForth {
699
701
  const connector = this.connectors[resource.dataSource];
700
702
  yield connector.createRecord({ resource, record });
701
703
  // execute hook if needed
702
- if ((_4 = (_3 = resource.hooks) === null || _3 === void 0 ? void 0 : _3.create) === null || _4 === void 0 ? void 0 : _4.afterSave) {
703
- const resp = yield ((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.create) === null || _6 === void 0 ? void 0 : _6.afterSave({ resource, record, adminUser }));
704
+ if ((_5 = (_4 = resource.hooks) === null || _4 === void 0 ? void 0 : _4.create) === null || _5 === void 0 ? void 0 : _5.afterSave) {
705
+ const resp = yield ((_7 = (_6 = resource.hooks) === null || _6 === void 0 ? void 0 : _6.create) === null || _7 === void 0 ? void 0 : _7.afterSave({ resource, record, adminUser }));
704
706
  if (!resp || (!resp.ok && !resp.error)) {
705
707
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
706
708
  }
@@ -717,8 +719,8 @@ class AdminForth {
717
719
  noAuth: true, // TODO
718
720
  method: 'POST',
719
721
  path: '/update_record',
720
- handler: (_7) => __awaiter(this, [_7], void 0, function* ({ body, adminUser }) {
721
- var _8, _9, _10, _11, _12, _13, _14, _15;
722
+ handler: (_8) => __awaiter(this, [_8], void 0, function* ({ body, adminUser }) {
723
+ var _9, _10, _11, _12, _13, _14, _15, _16;
722
724
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
723
725
  if (!resource) {
724
726
  return { error: `Resource '${body['resourceId']}' not found` };
@@ -732,8 +734,8 @@ class AdminForth {
732
734
  }
733
735
  const record = body['record'];
734
736
  // execute hook if needed
735
- if ((_9 = (_8 = resource.hooks) === null || _8 === void 0 ? void 0 : _8.edit) === null || _9 === void 0 ? void 0 : _9.beforeSave) {
736
- const resp = yield ((_11 = (_10 = resource.hooks) === null || _10 === void 0 ? void 0 : _10.edit) === null || _11 === void 0 ? void 0 : _11.beforeSave({ resource, record, adminUser }));
737
+ if ((_10 = (_9 = resource.hooks) === null || _9 === void 0 ? void 0 : _9.edit) === null || _10 === void 0 ? void 0 : _10.beforeSave) {
738
+ const resp = yield ((_12 = (_11 = resource.hooks) === null || _11 === void 0 ? void 0 : _11.edit) === null || _12 === void 0 ? void 0 : _12.beforeSave({ resource, record, adminUser }));
737
739
  if (!resp || (!resp.ok && !resp.error)) {
738
740
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
739
741
  }
@@ -751,8 +753,8 @@ class AdminForth {
751
753
  yield connector.updateRecord({ resource, recordId, record, newValues });
752
754
  }
753
755
  // execute hook if needed
754
- if ((_13 = (_12 = resource.hooks) === null || _12 === void 0 ? void 0 : _12.edit) === null || _13 === void 0 ? void 0 : _13.afterSave) {
755
- const resp = yield ((_15 = (_14 = resource.hooks) === null || _14 === void 0 ? void 0 : _14.edit) === null || _15 === void 0 ? void 0 : _15.afterSave({ resource, record, adminUser }));
756
+ if ((_14 = (_13 = resource.hooks) === null || _13 === void 0 ? void 0 : _13.edit) === null || _14 === void 0 ? void 0 : _14.afterSave) {
757
+ const resp = yield ((_16 = (_15 = resource.hooks) === null || _15 === void 0 ? void 0 : _15.edit) === null || _16 === void 0 ? void 0 : _16.afterSave({ resource, record, adminUser }));
756
758
  if (!resp || (!resp.ok && !resp.error)) {
757
759
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
758
760
  }
@@ -769,16 +771,16 @@ class AdminForth {
769
771
  noAuth: true, // TODO
770
772
  method: 'POST',
771
773
  path: '/delete_record',
772
- handler: (_16) => __awaiter(this, [_16], void 0, function* ({ body, adminUser }) {
773
- var _17, _18, _19, _20, _21, _22, _23, _24;
774
+ handler: (_17) => __awaiter(this, [_17], void 0, function* ({ body, adminUser }) {
775
+ var _18, _19, _20, _21, _22, _23, _24, _25;
774
776
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
775
777
  const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
776
778
  if (!resource) {
777
779
  return { error: `Resource '${body['resourceId']}' not found` };
778
780
  }
779
781
  // execute hook if needed
780
- if ((_18 = (_17 = resource.hooks) === null || _17 === void 0 ? void 0 : _17.delete) === null || _18 === void 0 ? void 0 : _18.beforeSave) {
781
- const resp = yield ((_20 = (_19 = resource.hooks) === null || _19 === void 0 ? void 0 : _19.delete) === null || _20 === void 0 ? void 0 : _20.beforeSave({ resource, record, adminUser }));
782
+ if ((_19 = (_18 = resource.hooks) === null || _18 === void 0 ? void 0 : _18.delete) === null || _19 === void 0 ? void 0 : _19.beforeSave) {
783
+ const resp = yield ((_21 = (_20 = resource.hooks) === null || _20 === void 0 ? void 0 : _20.delete) === null || _21 === void 0 ? void 0 : _21.beforeSave({ resource, record, adminUser }));
782
784
  if (!resp || (!resp.ok && !resp.error)) {
783
785
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
784
786
  }
@@ -789,8 +791,8 @@ class AdminForth {
789
791
  const connector = this.connectors[resource.dataSource];
790
792
  yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
791
793
  // execute hook if needed
792
- if ((_22 = (_21 = resource.hooks) === null || _21 === void 0 ? void 0 : _21.delete) === null || _22 === void 0 ? void 0 : _22.afterSave) {
793
- const resp = yield ((_24 = (_23 = resource.hooks) === null || _23 === void 0 ? void 0 : _23.delete) === null || _24 === void 0 ? void 0 : _24.afterSave({ resource, record, adminUser }));
794
+ if ((_23 = (_22 = resource.hooks) === null || _22 === void 0 ? void 0 : _22.delete) === null || _23 === void 0 ? void 0 : _23.afterSave) {
795
+ const resp = yield ((_25 = (_24 = resource.hooks) === null || _24 === void 0 ? void 0 : _24.delete) === null || _25 === void 0 ? void 0 : _25.afterSave({ resource, record, adminUser }));
794
796
  if (!resp || (!resp.ok && !resp.error)) {
795
797
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
796
798
  }
@@ -807,7 +809,7 @@ class AdminForth {
807
809
  noAuth: true, // TODO
808
810
  method: 'POST',
809
811
  path: '/start_bulk_action',
810
- handler: (_25) => __awaiter(this, [_25], void 0, function* ({ body }) {
812
+ handler: (_26) => __awaiter(this, [_26], void 0, function* ({ body }) {
811
813
  const { resourceId, actionId, recordIds } = body;
812
814
  const resource = this.config.resources.find((res) => res.resourceId == resourceId);
813
815
  if (!resource) {
@@ -15,6 +15,7 @@
15
15
  "flowbite": "^2.3.0",
16
16
  "flowbite-datepicker": "^1.2.6",
17
17
  "pinia": "^2.1.7",
18
+ "unhead": "^1.9.12",
18
19
  "vue": "^3.4.21",
19
20
  "vue-router": "^4.3.0",
20
21
  "vue-slider-component": "^4.1.0-beta.7"
@@ -1149,6 +1150,41 @@
1149
1150
  "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
1150
1151
  "dev": true
1151
1152
  },
1153
+ "node_modules/@unhead/dom": {
1154
+ "version": "1.9.12",
1155
+ "resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.9.12.tgz",
1156
+ "integrity": "sha512-3MY1TbZmEjGNZapi3wvJW0vWNS2CLKHt7/m57sScDHCNvNBe1mTwrIOhtZFDgAndhml2EVQ68RMa0Vhum/M+cw==",
1157
+ "dependencies": {
1158
+ "@unhead/schema": "1.9.12",
1159
+ "@unhead/shared": "1.9.12"
1160
+ },
1161
+ "funding": {
1162
+ "url": "https://github.com/sponsors/harlan-zw"
1163
+ }
1164
+ },
1165
+ "node_modules/@unhead/schema": {
1166
+ "version": "1.9.12",
1167
+ "resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.9.12.tgz",
1168
+ "integrity": "sha512-ue2FKyIZKsuZDpWJBMlBGwMm4s+vFeU3NUWsNt8Z+2JkOUIqO/VG43LxNgY1M595bOS71Gdxk+G9VtzfKJ5uEA==",
1169
+ "dependencies": {
1170
+ "hookable": "^5.5.3",
1171
+ "zhead": "^2.2.4"
1172
+ },
1173
+ "funding": {
1174
+ "url": "https://github.com/sponsors/harlan-zw"
1175
+ }
1176
+ },
1177
+ "node_modules/@unhead/shared": {
1178
+ "version": "1.9.12",
1179
+ "resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.9.12.tgz",
1180
+ "integrity": "sha512-72wlLXG3FP3sXUrwd42Uv8jYpHSg4R6IFJcsl+QisRjKM89JnjOFSw1DqWO4IOftW5xOxS4J5v7SQyJ4NJo7Bw==",
1181
+ "dependencies": {
1182
+ "@unhead/schema": "1.9.12"
1183
+ },
1184
+ "funding": {
1185
+ "url": "https://github.com/sponsors/harlan-zw"
1186
+ }
1187
+ },
1152
1188
  "node_modules/@vitejs/plugin-vue": {
1153
1189
  "version": "5.0.4",
1154
1190
  "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
@@ -2471,6 +2507,11 @@
2471
2507
  "he": "bin/he"
2472
2508
  }
2473
2509
  },
2510
+ "node_modules/hookable": {
2511
+ "version": "5.5.3",
2512
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
2513
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
2514
+ },
2474
2515
  "node_modules/ignore": {
2475
2516
  "version": "5.3.1",
2476
2517
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -3914,6 +3955,20 @@
3914
3955
  "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
3915
3956
  "dev": true
3916
3957
  },
3958
+ "node_modules/unhead": {
3959
+ "version": "1.9.12",
3960
+ "resolved": "https://registry.npmjs.org/unhead/-/unhead-1.9.12.tgz",
3961
+ "integrity": "sha512-s6VxcTV45hy8c/IioKQOonFnAO+kBOSpgDfqEHhnU0YVSQYaRPEp9pzW1qSPf0lx+bg9RKeOQyNNbSGGUP26aQ==",
3962
+ "dependencies": {
3963
+ "@unhead/dom": "1.9.12",
3964
+ "@unhead/schema": "1.9.12",
3965
+ "@unhead/shared": "1.9.12",
3966
+ "hookable": "^5.5.3"
3967
+ },
3968
+ "funding": {
3969
+ "url": "https://github.com/sponsors/harlan-zw"
3970
+ }
3971
+ },
3917
3972
  "node_modules/update-browserslist-db": {
3918
3973
  "version": "1.0.16",
3919
3974
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
@@ -4260,6 +4315,14 @@
4260
4315
  "funding": {
4261
4316
  "url": "https://github.com/sponsors/sindresorhus"
4262
4317
  }
4318
+ },
4319
+ "node_modules/zhead": {
4320
+ "version": "2.2.4",
4321
+ "resolved": "https://registry.npmjs.org/zhead/-/zhead-2.2.4.tgz",
4322
+ "integrity": "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==",
4323
+ "funding": {
4324
+ "url": "https://github.com/sponsors/harlan-zw"
4325
+ }
4263
4326
  }
4264
4327
  }
4265
4328
  }
@@ -19,6 +19,7 @@
19
19
  "flowbite": "^2.3.0",
20
20
  "flowbite-datepicker": "^1.2.6",
21
21
  "pinia": "^2.1.7",
22
+ "unhead": "^1.9.12",
22
23
  "vue": "^3.4.21",
23
24
  "vue-router": "^4.3.0",
24
25
  "vue-slider-component": "^4.1.0-beta.7"
@@ -64,6 +64,7 @@
64
64
  </div>
65
65
  </nav>
66
66
 
67
+
67
68
  <aside
68
69
  v-if="loggedIn"
69
70
 
@@ -73,6 +74,9 @@
73
74
  <template v-for="(item, i) in coreStore.menu" :key="`menu-${i}`">
74
75
  <div v-if="item.type === 'divider'" class="border-t border-gray-200 dark:border-gray-700"></div>
75
76
  <div v-else-if="item.type === 'gap'" class="flex items-center justify-center h-8"></div>
77
+ <div v-else-if="item.type === 'heading'" class="flex items-center justify-left pl-2 h-8 opacity-40
78
+ ">{{ item.label }}</div>
79
+
76
80
 
77
81
  <li v-else-if="item.children">
78
82
  <button type="button" class="flex items-center w-full p-2 text-base text-gray-900 transition duration-75 rounded-lg group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
@@ -87,7 +91,7 @@
87
91
  </svg>
88
92
  </button>
89
93
 
90
- <ul :id="`dropdown-example${i}`" role="none" class="py-2 space-y-2" :class="{ 'hidden': !item.open }">
94
+ <ul :id="`dropdown-example${i}`" role="none" class="pt-1 space-y-1" :class="{ 'hidden': !item.open }">
91
95
  <template v-for="(child, j) in item.children" :key="`menu-${i}-${j}`">
92
96
  <li>
93
97
  <MenuLink :item="child" isChild="true" />
@@ -129,10 +133,22 @@ import AcceptModal from './components/AcceptModal.vue';
129
133
  import MenuLink from './components/MenuLink.vue';
130
134
  import { useRoute, useRouter } from 'vue-router';
131
135
  import { getIcon } from '@/utils';
136
+ import { useHead } from 'unhead'
137
+ import { createHead } from 'unhead'
138
+ const coreStore = useCoreStore();
139
+
140
+
141
+ createHead()
142
+
143
+
144
+
145
+
132
146
 
133
147
 
134
148
  const route = useRoute();
135
149
  const router = useRouter();
150
+ const title = ref('');
151
+
136
152
 
137
153
  const routerIsReady = ref(false);
138
154
 
@@ -150,7 +166,6 @@ async function logout() {
150
166
  router.push({ name: 'login' });
151
167
  }
152
168
 
153
- const coreStore = useCoreStore();
154
169
 
155
170
 
156
171
 
@@ -164,6 +179,11 @@ onMounted(async () => {
164
179
  initRouter();
165
180
  initFlowbite();
166
181
  await coreStore.fetchMenuAndResource();
182
+ title.value = coreStore.config.title || 'Admin Forth';
183
+ useHead({
184
+ title: title.value,
185
+
186
+ })
167
187
  })
168
188
 
169
189
  </script>
package/index.ts CHANGED
@@ -486,6 +486,7 @@ class AdminForth {
486
486
  deleteConfirmation: this.config.deleteConfirmation,
487
487
  auth: this.config.auth,
488
488
  usernameField: this.config.auth.usernameField,
489
+ title: this.config?.title,
489
490
  },
490
491
  adminUser,
491
492
  version: ADMINFORTH_VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.0.45",
3
+ "version": "1.0.47",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,6 +15,7 @@
15
15
  "flowbite": "^2.3.0",
16
16
  "flowbite-datepicker": "^1.2.6",
17
17
  "pinia": "^2.1.7",
18
+ "unhead": "^1.9.12",
18
19
  "vue": "^3.4.21",
19
20
  "vue-router": "^4.3.0",
20
21
  "vue-slider-component": "^4.1.0-beta.7"
@@ -1149,6 +1150,41 @@
1149
1150
  "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
1150
1151
  "dev": true
1151
1152
  },
1153
+ "node_modules/@unhead/dom": {
1154
+ "version": "1.9.12",
1155
+ "resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.9.12.tgz",
1156
+ "integrity": "sha512-3MY1TbZmEjGNZapi3wvJW0vWNS2CLKHt7/m57sScDHCNvNBe1mTwrIOhtZFDgAndhml2EVQ68RMa0Vhum/M+cw==",
1157
+ "dependencies": {
1158
+ "@unhead/schema": "1.9.12",
1159
+ "@unhead/shared": "1.9.12"
1160
+ },
1161
+ "funding": {
1162
+ "url": "https://github.com/sponsors/harlan-zw"
1163
+ }
1164
+ },
1165
+ "node_modules/@unhead/schema": {
1166
+ "version": "1.9.12",
1167
+ "resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.9.12.tgz",
1168
+ "integrity": "sha512-ue2FKyIZKsuZDpWJBMlBGwMm4s+vFeU3NUWsNt8Z+2JkOUIqO/VG43LxNgY1M595bOS71Gdxk+G9VtzfKJ5uEA==",
1169
+ "dependencies": {
1170
+ "hookable": "^5.5.3",
1171
+ "zhead": "^2.2.4"
1172
+ },
1173
+ "funding": {
1174
+ "url": "https://github.com/sponsors/harlan-zw"
1175
+ }
1176
+ },
1177
+ "node_modules/@unhead/shared": {
1178
+ "version": "1.9.12",
1179
+ "resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.9.12.tgz",
1180
+ "integrity": "sha512-72wlLXG3FP3sXUrwd42Uv8jYpHSg4R6IFJcsl+QisRjKM89JnjOFSw1DqWO4IOftW5xOxS4J5v7SQyJ4NJo7Bw==",
1181
+ "dependencies": {
1182
+ "@unhead/schema": "1.9.12"
1183
+ },
1184
+ "funding": {
1185
+ "url": "https://github.com/sponsors/harlan-zw"
1186
+ }
1187
+ },
1152
1188
  "node_modules/@vitejs/plugin-vue": {
1153
1189
  "version": "5.0.4",
1154
1190
  "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz",
@@ -2471,6 +2507,11 @@
2471
2507
  "he": "bin/he"
2472
2508
  }
2473
2509
  },
2510
+ "node_modules/hookable": {
2511
+ "version": "5.5.3",
2512
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
2513
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="
2514
+ },
2474
2515
  "node_modules/ignore": {
2475
2516
  "version": "5.3.1",
2476
2517
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
@@ -3914,6 +3955,20 @@
3914
3955
  "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
3915
3956
  "dev": true
3916
3957
  },
3958
+ "node_modules/unhead": {
3959
+ "version": "1.9.12",
3960
+ "resolved": "https://registry.npmjs.org/unhead/-/unhead-1.9.12.tgz",
3961
+ "integrity": "sha512-s6VxcTV45hy8c/IioKQOonFnAO+kBOSpgDfqEHhnU0YVSQYaRPEp9pzW1qSPf0lx+bg9RKeOQyNNbSGGUP26aQ==",
3962
+ "dependencies": {
3963
+ "@unhead/dom": "1.9.12",
3964
+ "@unhead/schema": "1.9.12",
3965
+ "@unhead/shared": "1.9.12",
3966
+ "hookable": "^5.5.3"
3967
+ },
3968
+ "funding": {
3969
+ "url": "https://github.com/sponsors/harlan-zw"
3970
+ }
3971
+ },
3917
3972
  "node_modules/update-browserslist-db": {
3918
3973
  "version": "1.0.16",
3919
3974
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
@@ -4260,6 +4315,14 @@
4260
4315
  "funding": {
4261
4316
  "url": "https://github.com/sponsors/sindresorhus"
4262
4317
  }
4318
+ },
4319
+ "node_modules/zhead": {
4320
+ "version": "2.2.4",
4321
+ "resolved": "https://registry.npmjs.org/zhead/-/zhead-2.2.4.tgz",
4322
+ "integrity": "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==",
4323
+ "funding": {
4324
+ "url": "https://github.com/sponsors/harlan-zw"
4325
+ }
4263
4326
  }
4264
4327
  }
4265
4328
  }
package/spa/package.json CHANGED
@@ -19,6 +19,7 @@
19
19
  "flowbite": "^2.3.0",
20
20
  "flowbite-datepicker": "^1.2.6",
21
21
  "pinia": "^2.1.7",
22
+ "unhead": "^1.9.12",
22
23
  "vue": "^3.4.21",
23
24
  "vue-router": "^4.3.0",
24
25
  "vue-slider-component": "^4.1.0-beta.7"
package/spa/src/App.vue CHANGED
@@ -64,6 +64,7 @@
64
64
  </div>
65
65
  </nav>
66
66
 
67
+
67
68
  <aside
68
69
  v-if="loggedIn"
69
70
 
@@ -73,6 +74,9 @@
73
74
  <template v-for="(item, i) in coreStore.menu" :key="`menu-${i}`">
74
75
  <div v-if="item.type === 'divider'" class="border-t border-gray-200 dark:border-gray-700"></div>
75
76
  <div v-else-if="item.type === 'gap'" class="flex items-center justify-center h-8"></div>
77
+ <div v-else-if="item.type === 'heading'" class="flex items-center justify-left pl-2 h-8 opacity-40
78
+ ">{{ item.label }}</div>
79
+
76
80
 
77
81
  <li v-else-if="item.children">
78
82
  <button type="button" class="flex items-center w-full p-2 text-base text-gray-900 transition duration-75 rounded-lg group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
@@ -87,7 +91,7 @@
87
91
  </svg>
88
92
  </button>
89
93
 
90
- <ul :id="`dropdown-example${i}`" role="none" class="py-2 space-y-2" :class="{ 'hidden': !item.open }">
94
+ <ul :id="`dropdown-example${i}`" role="none" class="pt-1 space-y-1" :class="{ 'hidden': !item.open }">
91
95
  <template v-for="(child, j) in item.children" :key="`menu-${i}-${j}`">
92
96
  <li>
93
97
  <MenuLink :item="child" isChild="true" />
@@ -129,10 +133,22 @@ import AcceptModal from './components/AcceptModal.vue';
129
133
  import MenuLink from './components/MenuLink.vue';
130
134
  import { useRoute, useRouter } from 'vue-router';
131
135
  import { getIcon } from '@/utils';
136
+ import { useHead } from 'unhead'
137
+ import { createHead } from 'unhead'
138
+ const coreStore = useCoreStore();
139
+
140
+
141
+ createHead()
142
+
143
+
144
+
145
+
132
146
 
133
147
 
134
148
  const route = useRoute();
135
149
  const router = useRouter();
150
+ const title = ref('');
151
+
136
152
 
137
153
  const routerIsReady = ref(false);
138
154
 
@@ -150,7 +166,6 @@ async function logout() {
150
166
  router.push({ name: 'login' });
151
167
  }
152
168
 
153
- const coreStore = useCoreStore();
154
169
 
155
170
 
156
171
 
@@ -164,6 +179,11 @@ onMounted(async () => {
164
179
  initRouter();
165
180
  initFlowbite();
166
181
  await coreStore.fetchMenuAndResource();
182
+ title.value = coreStore.config.title || 'Admin Forth';
183
+ useHead({
184
+ title: title.value,
185
+
186
+ })
167
187
  })
168
188
 
169
189
  </script>
@@ -1,6 +1,7 @@
1
1
 
2
2
 
3
3
  export type AdminForthConfigMenuItem = {
4
+ type: 'heading' | 'group' | 'resource' | 'page' | undefined,
4
5
  label: string,
5
6
  icon?: string,
6
7
  path?: string,
@@ -107,6 +108,7 @@ export type AdminForthConfig = {
107
108
  brandName?: string,
108
109
  datesFormat?: string,
109
110
  deleteConfirmation?: boolean,
111
+ title?: string,
110
112
  }
111
113
 
112
114
  export type AllowedActions = {