adminforth 1.2.98 → 1.2.100
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/auth.ts +6 -5
- package/dataConnectors/baseConnector.ts +91 -21
- package/dataConnectors/clickhouse.ts +58 -37
- package/dataConnectors/mongo.ts +44 -14
- package/dataConnectors/postgres.ts +53 -36
- package/dataConnectors/sqlite.ts +44 -35
- package/dist/auth.js +6 -6
- package/dist/dataConnectors/baseConnector.js +75 -17
- package/dist/dataConnectors/clickhouse.js +59 -48
- package/dist/dataConnectors/mongo.js +28 -12
- package/dist/dataConnectors/postgres.js +62 -52
- package/dist/dataConnectors/sqlite.js +62 -45
- package/dist/index.js +52 -33
- package/dist/modules/configValidator.js +25 -16
- package/dist/modules/operationalResource.js +88 -0
- package/dist/modules/restApi.js +106 -96
- package/dist/modules/utils.js +16 -0
- package/dist/types/AdminForthConfig.js +37 -0
- package/index.ts +76 -43
- package/modules/configValidator.ts +26 -21
- package/modules/operationalResource.ts +91 -0
- package/modules/restApi.ts +91 -80
- package/modules/utils.ts +20 -0
- package/package.json +3 -2
- package/spa/src/App.vue +12 -9
- package/spa/src/components/Filters.vue +1 -1
- package/spa/src/components/ResourceForm.vue +7 -4
- package/spa/src/components/ResourceListTable.vue +124 -112
- package/spa/src/components/SkeleteLoader.vue +4 -4
- package/spa/src/components/ValueRenderer.vue +1 -1
- package/spa/src/router/index.ts +0 -8
- package/spa/src/spa_types/core.ts +1 -0
- package/spa/src/stores/filters.ts +3 -2
- package/spa/src/views/ListView.vue +16 -25
- package/spa/src/views/LoginView.vue +26 -9
- package/types/AdminForthConfig.ts +140 -38
package/modules/restApi.ts
CHANGED
|
@@ -67,73 +67,64 @@ export default class AdminForthRestAPI {
|
|
|
67
67
|
let adminUser: AdminUser;
|
|
68
68
|
let toReturn: { ok: boolean, redirectTo?: string, allowedLogin:boolean } = { ok: true, allowedLogin:true};
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
if (this.adminforth.config.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation as (BeforeLoginConfirmationFunction[] | undefined);
|
|
119
|
-
if (beforeLoginConfirmation?.length){
|
|
120
|
-
for (const hook of beforeLoginConfirmation) {
|
|
121
|
-
const resp = await hook({ adminUser, response });
|
|
122
|
-
|
|
123
|
-
if (resp?.body?.redirectTo) {
|
|
124
|
-
toReturn = {ok:resp.ok, redirectTo:resp?.body?.redirectTo, allowedLogin:resp?.body?.allowedLogin};
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
70
|
+
// get resource from db
|
|
71
|
+
if (!this.adminforth.config.auth) {
|
|
72
|
+
throw new Error('No config.auth defined we need it to find user, please follow the docs');
|
|
73
|
+
}
|
|
74
|
+
const userResource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
|
|
75
|
+
// if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
|
|
76
|
+
if (!userResource.dataSourceColumns.find((col) => col.name === this.adminforth.config.auth.passwordHashField)) {
|
|
77
|
+
userResource.dataSourceColumns.push({
|
|
78
|
+
name: this.adminforth.config.auth.passwordHashField,
|
|
79
|
+
backendOnly: true,
|
|
80
|
+
showIn: [],
|
|
81
|
+
type: AdminForthDataTypes.STRING,
|
|
82
|
+
});
|
|
83
|
+
console.log('Adding passwordHashField to userResource', userResource)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const userRecord = (
|
|
87
|
+
await this.adminforth.connectors[userResource.dataSource].getData({
|
|
88
|
+
resource: userResource,
|
|
89
|
+
filters: [
|
|
90
|
+
{ field: this.adminforth.config.auth.usernameField, operator: AdminForthFilterOperators.EQ, value: username },
|
|
91
|
+
],
|
|
92
|
+
limit: 1,
|
|
93
|
+
offset: 0,
|
|
94
|
+
sort: [],
|
|
95
|
+
})
|
|
96
|
+
).data?.[0];
|
|
97
|
+
|
|
98
|
+
if (!userRecord) {
|
|
99
|
+
return { error: 'User not found' };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const passwordHash = userRecord[this.adminforth.config.auth.passwordHashField];
|
|
103
|
+
const valid = await AdminForthAuth.verifyPassword(password, passwordHash);
|
|
104
|
+
if (valid) {
|
|
105
|
+
adminUser = {
|
|
106
|
+
dbUser: userRecord,
|
|
107
|
+
pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
|
|
108
|
+
username,
|
|
109
|
+
};
|
|
110
|
+
const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation as (BeforeLoginConfirmationFunction[] | undefined);
|
|
111
|
+
if (beforeLoginConfirmation?.length){
|
|
112
|
+
for (const hook of beforeLoginConfirmation) {
|
|
113
|
+
const resp = await hook({ adminUser, response });
|
|
114
|
+
|
|
115
|
+
if (resp?.body?.redirectTo) {
|
|
116
|
+
toReturn = {ok:resp.ok, redirectTo:resp?.body?.redirectTo, allowedLogin:resp?.body?.allowedLogin};
|
|
117
|
+
break;
|
|
127
118
|
}
|
|
128
119
|
}
|
|
129
|
-
if (toReturn.allowedLogin){
|
|
130
|
-
this.adminforth.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
|
|
131
|
-
}
|
|
132
|
-
} else {
|
|
133
|
-
return { error: INVALID_MESSAGE };
|
|
134
120
|
}
|
|
135
|
-
|
|
121
|
+
if (toReturn.allowedLogin){
|
|
122
|
+
this.adminforth.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
return { error: INVALID_MESSAGE };
|
|
136
126
|
}
|
|
127
|
+
|
|
137
128
|
|
|
138
129
|
return toReturn;
|
|
139
130
|
}
|
|
@@ -168,13 +159,14 @@ export default class AdminForthRestAPI {
|
|
|
168
159
|
throw new Error('No config.auth defined');
|
|
169
160
|
}
|
|
170
161
|
const usernameField = this.adminforth.config.auth.usernameField;
|
|
171
|
-
const resource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.
|
|
162
|
+
const resource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
|
|
172
163
|
const usernameColumn = resource.columns.find((col) => col.name === usernameField);
|
|
173
164
|
|
|
174
165
|
return {
|
|
175
166
|
brandName: this.adminforth.config.customization.brandName,
|
|
176
167
|
usernameFieldName: usernameColumn.label,
|
|
177
168
|
loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
|
|
169
|
+
loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
|
|
178
170
|
title: this.adminforth.config.customization?.title,
|
|
179
171
|
demoCredentials: this.adminforth.config.auth.demoCredentials,
|
|
180
172
|
loginPromptHTML: this.adminforth.config.auth.loginPromptHTML,
|
|
@@ -188,14 +180,10 @@ export default class AdminForthRestAPI {
|
|
|
188
180
|
handler: async ({input, adminUser, cookies}) => {
|
|
189
181
|
let username = ''
|
|
190
182
|
let userFullName = ''
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const dbUser = adminUser.dbUser;
|
|
196
|
-
username = dbUser[this.adminforth.config.auth.usernameField];
|
|
197
|
-
userFullName =dbUser[this.adminforth.config.auth.userFullNameField];
|
|
198
|
-
}
|
|
183
|
+
|
|
184
|
+
const dbUser = adminUser.dbUser;
|
|
185
|
+
username = dbUser[this.adminforth.config.auth.usernameField];
|
|
186
|
+
userFullName =dbUser[this.adminforth.config.auth.userFullNameField];
|
|
199
187
|
|
|
200
188
|
const userData = {
|
|
201
189
|
[this.adminforth.config.auth.usernameField]: username,
|
|
@@ -257,6 +245,7 @@ export default class AdminForthRestAPI {
|
|
|
257
245
|
menu: newMenu,
|
|
258
246
|
config: {
|
|
259
247
|
brandName: this.adminforth.config.customization.brandName,
|
|
248
|
+
showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
|
|
260
249
|
brandLogo: this.adminforth.config.customization.brandLogo,
|
|
261
250
|
datesFormat: this.adminforth.config.customization.datesFormat,
|
|
262
251
|
deleteConfirmation: this.adminforth.config.deleteConfirmation,
|
|
@@ -272,8 +261,6 @@ export default class AdminForthRestAPI {
|
|
|
272
261
|
},
|
|
273
262
|
});
|
|
274
263
|
|
|
275
|
-
|
|
276
|
-
|
|
277
264
|
function checkAccess(action: AllowedActionsEnum, allowedActions: AllowedActions): { allowed: boolean, error?: string } {
|
|
278
265
|
const allowed = (allowedActions[action] as boolean | string | undefined);
|
|
279
266
|
if (allowed !== true) {
|
|
@@ -331,6 +318,7 @@ export default class AdminForthRestAPI {
|
|
|
331
318
|
method: 'POST',
|
|
332
319
|
path: '/get_resource_data',
|
|
333
320
|
handler: async ({ body, adminUser }) => {
|
|
321
|
+
|
|
334
322
|
const { resourceId, source } = body;
|
|
335
323
|
if (['show', 'list'].includes(source) === false) {
|
|
336
324
|
return { error: 'Invalid source, should be list or show' };
|
|
@@ -394,6 +382,7 @@ export default class AdminForthRestAPI {
|
|
|
394
382
|
offset,
|
|
395
383
|
filters,
|
|
396
384
|
sort,
|
|
385
|
+
getTotals: true,
|
|
397
386
|
});
|
|
398
387
|
// for foreign keys, add references
|
|
399
388
|
await Promise.all(
|
|
@@ -581,12 +570,13 @@ export default class AdminForthRestAPI {
|
|
|
581
570
|
|
|
582
571
|
const response = await this.adminforth.createResourceRecord({ resource, record, adminUser });
|
|
583
572
|
if (response.error) {
|
|
584
|
-
return { error: response.error };
|
|
573
|
+
return { error: response.error, ok: false };
|
|
585
574
|
}
|
|
586
575
|
const connector = this.adminforth.connectors[resource.dataSource];
|
|
587
576
|
|
|
588
577
|
return {
|
|
589
|
-
newRecordId:
|
|
578
|
+
newRecordId: response.createdRecord[connector.getPrimaryKey(resource)],
|
|
579
|
+
ok: true
|
|
590
580
|
}
|
|
591
581
|
}
|
|
592
582
|
});
|
|
@@ -617,7 +607,12 @@ export default class AdminForthRestAPI {
|
|
|
617
607
|
|
|
618
608
|
// execute hook if needed
|
|
619
609
|
for (const hook of listify(resource.hooks?.edit?.beforeSave as BeforeSaveFunction[])) {
|
|
620
|
-
const resp = await hook({
|
|
610
|
+
const resp = await hook({
|
|
611
|
+
recordId,
|
|
612
|
+
resource,
|
|
613
|
+
record,
|
|
614
|
+
adminUser
|
|
615
|
+
});
|
|
621
616
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
622
617
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
623
618
|
}
|
|
@@ -647,7 +642,13 @@ export default class AdminForthRestAPI {
|
|
|
647
642
|
|
|
648
643
|
// execute hook if needed
|
|
649
644
|
for (const hook of listify(resource.hooks?.edit?.afterSave as AfterSaveFunction[])) {
|
|
650
|
-
const resp = await hook({
|
|
645
|
+
const resp = await hook({
|
|
646
|
+
resource,
|
|
647
|
+
record,
|
|
648
|
+
adminUser,
|
|
649
|
+
oldRecord,
|
|
650
|
+
recordId,
|
|
651
|
+
});
|
|
651
652
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
652
653
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
653
654
|
}
|
|
@@ -687,7 +688,12 @@ export default class AdminForthRestAPI {
|
|
|
687
688
|
|
|
688
689
|
// execute hook if needed
|
|
689
690
|
for (const hook of listify(resource.hooks?.delete?.beforeSave as BeforeSaveFunction[])) {
|
|
690
|
-
const resp = await hook({
|
|
691
|
+
const resp = await hook({
|
|
692
|
+
resource,
|
|
693
|
+
record,
|
|
694
|
+
adminUser,
|
|
695
|
+
recordId: body['primaryKey']
|
|
696
|
+
});
|
|
691
697
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
692
698
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
693
699
|
}
|
|
@@ -702,7 +708,12 @@ export default class AdminForthRestAPI {
|
|
|
702
708
|
|
|
703
709
|
// execute hook if needed
|
|
704
710
|
for (const hook of listify(resource.hooks?.delete?.afterSave as BeforeSaveFunction[])) {
|
|
705
|
-
const resp = await hook({
|
|
711
|
+
const resp = await hook({
|
|
712
|
+
resource,
|
|
713
|
+
record,
|
|
714
|
+
adminUser,
|
|
715
|
+
recordId: body['primaryKey']
|
|
716
|
+
});
|
|
706
717
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
707
718
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
708
719
|
}
|
package/modules/utils.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
+
import Fuse from 'fuse.js';
|
|
4
5
|
// @ts-ignore-next-line
|
|
5
6
|
|
|
6
7
|
|
|
@@ -335,3 +336,22 @@ export function inverseRGBA(rgba) {
|
|
|
335
336
|
return brightness > 128 ? 'rgba(0,0,0,1)' : 'rgba(255,255,255,1)';
|
|
336
337
|
}
|
|
337
338
|
|
|
339
|
+
|
|
340
|
+
export function suggestIfTypo(names: string[], name: string): string {
|
|
341
|
+
if (!name) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
const options = {
|
|
345
|
+
includeScore: true, // Includes score in the results to see how close matches are
|
|
346
|
+
threshold: 0.3, // Defines the fuzziness (lower values mean stricter matches)
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const fuse = new Fuse(names.filter(
|
|
350
|
+
(n) => !!n
|
|
351
|
+
), options);
|
|
352
|
+
// Search for a resource
|
|
353
|
+
const result = fuse.search(name);
|
|
354
|
+
if (result.length > 0) {
|
|
355
|
+
return result[0].item;
|
|
356
|
+
}
|
|
357
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adminforth",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.100",
|
|
4
4
|
"description": "OpenSource Vue3 powered forth-generation admin panel",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
"@clickhouse/client": "^1.4.0",
|
|
21
21
|
"better-sqlite3": "^10.0.0",
|
|
22
22
|
"dayjs": "^1.11.11",
|
|
23
|
-
"express": "^4.
|
|
23
|
+
"express": "^4.21.0",
|
|
24
24
|
"filewatcher": "^3.0.1",
|
|
25
25
|
"fs-extra": "^11.2.0",
|
|
26
|
+
"fuse.js": "^7.0.0",
|
|
26
27
|
"jsonwebtoken": "^9.0.2",
|
|
27
28
|
"mongodb": "6.6",
|
|
28
29
|
"node-fetch": "^3.3.2",
|
package/spa/src/App.vue
CHANGED
|
@@ -18,7 +18,12 @@
|
|
|
18
18
|
</div>
|
|
19
19
|
<div class="flex items-center">
|
|
20
20
|
<div class="flex items-center ms-3 ">
|
|
21
|
+
<span @click="toggleTheme" class="cursor-pointer flex items-center gap-1 block px-4 py-2 text-sm text-black hover:bg-lightHtml dark:text-darkSidebarTextHover dark:hover:bg-darkHtml dark:hover:text-darkSidebarTextActive" role="menuitem">
|
|
22
|
+
<IconMoonSolid class="w-5 h-5 text-blue-300" v-if="theme !== 'dark'" />
|
|
23
|
+
<IconSunSolid class="w-5 h-5 text-yellow-300" v-else />
|
|
24
|
+
</span>
|
|
21
25
|
<div>
|
|
26
|
+
|
|
22
27
|
<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">
|
|
23
28
|
<span class="sr-only">Open user menu</span>
|
|
24
29
|
<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">
|
|
@@ -36,14 +41,9 @@
|
|
|
36
41
|
</p>
|
|
37
42
|
</div>
|
|
38
43
|
<ul class="py-1" role="none">
|
|
39
|
-
<li>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<IconMoonSolid class="w-5 h-5 text-blue-300" v-if="theme !== 'dark'" />
|
|
43
|
-
<IconSunSolid class="w-5 h-5 text-yellow-300" v-else />
|
|
44
|
-
mode
|
|
45
|
-
</span>
|
|
46
|
-
</li>
|
|
44
|
+
<!-- <li>
|
|
45
|
+
|
|
46
|
+
</li> -->
|
|
47
47
|
<li>
|
|
48
48
|
<button @click="logout" class="cursor-pointer flex items-center gap-1 block px-4 py-2 text-sm text-black hover:bg-html dark:text-darkSidebarTextHover dark:hover:bg-darkSidebarItemHover dark:hover:text-darkSidebarTextActive w-full" role="menuitem">Sign out</button>
|
|
49
49
|
</li>
|
|
@@ -68,7 +68,10 @@
|
|
|
68
68
|
<div class="h-full px-3 pb-4 overflow-y-auto bg-lightSidebar dark:bg-darkSidebar border-r border-lightSidebarBorder dark:border-darkSidebarBorder">
|
|
69
69
|
<div class="flex ms-2 md:me-24 m-4 ">
|
|
70
70
|
<img :src="loadFile(coreStore.config?.brandLogo || '@/assets/logo.svg')" :alt="`${ coreStore.config?.brandName } Logo`" class="h-8 me-3" />
|
|
71
|
-
<span
|
|
71
|
+
<span
|
|
72
|
+
v-if="coreStore.config?.showBrandNameInSidebar"
|
|
73
|
+
class="self-center text-lightNavbarText-size font-semibold sm:text-lightNavbarText-size whitespace-nowrap dark:text-darkSidebarText text-lightSidebarText"
|
|
74
|
+
>
|
|
72
75
|
{{ coreStore.config?.brandName }}
|
|
73
76
|
</span>
|
|
74
77
|
</div>
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
</template>
|
|
116
116
|
|
|
117
117
|
<script setup>
|
|
118
|
-
import { watch, computed
|
|
118
|
+
import { watch, computed } from 'vue'
|
|
119
119
|
import Dropdown from '@/components/Dropdown.vue';
|
|
120
120
|
import CustomDateRangePicker from '@/components/CustomDateRangePicker.vue';
|
|
121
121
|
import { callAdminForthApi } from '@/utils';
|
|
@@ -130,7 +130,6 @@
|
|
|
130
130
|
</button>
|
|
131
131
|
</template>
|
|
132
132
|
<div v-if="columnError(column) && validating" class="mt-1 text-xs text-red-500 dark:text-red-400">{{ columnError(column) }}</div>
|
|
133
|
-
|
|
134
133
|
<div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-gray-400 dark:text-gray-500">{{ column.editingNote[mode] }}</div>
|
|
135
134
|
|
|
136
135
|
</td>
|
|
@@ -154,10 +153,10 @@ import { IconExclamationCircleSolid, IconEyeSlashSolid, IconEyeSolid } from '@ic
|
|
|
154
153
|
import { computedAsync } from '@vueuse/core';
|
|
155
154
|
import { initFlowbite } from 'flowbite';
|
|
156
155
|
import { computed, onMounted, ref, watch } from 'vue';
|
|
157
|
-
import { useRouter } from 'vue-router';
|
|
156
|
+
import { useRouter, useRoute } from 'vue-router';
|
|
158
157
|
|
|
159
158
|
const router = useRouter();
|
|
160
|
-
|
|
159
|
+
const route = useRoute();
|
|
161
160
|
const props = defineProps({
|
|
162
161
|
loading: Boolean,
|
|
163
162
|
resource: Object,
|
|
@@ -168,7 +167,7 @@ const props = defineProps({
|
|
|
168
167
|
|
|
169
168
|
const unmasked = ref({});
|
|
170
169
|
|
|
171
|
-
const mode = computed(() =>
|
|
170
|
+
const mode = computed(() => route.name === 'resource-create' ? 'create' : 'edit');
|
|
172
171
|
|
|
173
172
|
const emit = defineEmits(['update:record', 'update:isValid']);
|
|
174
173
|
|
|
@@ -245,6 +244,10 @@ const setCurrentValue = (key, value) => {
|
|
|
245
244
|
} else {
|
|
246
245
|
currentValues.value[key] = value;
|
|
247
246
|
}
|
|
247
|
+
if (['text', 'richtext', 'string'].includes(col.type) && col.enforceLowerCase) {
|
|
248
|
+
currentValues.value[key] = currentValues.value[key].toLowerCase();
|
|
249
|
+
}
|
|
250
|
+
|
|
248
251
|
currentValues.value = { ...currentValues.value };
|
|
249
252
|
console.log('3️⃣ setCurrentValue', key, value);
|
|
250
253
|
emit('update:record', currentValues.value);
|