adminforth 1.1.59 → 1.1.60
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 +68 -63
- package/dist/modules/styleGenerator.js +22 -18
- package/dist/modules/styles.js +44 -36
- package/dist/modules/utils.js +74 -0
- package/dist/plugins/AccessControl/index.js +66 -0
- package/dist/plugins/AccessControl/types.js +1 -0
- package/dist/plugins/AuditLogPlugin/custom/AuditLogView.vue +20 -0
- package/dist/plugins/AuditLogPlugin/index.js +83 -0
- package/dist/plugins/AuditLogPlugin/types.js +1 -0
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +4 -17
- package/dist/spa/package.json +1 -2
- package/dist/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/package-lock.json +111 -0
- package/dist/spa/spa/package.json +1 -0
- package/dist/spa/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/src/App.vue +4 -4
- package/dist/spa/spa/src/components/MenuLink.vue +1 -1
- package/dist/spa/spa/src/components/ResourceForm.vue +1 -1
- package/dist/spa/src/App.vue +45 -125
- package/dist/spa/src/assets/logo.svg +1 -19
- package/dist/spa/src/components/AcceptModal.vue +9 -2
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +3 -16
- package/dist/spa/src/components/CustomDateRangePicker.vue +2 -11
- package/dist/spa/src/components/Dropdown.vue +1 -6
- package/dist/spa/src/components/Filters.vue +19 -40
- package/dist/spa/src/components/ResourceForm.vue +24 -26
- package/dist/spa/src/components/ValueRenderer.vue +8 -14
- package/dist/spa/src/main.ts +1 -1
- package/dist/spa/src/router/index.ts +20 -30
- package/dist/spa/src/stores/core.ts +31 -48
- package/dist/spa/src/stores/modal.ts +3 -13
- package/dist/spa/src/utils.ts +7 -62
- package/dist/spa/src/views/CreateView.vue +15 -67
- package/dist/spa/src/views/EditView.vue +11 -45
- package/dist/spa/src/views/ListView.vue +366 -82
- package/dist/spa/src/views/LoginView.vue +4 -16
- package/dist/spa/src/views/ShowView.vue +21 -105
- package/dist/spa/tailwind.config.js +1 -1
- package/dist/spa/vite.config.ts +0 -6
- package/dist/types.js +30 -0
- package/index.ts +62 -61
- package/modules/styleGenerator.ts +25 -18
- package/modules/styles.ts +46 -36
- package/modules/utils.ts +94 -1
- package/package.json +2 -1
- package/plugins/AuditLogPlugin/custom/AuditLogView.vue +20 -0
- package/plugins/AuditLogPlugin/index.ts +99 -0
- package/plugins/AuditLogPlugin/types.ts +40 -0
- package/spa/package-lock.json +111 -0
- package/spa/package.json +1 -0
- package/spa/src/App.vue +4 -4
- package/spa/src/components/MenuLink.vue +1 -1
- package/spa/src/components/ResourceForm.vue +1 -1
- package/types/AdminForthConfig.ts +6 -1
- package/dist/spa/src/components/ResourceListTable.vue +0 -419
- package/dist/spa/src/components/Toast.vue +0 -66
- package/dist/spa/src/composables/useFrontendApi.ts +0 -26
- package/dist/spa/src/composables/useStores.ts +0 -113
- package/dist/spa/src/spa_types/core.ts +0 -51
- package/dist/spa/src/stores/filters.ts +0 -22
- package/dist/spa/src/stores/toast.ts +0 -15
- package/dist/spa/src/stores/user.ts +0 -54
- package/plugins/AuditLog/index.ts +0 -29
- package/plugins/AuditLog/types.ts +0 -4
- /package/dist/spa/{public/assets → spa/public}/favicon.png +0 -0
package/modules/utils.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
+
var csscolors = require('css-color-names');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
4
9
|
|
|
5
10
|
|
|
6
11
|
export function guessLabelFromName(name) {
|
|
@@ -84,4 +89,92 @@ export function transformObject(obj, parentKey = '', result = {}) {
|
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
return result;
|
|
87
|
-
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
function hexToRGBA(hex,opacity){
|
|
96
|
+
var c;
|
|
97
|
+
if(/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)){
|
|
98
|
+
c= hex.substring(1).split('');
|
|
99
|
+
if(c.length== 3){
|
|
100
|
+
c= [c[0], c[0], c[1], c[1], c[2], c[2]];
|
|
101
|
+
}
|
|
102
|
+
c= '0x'+c.join('');
|
|
103
|
+
return 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',') + ',' + opacity + ')';
|
|
104
|
+
}
|
|
105
|
+
throw new Error('Bad Hex');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function createRGBA(color:string, opacity:number) {
|
|
109
|
+
if(!color) return
|
|
110
|
+
else if (color.startsWith("rgba")) {
|
|
111
|
+
//add opacity to existing rgba color
|
|
112
|
+
const rgb = color.match(/[\d.]+/g);
|
|
113
|
+
console.log('rgb',rgb)
|
|
114
|
+
return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${Number(rgb[3]) * opacity})`;
|
|
115
|
+
}
|
|
116
|
+
else if (color.startsWith("rgb")) {
|
|
117
|
+
const rgb = color.match(/\d+/g);
|
|
118
|
+
return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${opacity})`;
|
|
119
|
+
}
|
|
120
|
+
else if (color.startsWith("#")) {
|
|
121
|
+
return hexToRGBA(color, opacity);
|
|
122
|
+
}
|
|
123
|
+
else if (csscolors[color]) {
|
|
124
|
+
return hexToRGBA(csscolors[color], opacity);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
return color;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function parseColorForAliases(str){
|
|
132
|
+
const aliasRegex = /alias:([^ ]+)/;
|
|
133
|
+
const opacityRegex = /opacity:([0-9.]+)/;
|
|
134
|
+
const darkenRegex = /darken/;
|
|
135
|
+
const lightenRegex = /lighten/;
|
|
136
|
+
|
|
137
|
+
// Extract alias and properties
|
|
138
|
+
const aliasMatch = str.match(aliasRegex);
|
|
139
|
+
const opacityMatch = str.match(opacityRegex)
|
|
140
|
+
const darkenMatch = str.match(darkenRegex)
|
|
141
|
+
const lightenMatch = str.match(lightenRegex)
|
|
142
|
+
return {aliasMatch,opacityMatch,darkenMatch,lightenMatch}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function darkenRGBA(rgba) {
|
|
146
|
+
let amount = 0.2
|
|
147
|
+
// Extract the RGBA components
|
|
148
|
+
let [r, g, b, a] = rgba.match(/\d+/g).map(Number);
|
|
149
|
+
|
|
150
|
+
// Ensure amount is between 0 and 1
|
|
151
|
+
amount = Math.max(0, Math.min(1, amount));
|
|
152
|
+
|
|
153
|
+
// Calculate the new RGB values
|
|
154
|
+
r = Math.max(0, r * (1 - amount));
|
|
155
|
+
g = Math.max(0, g * (1 - amount));
|
|
156
|
+
b = Math.max(0, b * (1 - amount));
|
|
157
|
+
|
|
158
|
+
// Return the new RGBA color
|
|
159
|
+
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function lightenRGBA(rgba) {
|
|
163
|
+
let amount = 0.2
|
|
164
|
+
// Extract the RGBA components
|
|
165
|
+
let [r, g, b, a] = rgba.match(/\d+/g).map(Number);
|
|
166
|
+
|
|
167
|
+
// Ensure amount is between 0 and 1
|
|
168
|
+
amount = Math.max(0, Math.min(1, amount));
|
|
169
|
+
|
|
170
|
+
// Calculate the new RGB values
|
|
171
|
+
r = Math.min(255, r * (1 + amount));
|
|
172
|
+
g = Math.min(255, g * (1 + amount));
|
|
173
|
+
b = Math.min(255, b * (1 + amount));
|
|
174
|
+
|
|
175
|
+
// Return the new RGBA color
|
|
176
|
+
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adminforth",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.60",
|
|
4
4
|
"description": "OpenSource Vue3 powered forth-generation admin panel",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"better-sqlite3": "^10.0.0",
|
|
19
19
|
"crypto": "^1.0.1",
|
|
20
|
+
"css-color-names": "^1.0.1",
|
|
20
21
|
"dayjs": "^1.11.11",
|
|
21
22
|
"express": "^4.19.2",
|
|
22
23
|
"filewatcher": "^3.0.1",
|
|
@@ -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,99 @@
|
|
|
1
|
+
import AdminForth from "../../index.js";
|
|
2
|
+
import {
|
|
3
|
+
AdminForthResource, AdminForthClass, BeforeDataSourceRequestFunction,
|
|
4
|
+
AllowedActionsEnum,
|
|
5
|
+
BeforeSaveFunction,
|
|
6
|
+
AdminUser,
|
|
7
|
+
AdminForthDataTypes
|
|
8
|
+
} from "../../types/AdminForthConfig.js";
|
|
9
|
+
import AdminForthPlugin from "../base.js";
|
|
10
|
+
import { PluginOptions } from "./types.js";
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export default class AuditLogPlugin extends AdminForthPlugin {
|
|
15
|
+
|
|
16
|
+
options: PluginOptions;
|
|
17
|
+
adminforth: AdminForthClass;
|
|
18
|
+
auditLogResource: string;
|
|
19
|
+
|
|
20
|
+
constructor(options: PluginOptions) {
|
|
21
|
+
super(options, import.meta.url);
|
|
22
|
+
this.options = options;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static defaultError = 'Sorry, you do not have access to this resource.'
|
|
26
|
+
|
|
27
|
+
createLogRecord = async (resource: AdminForthResource, action: AllowedActionsEnum, data: Object, user: AdminUser) => {
|
|
28
|
+
const recordIdFieldName = resource.columns.find((c) => c.primaryKey === true)?.name;
|
|
29
|
+
const recordId = data[recordIdFieldName];
|
|
30
|
+
const record = {
|
|
31
|
+
[this.options.resourceColumns.resourceIdColumnName]: resource.resourceId,
|
|
32
|
+
[this.options.resourceColumns.resourceActionColumnName]: action,
|
|
33
|
+
[this.options.resourceColumns.resourceDataColumnName]: data,
|
|
34
|
+
[this.options.resourceColumns.resourceUserIdColumnName]: user.pk,
|
|
35
|
+
[this.options.resourceColumns.resourceRecordIdColumnName]: recordId,
|
|
36
|
+
[this.options.resourceColumns.resourceCreatedColumnName]: new Date()
|
|
37
|
+
}
|
|
38
|
+
const auditLogResource = this.adminforth.config.resources.find((r) => r.resourceId === this.auditLogResource);
|
|
39
|
+
console.log('rec', record);
|
|
40
|
+
await this.adminforth.createResourceRecord({ resource: auditLogResource, record, adminUser: user});
|
|
41
|
+
return {ok: true};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
modifyResourceConfig(adminforth: AdminForthClass, resourceConfig: AdminForthResource) {
|
|
45
|
+
super.modifyResourceConfig(adminforth, resourceConfig);
|
|
46
|
+
this.adminforth = adminforth;
|
|
47
|
+
this.auditLogResource = resourceConfig.resourceId;
|
|
48
|
+
|
|
49
|
+
this.adminforth.config.resources.forEach((resource) => {
|
|
50
|
+
if (this.options.excludeResourceIds?.includes(resource.resourceId)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (this.auditLogResource === resource.resourceId) {
|
|
55
|
+
let diffColumn = resource.columns.find((c) => c.name === this.options.resourceColumns.resourceDataColumnName);
|
|
56
|
+
if (!diffColumn) {
|
|
57
|
+
diffColumn = {
|
|
58
|
+
name: this.options.resourceColumns.resourceDataColumnName,
|
|
59
|
+
components: {},
|
|
60
|
+
// type: AdminForthDataTypes.STRING
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
diffColumn.components = {
|
|
65
|
+
showRow: {
|
|
66
|
+
file: this.componentPath('AuditLogView.vue'),
|
|
67
|
+
meta: {
|
|
68
|
+
...this.options,
|
|
69
|
+
pluginInstanceId: this.pluginInstanceId
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
resource.columns.push(diffColumn);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const defaultHooks = {
|
|
78
|
+
create: { beforeSave: [] },
|
|
79
|
+
edit: { beforeSave: [] },
|
|
80
|
+
delete: { beforeSave: [] }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if ( !resource.hooks ) {
|
|
84
|
+
resource.hooks = defaultHooks;
|
|
85
|
+
} else {
|
|
86
|
+
resource.hooks = {...defaultHooks, ...resource.hooks}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Object.keys(resource.hooks).forEach((hook) => {
|
|
90
|
+
if(!Array.isArray(resource.hooks[hook].beforeSave)){
|
|
91
|
+
resource.hooks[hook].beforeSave = [resource.hooks[hook].beforeSave]
|
|
92
|
+
}
|
|
93
|
+
resource.hooks[hook].beforeSave.push(async ({resource, record, adminUser}) => {
|
|
94
|
+
return await this.createLogRecord(resource, hook as AllowedActionsEnum, record, adminUser)
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type PluginOptions = {
|
|
2
|
+
/**
|
|
3
|
+
* Id of the resource to be ignored from audit log.
|
|
4
|
+
*/
|
|
5
|
+
excludeResourceIds?: string[];
|
|
6
|
+
/**
|
|
7
|
+
* Mappping of columns for audit log table.
|
|
8
|
+
*/
|
|
9
|
+
resourceColumns: {
|
|
10
|
+
/**
|
|
11
|
+
* Should be name of column were plugin will store resourceId.
|
|
12
|
+
* Column should be of type string.
|
|
13
|
+
*/
|
|
14
|
+
resourceIdColumnName: string
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Should be name of column were plugin will store action.
|
|
18
|
+
* Column should be of type string.
|
|
19
|
+
*/
|
|
20
|
+
resourceActionColumnName: string
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Should be name of column were plugin will store data.
|
|
24
|
+
* Column should be of type string.
|
|
25
|
+
*/
|
|
26
|
+
resourceDataColumnName: string
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Should be name of column were plugin will store user id.
|
|
30
|
+
* Column should be same type as type of column where user id is stored.
|
|
31
|
+
* E.g. is user id is stored as UUID, then this column should be of type UUID.
|
|
32
|
+
*/
|
|
33
|
+
resourceUserIdColumnName: string
|
|
34
|
+
|
|
35
|
+
resourceRecordIdColumnName: string
|
|
36
|
+
|
|
37
|
+
resourceCreatedColumnName: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
package/spa/package-lock.json
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"unhead": "^1.9.12",
|
|
20
20
|
"uuid": "^10.0.0",
|
|
21
21
|
"vue": "^3.4.21",
|
|
22
|
+
"vue-diff": "^1.2.4",
|
|
22
23
|
"vue-router": "^4.3.0",
|
|
23
24
|
"vue-slider-component": "^4.1.0-beta.7"
|
|
24
25
|
},
|
|
@@ -1902,6 +1903,11 @@
|
|
|
1902
1903
|
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
|
1903
1904
|
"dev": true
|
|
1904
1905
|
},
|
|
1906
|
+
"node_modules/diff-match-patch": {
|
|
1907
|
+
"version": "1.0.5",
|
|
1908
|
+
"resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
|
|
1909
|
+
"integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="
|
|
1910
|
+
},
|
|
1905
1911
|
"node_modules/dir-glob": {
|
|
1906
1912
|
"version": "3.0.1",
|
|
1907
1913
|
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
|
@@ -2526,6 +2532,14 @@
|
|
|
2526
2532
|
"he": "bin/he"
|
|
2527
2533
|
}
|
|
2528
2534
|
},
|
|
2535
|
+
"node_modules/highlight.js": {
|
|
2536
|
+
"version": "11.9.0",
|
|
2537
|
+
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz",
|
|
2538
|
+
"integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==",
|
|
2539
|
+
"engines": {
|
|
2540
|
+
"node": ">=12.0.0"
|
|
2541
|
+
}
|
|
2542
|
+
},
|
|
2529
2543
|
"node_modules/hookable": {
|
|
2530
2544
|
"version": "5.5.3",
|
|
2531
2545
|
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
|
@@ -4120,6 +4134,103 @@
|
|
|
4120
4134
|
}
|
|
4121
4135
|
}
|
|
4122
4136
|
},
|
|
4137
|
+
"node_modules/vue-diff": {
|
|
4138
|
+
"version": "1.2.4",
|
|
4139
|
+
"resolved": "https://registry.npmjs.org/vue-diff/-/vue-diff-1.2.4.tgz",
|
|
4140
|
+
"integrity": "sha512-9z+0uPHigi/DdE3Zhdg1zR3VkiBaDN4czjWa/Zznvizn4znckVqz20p1KytaWq5NJtJ7UnkqRbuWsaCuC1ZHaA==",
|
|
4141
|
+
"dependencies": {
|
|
4142
|
+
"@vueuse/core": "^8.3.1",
|
|
4143
|
+
"diff-match-patch": "^1.0.5",
|
|
4144
|
+
"highlight.js": "^11.5.1"
|
|
4145
|
+
}
|
|
4146
|
+
},
|
|
4147
|
+
"node_modules/vue-diff/node_modules/@types/web-bluetooth": {
|
|
4148
|
+
"version": "0.0.14",
|
|
4149
|
+
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.14.tgz",
|
|
4150
|
+
"integrity": "sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A=="
|
|
4151
|
+
},
|
|
4152
|
+
"node_modules/vue-diff/node_modules/@vueuse/core": {
|
|
4153
|
+
"version": "8.9.4",
|
|
4154
|
+
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.9.4.tgz",
|
|
4155
|
+
"integrity": "sha512-B/Mdj9TK1peFyWaPof+Zf/mP9XuGAngaJZBwPaXBvU3aCTZlx3ltlrFFFyMV4iGBwsjSCeUCgZrtkEj9dS2Y3Q==",
|
|
4156
|
+
"dependencies": {
|
|
4157
|
+
"@types/web-bluetooth": "^0.0.14",
|
|
4158
|
+
"@vueuse/metadata": "8.9.4",
|
|
4159
|
+
"@vueuse/shared": "8.9.4",
|
|
4160
|
+
"vue-demi": "*"
|
|
4161
|
+
},
|
|
4162
|
+
"funding": {
|
|
4163
|
+
"url": "https://github.com/sponsors/antfu"
|
|
4164
|
+
},
|
|
4165
|
+
"peerDependencies": {
|
|
4166
|
+
"@vue/composition-api": "^1.1.0",
|
|
4167
|
+
"vue": "^2.6.0 || ^3.2.0"
|
|
4168
|
+
},
|
|
4169
|
+
"peerDependenciesMeta": {
|
|
4170
|
+
"@vue/composition-api": {
|
|
4171
|
+
"optional": true
|
|
4172
|
+
},
|
|
4173
|
+
"vue": {
|
|
4174
|
+
"optional": true
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
},
|
|
4178
|
+
"node_modules/vue-diff/node_modules/@vueuse/metadata": {
|
|
4179
|
+
"version": "8.9.4",
|
|
4180
|
+
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.9.4.tgz",
|
|
4181
|
+
"integrity": "sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw==",
|
|
4182
|
+
"funding": {
|
|
4183
|
+
"url": "https://github.com/sponsors/antfu"
|
|
4184
|
+
}
|
|
4185
|
+
},
|
|
4186
|
+
"node_modules/vue-diff/node_modules/@vueuse/shared": {
|
|
4187
|
+
"version": "8.9.4",
|
|
4188
|
+
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.9.4.tgz",
|
|
4189
|
+
"integrity": "sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag==",
|
|
4190
|
+
"dependencies": {
|
|
4191
|
+
"vue-demi": "*"
|
|
4192
|
+
},
|
|
4193
|
+
"funding": {
|
|
4194
|
+
"url": "https://github.com/sponsors/antfu"
|
|
4195
|
+
},
|
|
4196
|
+
"peerDependencies": {
|
|
4197
|
+
"@vue/composition-api": "^1.1.0",
|
|
4198
|
+
"vue": "^2.6.0 || ^3.2.0"
|
|
4199
|
+
},
|
|
4200
|
+
"peerDependenciesMeta": {
|
|
4201
|
+
"@vue/composition-api": {
|
|
4202
|
+
"optional": true
|
|
4203
|
+
},
|
|
4204
|
+
"vue": {
|
|
4205
|
+
"optional": true
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
4208
|
+
},
|
|
4209
|
+
"node_modules/vue-diff/node_modules/vue-demi": {
|
|
4210
|
+
"version": "0.14.8",
|
|
4211
|
+
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
|
|
4212
|
+
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
|
|
4213
|
+
"hasInstallScript": true,
|
|
4214
|
+
"bin": {
|
|
4215
|
+
"vue-demi-fix": "bin/vue-demi-fix.js",
|
|
4216
|
+
"vue-demi-switch": "bin/vue-demi-switch.js"
|
|
4217
|
+
},
|
|
4218
|
+
"engines": {
|
|
4219
|
+
"node": ">=12"
|
|
4220
|
+
},
|
|
4221
|
+
"funding": {
|
|
4222
|
+
"url": "https://github.com/sponsors/antfu"
|
|
4223
|
+
},
|
|
4224
|
+
"peerDependencies": {
|
|
4225
|
+
"@vue/composition-api": "^1.0.0-rc.1",
|
|
4226
|
+
"vue": "^3.0.0-0 || ^2.6.0"
|
|
4227
|
+
},
|
|
4228
|
+
"peerDependenciesMeta": {
|
|
4229
|
+
"@vue/composition-api": {
|
|
4230
|
+
"optional": true
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
},
|
|
4123
4234
|
"node_modules/vue-eslint-parser": {
|
|
4124
4235
|
"version": "9.4.2",
|
|
4125
4236
|
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz",
|
package/spa/package.json
CHANGED
package/spa/src/App.vue
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<div>
|
|
20
20
|
<button type="button" class="flex text-sm bg- rounded-full focus:ring-4 focus:ring-lightSidebarDevider dark:focus:ring-darkSidebarDevider dark:bg-" aria-expanded="false" data-dropdown-toggle="dropdown-user">
|
|
21
21
|
<span class="sr-only">Open user menu</span>
|
|
22
|
-
<svg class="w-8 h-8 text-
|
|
22
|
+
<svg class="w-8 h-8 text-lightNavbarIcons dark:text-darkNavbarIcons" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
|
|
23
23
|
<path fill-rule="evenodd" d="M12 20a7.966 7.966 0 0 1-5.002-1.756l.002.001v-.683c0-1.794 1.492-3.25 3.333-3.25h3.334c1.84 0 3.333 1.456 3.333 3.25v.683A7.966 7.966 0 0 1 12 20ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10c0 5.5-4.44 9.963-9.932 10h-.138C6.438 21.962 2 17.5 2 12Zm10-5c-1.84 0-3.333 1.455-3.333 3.25S10.159 13.5 12 13.5c1.84 0 3.333-1.455 3.333-3.25S13.841 7 12 7Z" clip-rule="evenodd"/>
|
|
24
24
|
</svg>
|
|
25
25
|
</button>
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
:class="{ '-translate-x-full': !sideBarOpen, 'transform-none': sideBarOpen }"
|
|
64
64
|
aria-label="Sidebar"
|
|
65
65
|
>
|
|
66
|
-
<div class="h-full px-3 pb-4 overflow-y-auto bg-lightSidebar dark:bg-darkSidebar border-r dark:border-darkSidebarBorder">
|
|
66
|
+
<div class="h-full px-3 pb-4 overflow-y-auto bg-lightSidebar dark:bg-darkSidebar border-r border-lightSidebarBorder dark:border-darkSidebarBorder">
|
|
67
67
|
<div class="flex ms-2 md:me-24 m-4 ">
|
|
68
68
|
<img :src="loadFile(coreStore.config?.brandLogo || '@/assets/logo.svg')" :alt="`${ coreStore.config?.brandName } Logo`" class="h-8 me-3" />
|
|
69
|
-
<span class="self-center text-lightNavbarText-size font-semibold sm:text-lightNavbarText-size whitespace-nowrap dark:text-
|
|
69
|
+
<span class="self-center text-lightNavbarText-size font-semibold sm:text-lightNavbarText-size whitespace-nowrap dark:text-darkSidebarText text-lightSidebarText">
|
|
70
70
|
{{ coreStore.config?.brandName }}
|
|
71
71
|
</span>
|
|
72
72
|
</div>
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
<template v-for="(item, i) in coreStore.menu" :key="`menu-${i}`">
|
|
76
76
|
<div v-if="item.type === 'divider'" class="border-t border-lightSidebarDevider dark:border-darkSidebarDevider"></div>
|
|
77
77
|
<div v-else-if="item.type === 'gap'" class="flex items-center justify-center h-8"></div>
|
|
78
|
-
<div v-else-if="item.type === 'heading'" class="flex items-center justify-left pl-2 h-8 text-
|
|
78
|
+
<div v-else-if="item.type === 'heading'" class="flex items-center justify-left pl-2 h-8 text-lightSidebarHeading dark:text-darkSidebarHeading
|
|
79
79
|
">{{ item.label }}</div>
|
|
80
80
|
<li v-else-if="item.children" class=" ">
|
|
81
81
|
<button @click="clickOnMenuItem(i)" type="button" class="flex items-center w-full p-2 text-base text-lightSidebarText rounded-default transition duration-75 group hover:bg-lightSidebarItemHover hover:text-lightSidebarTextHover dark:text-darkSidebarText dark:hover:bg-darkSidebarHover dark:hover:text-darkSidebarTextHover"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<RouterLink
|
|
3
3
|
:to="{name: item.resourceId ? 'resource-list' : item.path, params: item.resourceId ? { resourceId: item.resourceId }: {}}"
|
|
4
|
-
class="flex group items-center py-2 text-lightSidebarText dark:text-darkSidebarText rounded-default hover:bg-lightSidebarItemHover hover:text-lightSidebarTextHover dark:hover:bg-
|
|
4
|
+
class="flex group items-center py-2 text-lightSidebarText dark:text-darkSidebarText rounded-default hover:bg-lightSidebarItemHover hover:text-lightSidebarTextHover dark:hover:bg-darkSidebarItemHover dark:hover:text-darkSidebarTextHover active:bg-lightSidebarActive dark:active:bg-darkSidebarHover" role="menuitem"
|
|
5
5
|
:class="{
|
|
6
6
|
'px-4': isChild,
|
|
7
7
|
'px-2': !isChild,
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
class="bg-ligftForm dark:bg-gray-800 border-b dark:border-gray-700"
|
|
22
22
|
>
|
|
23
23
|
<!-- if column is in customComponentsPerColumn, use this component. If not, use this code -->
|
|
24
|
-
<template v-if="column?.components?.[props.source]
|
|
24
|
+
<template v-if="column?.components?.[props.source]?.file">
|
|
25
25
|
<component
|
|
26
26
|
:is="getCustomComponent(column.components[props.source])"
|
|
27
27
|
:column="column"
|
|
@@ -83,6 +83,11 @@ export interface AdminForthClass {
|
|
|
83
83
|
codeInjector: CodeInjectorType;
|
|
84
84
|
express: GenericHttpServer;
|
|
85
85
|
|
|
86
|
+
connectors: {
|
|
87
|
+
[key: string]: AdminForthDataSource
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
createResourceRecord(params: { resource: AdminForthResource, record: any, adminUser: AdminUser }): Promise<any>;
|
|
86
91
|
|
|
87
92
|
auth: {
|
|
88
93
|
|
|
@@ -461,7 +466,7 @@ export type AdminForthResource = {
|
|
|
461
466
|
* If you wish you can explicitly set it to any string.
|
|
462
467
|
* We added to support cases when 2 datasources have tables with the same name.
|
|
463
468
|
*/
|
|
464
|
-
resourceId
|
|
469
|
+
resourceId?: string,
|
|
465
470
|
|
|
466
471
|
/**
|
|
467
472
|
* Label for resource which will be displayed in the admin panel.
|