adminforth 1.1.59 → 1.1.61

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.
Files changed (67) hide show
  1. package/dist/index.js +68 -63
  2. package/dist/modules/styleGenerator.js +22 -18
  3. package/dist/modules/styles.js +44 -36
  4. package/dist/modules/utils.js +75 -0
  5. package/dist/plugins/AccessControl/index.js +66 -0
  6. package/dist/plugins/AccessControl/types.js +1 -0
  7. package/dist/plugins/AuditLogPlugin/custom/AuditLogView.vue +20 -0
  8. package/dist/plugins/AuditLogPlugin/index.js +83 -0
  9. package/dist/plugins/AuditLogPlugin/types.js +1 -0
  10. package/dist/spa/index.html +2 -2
  11. package/dist/spa/package-lock.json +4 -17
  12. package/dist/spa/package.json +1 -2
  13. package/dist/spa/public/favicon.ico +0 -0
  14. package/dist/spa/spa/package-lock.json +111 -0
  15. package/dist/spa/spa/package.json +1 -0
  16. package/dist/spa/spa/public/favicon.ico +0 -0
  17. package/dist/spa/spa/src/App.vue +4 -4
  18. package/dist/spa/spa/src/components/MenuLink.vue +1 -1
  19. package/dist/spa/spa/src/components/ResourceForm.vue +2 -2
  20. package/dist/spa/src/App.vue +45 -125
  21. package/dist/spa/src/assets/logo.svg +1 -19
  22. package/dist/spa/src/components/AcceptModal.vue +9 -2
  23. package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
  24. package/dist/spa/src/components/CustomDatePicker.vue +3 -16
  25. package/dist/spa/src/components/CustomDateRangePicker.vue +2 -11
  26. package/dist/spa/src/components/Dropdown.vue +1 -6
  27. package/dist/spa/src/components/Filters.vue +19 -40
  28. package/dist/spa/src/components/ResourceForm.vue +24 -26
  29. package/dist/spa/src/components/ValueRenderer.vue +8 -14
  30. package/dist/spa/src/main.ts +1 -1
  31. package/dist/spa/src/router/index.ts +20 -30
  32. package/dist/spa/src/stores/core.ts +31 -48
  33. package/dist/spa/src/stores/modal.ts +3 -13
  34. package/dist/spa/src/utils.ts +7 -62
  35. package/dist/spa/src/views/CreateView.vue +15 -67
  36. package/dist/spa/src/views/EditView.vue +11 -45
  37. package/dist/spa/src/views/ListView.vue +366 -82
  38. package/dist/spa/src/views/LoginView.vue +4 -16
  39. package/dist/spa/src/views/ShowView.vue +21 -105
  40. package/dist/spa/tailwind.config.js +1 -1
  41. package/dist/spa/vite.config.ts +0 -6
  42. package/dist/types.js +30 -0
  43. package/index.ts +62 -61
  44. package/modules/styleGenerator.ts +25 -18
  45. package/modules/styles.ts +46 -36
  46. package/modules/utils.ts +91 -1
  47. package/package.json +2 -1
  48. package/plugins/AuditLogPlugin/custom/AuditLogView.vue +20 -0
  49. package/plugins/AuditLogPlugin/index.ts +99 -0
  50. package/plugins/AuditLogPlugin/types.ts +40 -0
  51. package/spa/package-lock.json +111 -0
  52. package/spa/package.json +1 -0
  53. package/spa/src/App.vue +4 -4
  54. package/spa/src/components/MenuLink.vue +1 -1
  55. package/spa/src/components/ResourceForm.vue +2 -2
  56. package/types/AdminForthConfig.ts +6 -1
  57. package/dist/spa/src/components/ResourceListTable.vue +0 -419
  58. package/dist/spa/src/components/Toast.vue +0 -66
  59. package/dist/spa/src/composables/useFrontendApi.ts +0 -26
  60. package/dist/spa/src/composables/useStores.ts +0 -113
  61. package/dist/spa/src/spa_types/core.ts +0 -51
  62. package/dist/spa/src/stores/filters.ts +0 -22
  63. package/dist/spa/src/stores/toast.ts +0 -15
  64. package/dist/spa/src/stores/user.ts +0 -54
  65. package/plugins/AuditLog/index.ts +0 -29
  66. package/plugins/AuditLog/types.ts +0 -4
  67. /package/dist/spa/{public/assets → spa/public}/favicon.png +0 -0
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import ExpressServer from './servers/express.js';
23
23
  import { v1 as uuid } from 'uuid';
24
24
  import fs from 'fs';
25
25
  import { ADMINFORTH_VERSION, listify } from './modules/utils.js';
26
- import { AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages, AllowedActionsEnum, ActionCheckSource } from './types/AdminForthConfig.js';
26
+ import { AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages, AllowedActionsEnum, ActionCheckSource, } from './types/AdminForthConfig.js';
27
27
  import path from 'path';
28
28
  //get array from enum AdminForthResourcePages
29
29
  class AdminForth {
@@ -473,6 +473,63 @@ class AdminForth {
473
473
  return users.data[0] || null;
474
474
  });
475
475
  }
476
+ createResourceRecord(_b) {
477
+ return __awaiter(this, arguments, void 0, function* ({ resource, record, adminUser }) {
478
+ var _c, _d, _e, _f, _g;
479
+ for (const column of resource.columns) {
480
+ if (column.fillOnCreate) {
481
+ if (record[column.name] === undefined) {
482
+ record[column.name] = column.fillOnCreate({
483
+ initialRecord: record, adminUser
484
+ });
485
+ }
486
+ }
487
+ if (((_c = column.required) === null || _c === void 0 ? void 0 : _c.create) && record[column.name] === undefined) {
488
+ return { error: `Column '${column.name}' is required` };
489
+ }
490
+ if (column.isUnique) {
491
+ const existingRecord = yield this.connectors[resource.dataSource].getData({
492
+ resource,
493
+ filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value: record[column.name] }],
494
+ limit: 1,
495
+ sort: [],
496
+ offset: 0
497
+ });
498
+ if (existingRecord.data.length > 0) {
499
+ return { error: `Record with ${column.name} ${record[column.name]} already exists` };
500
+ }
501
+ }
502
+ }
503
+ // execute hook if needed
504
+ for (const hook of listify((_e = (_d = resource.hooks) === null || _d === void 0 ? void 0 : _d.create) === null || _e === void 0 ? void 0 : _e.beforeSave)) {
505
+ const resp = yield hook({ resource, record, adminUser });
506
+ if (!resp || (!resp.ok && !resp.error)) {
507
+ throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
508
+ }
509
+ if (resp.error) {
510
+ return { error: resp.error };
511
+ }
512
+ }
513
+ // remove virtual columns from record
514
+ for (const column of resource.columns.filter((col) => col.virtual)) {
515
+ if (record[column.name]) {
516
+ delete record[column.name];
517
+ }
518
+ }
519
+ const connector = this.connectors[resource.dataSource];
520
+ yield connector.createRecord({ resource, record });
521
+ // execute hook if needed
522
+ for (const hook of listify((_g = (_f = resource.hooks) === null || _f === void 0 ? void 0 : _f.create) === null || _g === void 0 ? void 0 : _g.afterSave)) {
523
+ const resp = yield hook({ resource, record, adminUser });
524
+ if (!resp || (!resp.ok && !resp.error)) {
525
+ throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
526
+ }
527
+ if (resp.error) {
528
+ return { error: resp.error };
529
+ }
530
+ }
531
+ });
532
+ }
476
533
  setupEndpoints(server) {
477
534
  server.endpoint({
478
535
  noAuth: true,
@@ -904,7 +961,6 @@ class AdminForth {
904
961
  method: 'POST',
905
962
  path: '/create_record',
906
963
  handler: (_y) => __awaiter(this, [_y], void 0, function* ({ body, adminUser }) {
907
- var _z, _0, _1, _2, _3;
908
964
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
909
965
  if (!resource) {
910
966
  return { error: `Resource '${body['resourceId']}' not found` };
@@ -914,59 +970,8 @@ class AdminForth {
914
970
  if (!allowed) {
915
971
  return { error };
916
972
  }
917
- const record = body['record'];
918
- // execute hook if needed
919
- for (const hook of listify((_0 = (_z = resource.hooks) === null || _z === void 0 ? void 0 : _z.create) === null || _0 === void 0 ? void 0 : _0.beforeSave)) {
920
- const resp = yield hook({ resource, record, adminUser });
921
- if (!resp || (!resp.ok && !resp.error)) {
922
- throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
923
- }
924
- if (resp.error) {
925
- return { error: resp.error };
926
- }
927
- }
928
- for (const column of resource.columns) {
929
- if (column.fillOnCreate) {
930
- if (body['record'][column.name] === undefined) {
931
- body['record'][column.name] = column.fillOnCreate({
932
- initialRecord: body['record'], adminUser
933
- });
934
- }
935
- }
936
- if (((_1 = column.required) === null || _1 === void 0 ? void 0 : _1.create) && body['record'][column.name] === undefined) {
937
- return { error: `Column '${column.name}' is required` };
938
- }
939
- if (column.isUnique) {
940
- const existingRecord = yield this.connectors[resource.dataSource].getData({
941
- resource,
942
- filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value: body['record'][column.name] }],
943
- limit: 1,
944
- sort: [],
945
- offset: 0
946
- });
947
- if (existingRecord.data.length > 0) {
948
- return { error: `Record with ${column.name} ${body['record'][column.name]} already exists` };
949
- }
950
- }
951
- }
952
- // remove virtual columns from record
953
- for (const column of resource.columns.filter((col) => col.virtual)) {
954
- if (record[column.name]) {
955
- delete record[column.name];
956
- }
957
- }
973
+ yield this.createResourceRecord({ resource, record: body['record'], adminUser });
958
974
  const connector = this.connectors[resource.dataSource];
959
- yield connector.createRecord({ resource, record });
960
- // execute hook if needed
961
- for (const hook of listify((_3 = (_2 = resource.hooks) === null || _2 === void 0 ? void 0 : _2.create) === null || _3 === void 0 ? void 0 : _3.afterSave)) {
962
- const resp = yield hook({ resource, record, adminUser });
963
- if (!resp || (!resp.ok && !resp.error)) {
964
- throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
965
- }
966
- if (resp.error) {
967
- return { error: resp.error };
968
- }
969
- }
970
975
  return {
971
976
  newRecordId: body['record'][connector.getPrimaryKey(resource)]
972
977
  };
@@ -975,8 +980,8 @@ class AdminForth {
975
980
  server.endpoint({
976
981
  method: 'POST',
977
982
  path: '/update_record',
978
- handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
979
- var _5, _6, _7, _8;
983
+ handler: (_z) => __awaiter(this, [_z], void 0, function* ({ body, adminUser }) {
984
+ var _0, _1, _2, _3;
980
985
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
981
986
  if (!resource) {
982
987
  return { error: `Resource '${body['resourceId']}' not found` };
@@ -995,7 +1000,7 @@ class AdminForth {
995
1000
  return { error };
996
1001
  }
997
1002
  // execute hook if needed
998
- for (const hook of listify((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.edit) === null || _6 === void 0 ? void 0 : _6.beforeSave)) {
1003
+ for (const hook of listify((_1 = (_0 = resource.hooks) === null || _0 === void 0 ? void 0 : _0.edit) === null || _1 === void 0 ? void 0 : _1.beforeSave)) {
999
1004
  const resp = yield hook({ resource, record, adminUser });
1000
1005
  if (!resp || (!resp.ok && !resp.error)) {
1001
1006
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1022,7 +1027,7 @@ class AdminForth {
1022
1027
  yield connector.updateRecord({ resource, recordId, record, newValues });
1023
1028
  }
1024
1029
  // execute hook if needed
1025
- for (const hook of listify((_8 = (_7 = resource.hooks) === null || _7 === void 0 ? void 0 : _7.edit) === null || _8 === void 0 ? void 0 : _8.afterSave)) {
1030
+ for (const hook of listify((_3 = (_2 = resource.hooks) === null || _2 === void 0 ? void 0 : _2.edit) === null || _3 === void 0 ? void 0 : _3.afterSave)) {
1026
1031
  const resp = yield hook({ resource, record, adminUser });
1027
1032
  if (!resp || (!resp.ok && !resp.error)) {
1028
1033
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1039,8 +1044,8 @@ class AdminForth {
1039
1044
  server.endpoint({
1040
1045
  method: 'POST',
1041
1046
  path: '/delete_record',
1042
- handler: (_9) => __awaiter(this, [_9], void 0, function* ({ body, adminUser }) {
1043
- var _10, _11, _12, _13;
1047
+ handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
1048
+ var _5, _6, _7, _8;
1044
1049
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
1045
1050
  const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
1046
1051
  if (!resource) {
@@ -1058,7 +1063,7 @@ class AdminForth {
1058
1063
  return { error };
1059
1064
  }
1060
1065
  // execute hook if needed
1061
- for (const hook of listify((_11 = (_10 = resource.hooks) === null || _10 === void 0 ? void 0 : _10.delete) === null || _11 === void 0 ? void 0 : _11.beforeSave)) {
1066
+ for (const hook of listify((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.delete) === null || _6 === void 0 ? void 0 : _6.beforeSave)) {
1062
1067
  const resp = yield hook({ resource, record, adminUser });
1063
1068
  if (!resp || (!resp.ok && !resp.error)) {
1064
1069
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1070,7 +1075,7 @@ class AdminForth {
1070
1075
  const connector = this.connectors[resource.dataSource];
1071
1076
  yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
1072
1077
  // execute hook if needed
1073
- for (const hook of listify((_13 = (_12 = resource.hooks) === null || _12 === void 0 ? void 0 : _12.delete) === null || _13 === void 0 ? void 0 : _13.afterSave)) {
1078
+ for (const hook of listify((_8 = (_7 = resource.hooks) === null || _7 === void 0 ? void 0 : _7.delete) === null || _8 === void 0 ? void 0 : _8.afterSave)) {
1074
1079
  const resp = yield hook({ resource, record, adminUser });
1075
1080
  if (!resp || (!resp.ok && !resp.error)) {
1076
1081
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1087,7 +1092,7 @@ class AdminForth {
1087
1092
  server.endpoint({
1088
1093
  method: 'POST',
1089
1094
  path: '/start_bulk_action',
1090
- handler: (_14) => __awaiter(this, [_14], void 0, function* ({ body }) {
1095
+ handler: (_9) => __awaiter(this, [_9], void 0, function* ({ body }) {
1091
1096
  const { resourceId, actionId, recordIds } = body;
1092
1097
  const resource = this.config.resources.find((res) => res.resourceId == resourceId);
1093
1098
  if (!resource) {
@@ -1,11 +1,7 @@
1
- import { transformObject, deepMerge } from "./utils.js";
1
+ import { transformObject, deepMerge, createRGBA, parseColorForAliases, darkenRGBA, lightenRGBA } from "./utils.js";
2
2
  import { styles } from "./styles.js";
3
- const aliasObject = { lightSidebarText: 'lightSidebarIcons', darkSidebarText: 'darkSidebarIcons', lightSidebarTextHover: 'lightSidebarIconsHover', darkSidebarTextHover: 'darkSidebarIconsHover' };
4
3
  export class StylesGenerator {
5
4
  constructor(styleConfig) {
6
- if (!styleConfig) {
7
- console.log("No styles provided using default styles.");
8
- }
9
5
  this.styleConfig = styleConfig;
10
6
  this.defaultStyles = styles();
11
7
  }
@@ -18,22 +14,30 @@ export class StylesGenerator {
18
14
  }
19
15
  return plainCustomStyles;
20
16
  }
21
- changeAliases(plateStyles) {
22
- if (!plateStyles || !plateStyles.colors) {
23
- return plateStyles;
17
+ changeAlias(str, mergedStyles) {
18
+ const { aliasMatch, opacityMatch, darkenMatch, lightenMatch } = parseColorForAliases(str);
19
+ if (!aliasMatch) {
20
+ return str;
21
+ }
22
+ else {
23
+ const alias = aliasMatch[1];
24
+ let opacity = opacityMatch ? parseFloat(opacityMatch[1]) : 1;
25
+ const color = mergedStyles[alias];
26
+ if (darkenMatch) {
27
+ return darkenRGBA(createRGBA(color, opacity));
28
+ }
29
+ if (lightenMatch) {
30
+ return lightenRGBA(createRGBA(color, opacity));
31
+ }
32
+ return createRGBA(color, opacity);
24
33
  }
25
- let colors = plateStyles.colors;
26
- Object.keys(aliasObject).forEach((aliasName) => {
27
- if (colors[aliasObject[aliasName]])
28
- return;
29
- if (!colors[aliasName])
30
- return;
31
- colors[aliasObject[aliasName]] = colors[aliasName];
32
- });
33
- return plateStyles;
34
34
  }
35
35
  mergeStyles() {
36
- let mergedStyles = deepMerge(this.defaultStyles, this.changeAliases(this.generatePlainStyles(this.styleConfig)));
36
+ let mergedStyles = deepMerge(this.defaultStyles, this.generatePlainStyles(this.styleConfig));
37
+ let colors = mergedStyles.colors;
38
+ Object.entries(colors).forEach(([key, value]) => {
39
+ colors[key] = this.changeAlias(value, colors);
40
+ });
37
41
  return mergedStyles;
38
42
  }
39
43
  }
@@ -2,26 +2,22 @@ export const styles = () => ({
2
2
  colors: {
3
3
  lightHtml: "#FFFFFF", // main background
4
4
  lightNavbar: "#FFFFFF", // navbar background
5
- lightNavbarHover: "#D3D3D3", // in development
6
- lightNavbarText: "#333333", // in development
7
- lightNavbarTextHover: "#333333", // in development
8
- lightNavbarIcons: "#333333", // in development
9
- lightNavbarIconsHover: "#333333", // in development
10
- lightNavbarLogo: "#333333", // in development
11
5
  lightNavbarBorder: "rgb(229 231 235)", // border
6
+ lightNavbarText: "#111827", // navbar text
7
+ lightNavbarTextHover: "alias:lightNavbarText darken", // navbar text hover
8
+ lightNavbarTextActive: "alias:lightNavbarText darken", // navbar text active
9
+ lightNavbarIcons: "alias:lightNavbarText opacity:0.7", // navbar icons
12
10
  lightSidebar: "#ffffff", // sidebar background
13
- lightSidebarBorder: "rgb(229 231 235)", // sidebar right border
14
- lightSidebarHover: "rgb(243 244 246)", // in development
15
- lightSidebarActive: "rgb(233, 234, 236)", // in development
16
- lightSidebarItemHover: "#f3f4f6", // sidebar list item hover
17
- lightSidebarItemActive: "rgb(233, 234, 236)", // sidebar list item active
18
- lightSidebarText: "#4b5563", // sidebar list item text
19
- lightSidebarTextHover: "#333333", // sidebar list item text hover
20
- lightSidebarTextActive: "#333333", // sidebar list item text active
21
- lightSidebarDevider: "#E0E0E0", // sidebar devider
22
- lightSidebarIcons: "#6b7280", // sidebar list item icons
23
- lightSidebarIconsHover: "#333333", // sidebar list item icons hover
24
- lightSidebarHeading: "#333333", // sidebar heading
11
+ lightSidebarBorder: "alias:lightSidebarText opacity:0.3", // sidebar right border
12
+ lightSidebarItemHover: "alias:lightSidebarText opacity:0.1", // sidebar list item hover
13
+ lightSidebarItemActive: "alias:lightSidebarText opacity:0.4", // sidebar list item active
14
+ lightSidebarText: "#1f2937", // sidebar list item text
15
+ lightSidebarTextHover: "alias:lightSidebarText darken", // sidebar list item text hover
16
+ lightSidebarTextActive: "alias:lightSidebarText darken", // sidebar list item text active
17
+ lightSidebarDevider: "alias:lightSidebarText opacity:0.3", // sidebar devider
18
+ lightSidebarIcons: "alias:lightSidebarText opacity:0.7", // sidebar list item icons
19
+ lightSidebarIconsHover: "alias:lightSidebarText", // sidebar list item icons hover
20
+ lightSidebarHeading: "alias:lightSidebarText opacity:0.3", // sidebar heading
25
21
  lightList: "#FFFFFF", // list view background
26
22
  lightListTable: "#FFFFFF", // list view table background
27
23
  lightListTableHeading: "rgb(249 250 251)", // list view table heading
@@ -30,33 +26,45 @@ export const styles = () => ({
30
26
  lightListTableRowHover: "rgb(249 250 251)", // list view row hover
31
27
  lightListBreadcrumbsText: "#666666", // list view breadcrumbs text
32
28
  lightListBorder: "#DDDDDD", // list view rows border
29
+ // lightListButton: "#FFFFFF", // list view button
30
+ // lightListButtonBorder: "#d1d5db", // list view button border
31
+ // lightListButtonHover: "#f3f4f6", // list view button hover
32
+ // lightListButtonBorderHover: "#f3f4f6", // list view button border hover
33
+ // lightListButtonActive: "#f3f4f6", // list view button active
34
+ // lightListButtonDisabled: "#f3f4f6", // list view button disabled
35
+ // lightListButtonDisabledText: "#f3f4f6", // list view button disabled text
36
+ // lightListButtonIcon: "#333333", // list view button icon
33
37
  lightForm: "#FFFFFF", // show view background
34
38
  lightFormBorder: "#F5F5F5", // show view rows border
35
39
  lightFormHeading: "rgb(249 250 251)", // show view heading
40
+ lightButtons: "#FFFFFF", // button background
41
+ lightButtonsBorder: "#DDDDDD", // button border
42
+ lightButtonsText: "#111827", // button text
43
+ lightButtonsHover: "#f3f4f6", // button hover
44
+ lightButtonsBorderHover: "#f3f4f6", // button border hover
45
+ lightButtonsActive: "#f3f4f6", // button active
46
+ lightButtonsDisabled: "#f3f4f6", // button disabled
47
+ lightButtonsDisabledText: "#f3f4f6", // button disabled text
48
+ lightButtonsIcon: "#333333", // button icon
36
49
  // colors for dark theme
37
50
  darkHtml: "#111827",
38
51
  darkNavbar: "#111827",
39
- darkNavbarHover: "#444444",
40
- darkNavbarText: "#FFFFFF",
41
- darkNavbarTextHover: "#FFFFFF",
42
- darkNavbarIcons: "#FFFFFF",
43
- darkNavbarIconsHover: "#FFFFFF",
44
- darkNavbarLogo: "#FFFFFF",
45
52
  darkNavbarBorder: "#e5e7eb",
53
+ darkNavbarText: "#9ca3af",
54
+ darkNavbarTextHover: "alias:darkNavbarText lighten",
55
+ darkNavbarTextActive: "alias:darkNavbarText lighten",
56
+ darkNavbarIcons: "alias:darkNavbarText opacity:0.7",
46
57
  darkSidebar: "#1f2937",
47
- darkSidebarBorder: "#374151",
48
- darkSidebarHover: "#374151",
49
- darkSidebarActive: "#444444",
50
- darkSidebarItemHover: "#374151",
51
- darkSidebarItemActive: "#52525b",
52
- darkSidebarItemActiveText: "#FFFFFF",
58
+ darkSidebarBorder: "alias:darkSidebarText opacity:0.3",
59
+ darkSidebarItemHover: "alias:darkSidebarText opacity:0.1",
60
+ darkSidebarItemActive: "alias:darkSidebarText opacity:0.4",
53
61
  darkSidebarText: "#9ca3af",
54
- darkSidebarTextHover: "#FFFFFF",
55
- darkSidebarTextActive: "#FFFFFF",
56
- darkSidebarDevider: "#374151",
57
- darkSidebarIcons: "#9ca3af",
58
- darkSidebarIconsHover: "#FFFFFF",
59
- darkSidebarHeading: "#FFFFFF",
62
+ darkSidebarTextHover: "alias:darkSidebarText lighten",
63
+ darkSidebarTextActive: "alias:darkSidebarText lighten",
64
+ darkSidebarDevider: "alias:darkSidebarText opacity:0.3",
65
+ darkSidebarIcons: "alias:darkSidebarText opacity:0.7",
66
+ darkSidebarIconsHover: "alias:darkSidebarText",
67
+ darkSidebarHeading: "alias:darkSidebarText opacity:0.3",
60
68
  darkList: "#111111",
61
69
  darkListTable: "#1f2937",
62
70
  darkListTableHeading: "#334155",
@@ -1,6 +1,8 @@
1
1
  import path from 'path';
2
2
  import { fileURLToPath } from 'url';
3
3
  import fs from 'fs';
4
+ // @ts-ignore-next-line
5
+ import csscolors from 'css-color-names';
4
6
  export function guessLabelFromName(name) {
5
7
  if (name.includes('_')) {
6
8
  return name.split('_').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
@@ -75,3 +77,76 @@ export function transformObject(obj, parentKey = '', result = {}) {
75
77
  }
76
78
  return result;
77
79
  }
80
+ function hexToRGBA(hex, opacity) {
81
+ var c;
82
+ if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
83
+ c = hex.substring(1).split('');
84
+ if (c.length == 3) {
85
+ c = [c[0], c[0], c[1], c[1], c[2], c[2]];
86
+ }
87
+ c = '0x' + c.join('');
88
+ return 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',') + ',' + opacity + ')';
89
+ }
90
+ throw new Error('Bad Hex');
91
+ }
92
+ export function createRGBA(color, opacity) {
93
+ if (!color)
94
+ return;
95
+ else if (color.startsWith("rgba")) {
96
+ //add opacity to existing rgba color
97
+ const rgb = color.match(/[\d.]+/g);
98
+ console.log('rgb', rgb);
99
+ return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${Number(rgb[3]) * opacity})`;
100
+ }
101
+ else if (color.startsWith("rgb")) {
102
+ const rgb = color.match(/\d+/g);
103
+ return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${opacity})`;
104
+ }
105
+ else if (color.startsWith("#")) {
106
+ return hexToRGBA(color, opacity);
107
+ }
108
+ else if (csscolors[color]) {
109
+ return hexToRGBA(csscolors[color], opacity);
110
+ }
111
+ else {
112
+ return color;
113
+ }
114
+ }
115
+ export function parseColorForAliases(str) {
116
+ const aliasRegex = /alias:([^ ]+)/;
117
+ const opacityRegex = /opacity:([0-9.]+)/;
118
+ const darkenRegex = /darken/;
119
+ const lightenRegex = /lighten/;
120
+ // Extract alias and properties
121
+ const aliasMatch = str.match(aliasRegex);
122
+ const opacityMatch = str.match(opacityRegex);
123
+ const darkenMatch = str.match(darkenRegex);
124
+ const lightenMatch = str.match(lightenRegex);
125
+ return { aliasMatch, opacityMatch, darkenMatch, lightenMatch };
126
+ }
127
+ export function darkenRGBA(rgba) {
128
+ let amount = 0.2;
129
+ // Extract the RGBA components
130
+ let [r, g, b, a] = rgba.match(/\d+/g).map(Number);
131
+ // Ensure amount is between 0 and 1
132
+ amount = Math.max(0, Math.min(1, amount));
133
+ // Calculate the new RGB values
134
+ r = Math.max(0, r * (1 - amount));
135
+ g = Math.max(0, g * (1 - amount));
136
+ b = Math.max(0, b * (1 - amount));
137
+ // Return the new RGBA color
138
+ return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;
139
+ }
140
+ export function lightenRGBA(rgba) {
141
+ let amount = 0.2;
142
+ // Extract the RGBA components
143
+ let [r, g, b, a] = rgba.match(/\d+/g).map(Number);
144
+ // Ensure amount is between 0 and 1
145
+ amount = Math.max(0, Math.min(1, amount));
146
+ // Calculate the new RGB values
147
+ r = Math.min(255, r * (1 + amount));
148
+ g = Math.min(255, g * (1 + amount));
149
+ b = Math.min(255, b * (1 + amount));
150
+ // Return the new RGBA color
151
+ return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;
152
+ }
@@ -0,0 +1,66 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { AllowedActionsEnum } from "../../types/AdminForthConfig.js";
11
+ import AdminForthPlugin from "../base.js";
12
+ class AccessControlPlugin extends AdminForthPlugin {
13
+ constructor(options) {
14
+ super(options, import.meta.url);
15
+ this.options = options;
16
+ }
17
+ modifyResourceConfig(adminforth, resourceConfig) {
18
+ super.modifyResourceConfig(adminforth, resourceConfig);
19
+ this.adminforth = adminforth;
20
+ if (!resourceConfig.hooks) {
21
+ resourceConfig.hooks = {};
22
+ }
23
+ const checkAccess = (adminUser, action, meta) => __awaiter(this, void 0, void 0, function* () {
24
+ const hasAccessOrError = yield this.options.hasAccess(adminUser, action, meta);
25
+ if (hasAccessOrError === true) {
26
+ return { ok: true };
27
+ }
28
+ else {
29
+ return { ok: false, error: hasAccessOrError || AccessControlPlugin.defaultError };
30
+ }
31
+ });
32
+ const bindHookCheck = (action, hookName) => {
33
+ if (!resourceConfig.hooks[action]) {
34
+ resourceConfig.hooks[action] = {};
35
+ }
36
+ if (!resourceConfig.hooks[action][hookName]) {
37
+ resourceConfig.hooks[action][hookName] = [];
38
+ }
39
+ if (hookName === 'beforeDatasourceRequest') {
40
+ resourceConfig.hooks[action][hookName].unshift((_a) => __awaiter(this, [_a], void 0, function* ({ adminUser, query }) {
41
+ return checkAccess(adminUser, action, { query });
42
+ }));
43
+ }
44
+ else {
45
+ resourceConfig.hooks[action][hookName].unshift((_b) => __awaiter(this, [_b], void 0, function* ({ adminUser, record }) {
46
+ return checkAccess(adminUser, action, { record });
47
+ }));
48
+ }
49
+ };
50
+ // List check
51
+ bindHookCheck(AllowedActionsEnum.list, 'beforeDatasourceRequest');
52
+ // Show check
53
+ bindHookCheck(AllowedActionsEnum.show, 'beforeDatasourceRequest');
54
+ // Edit check
55
+ bindHookCheck(AllowedActionsEnum.edit, 'beforeDatasourceRequest');
56
+ // create check
57
+ bindHookCheck(AllowedActionsEnum.create, 'beforeSave');
58
+ // edit check
59
+ bindHookCheck(AllowedActionsEnum.edit, 'beforeSave');
60
+ // delete check
61
+ bindHookCheck(AllowedActionsEnum.delete, 'beforeSave');
62
+ console.log('resourceConfig', resourceConfig);
63
+ }
64
+ }
65
+ AccessControlPlugin.defaultError = 'Sorry, you do not have access to this resource.';
66
+ export default AccessControlPlugin;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ <script setup>
2
+ import VueDiff from 'vue-diff';
3
+ import { ref } from 'vue';
4
+ import 'vue-diff/dist/index.css';
5
+
6
+ const props = defineProps(['prev', 'current', 'meta']);
7
+
8
+ app.use(VueDiff);
9
+ </script>
10
+
11
+ <template>
12
+ <Diff
13
+ :mode="'split'"
14
+ :theme="'light'"
15
+ :language="'plaintext'"
16
+ :prev="props.prev"
17
+ :current="props.current"
18
+ />
19
+ </template>
20
+
@@ -0,0 +1,83 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import AdminForthPlugin from "../base.js";
11
+ class AuditLogPlugin extends AdminForthPlugin {
12
+ constructor(options) {
13
+ super(options, import.meta.url);
14
+ this.createLogRecord = (resource, action, data, user) => __awaiter(this, void 0, void 0, function* () {
15
+ var _a;
16
+ const recordIdFieldName = (_a = resource.columns.find((c) => c.primaryKey === true)) === null || _a === void 0 ? void 0 : _a.name;
17
+ const recordId = data[recordIdFieldName];
18
+ const record = {
19
+ [this.options.resourceColumns.resourceIdColumnName]: resource.resourceId,
20
+ [this.options.resourceColumns.resourceActionColumnName]: action,
21
+ [this.options.resourceColumns.resourceDataColumnName]: data,
22
+ [this.options.resourceColumns.resourceUserIdColumnName]: user.pk,
23
+ [this.options.resourceColumns.resourceRecordIdColumnName]: recordId,
24
+ [this.options.resourceColumns.resourceCreatedColumnName]: new Date()
25
+ };
26
+ const auditLogResource = this.adminforth.config.resources.find((r) => r.resourceId === this.auditLogResource);
27
+ console.log('rec', record);
28
+ yield this.adminforth.createResourceRecord({ resource: auditLogResource, record, adminUser: user });
29
+ return { ok: true };
30
+ });
31
+ this.options = options;
32
+ }
33
+ modifyResourceConfig(adminforth, resourceConfig) {
34
+ super.modifyResourceConfig(adminforth, resourceConfig);
35
+ this.adminforth = adminforth;
36
+ this.auditLogResource = resourceConfig.resourceId;
37
+ this.adminforth.config.resources.forEach((resource) => {
38
+ var _a;
39
+ if ((_a = this.options.excludeResourceIds) === null || _a === void 0 ? void 0 : _a.includes(resource.resourceId)) {
40
+ return;
41
+ }
42
+ if (this.auditLogResource === resource.resourceId) {
43
+ let diffColumn = resource.columns.find((c) => c.name === this.options.resourceColumns.resourceDataColumnName);
44
+ if (!diffColumn) {
45
+ diffColumn = {
46
+ name: this.options.resourceColumns.resourceDataColumnName,
47
+ components: {},
48
+ // type: AdminForthDataTypes.STRING
49
+ };
50
+ }
51
+ diffColumn.components = {
52
+ showRow: {
53
+ file: this.componentPath('AuditLogView.vue'),
54
+ meta: Object.assign(Object.assign({}, this.options), { pluginInstanceId: this.pluginInstanceId })
55
+ }
56
+ };
57
+ resource.columns.push(diffColumn);
58
+ return;
59
+ }
60
+ const defaultHooks = {
61
+ create: { beforeSave: [] },
62
+ edit: { beforeSave: [] },
63
+ delete: { beforeSave: [] }
64
+ };
65
+ if (!resource.hooks) {
66
+ resource.hooks = defaultHooks;
67
+ }
68
+ else {
69
+ resource.hooks = Object.assign(Object.assign({}, defaultHooks), resource.hooks);
70
+ }
71
+ Object.keys(resource.hooks).forEach((hook) => {
72
+ if (!Array.isArray(resource.hooks[hook].beforeSave)) {
73
+ resource.hooks[hook].beforeSave = [resource.hooks[hook].beforeSave];
74
+ }
75
+ resource.hooks[hook].beforeSave.push((_a) => __awaiter(this, [_a], void 0, function* ({ resource, record, adminUser }) {
76
+ return yield this.createLogRecord(resource, hook, record, adminUser);
77
+ }));
78
+ });
79
+ });
80
+ }
81
+ }
82
+ AuditLogPlugin.defaultError = 'Sorry, you do not have access to this resource.';
83
+ export default AuditLogPlugin;
@@ -0,0 +1 @@
1
+ export {};
@@ -2,9 +2,9 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <link rel="icon" href="/* IMPORTANT:ADMINFORTH FAVICON */">
5
+ <link rel="icon" href="/favicon.ico">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>/* IMPORTANT:ADMINFORTH TITLE */</title>
7
+ <title>Vite App</title>
8
8
  <!--
9
9
  <script>
10
10
  // On page load or when changing themes, best to add inline in `head` to avoid FOUC