adminforth 1.3.12 → 1.3.14
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/dataConnectors/baseConnector.ts +32 -2
- package/dist/dataConnectors/baseConnector.js +29 -1
- package/dist/index.js +12 -18
- package/dist/modules/configValidator.js +3 -0
- package/dist/modules/operationalResource.js +7 -2
- package/dist/modules/restApi.js +5 -2
- package/index.ts +25 -26
- package/modules/configValidator.ts +3 -1
- package/modules/operationalResource.ts +8 -2
- package/modules/restApi.ts +5 -2
- package/package.json +1 -1
- package/spa/src/App.vue +4 -1
- package/spa/src/components/ResourceListTable.vue +8 -7
- package/spa/src/spa_types/core.ts +1 -0
- package/spa/src/views/LoginView.vue +26 -9
- package/types/AdminForthConfig.ts +22 -3
|
@@ -59,7 +59,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
|
|
|
59
59
|
|
|
60
60
|
async createRecord({ resource, record, adminUser }: {
|
|
61
61
|
resource: AdminForthResource; record: any; adminUser: any;
|
|
62
|
-
}): Promise<any> {
|
|
62
|
+
}): Promise<{ error?: string; ok: boolean; createdRecord?: any; }> {
|
|
63
63
|
// transform value using setFieldValue and call createRecordOriginalValues
|
|
64
64
|
const filledRecord = {...record};
|
|
65
65
|
const recordWithOriginalValues = {...record};
|
|
@@ -75,10 +75,40 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
|
|
|
75
75
|
}
|
|
76
76
|
recordWithOriginalValues[col.name] = this.setFieldValue(col, filledRecord[col.name]);
|
|
77
77
|
}
|
|
78
|
+
|
|
79
|
+
async function checkUnique(column: AdminForthResourceColumn, value: any) {
|
|
80
|
+
const existingRecord = await this.getData({
|
|
81
|
+
resource,
|
|
82
|
+
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value }],
|
|
83
|
+
limit: 1,
|
|
84
|
+
sort: [],
|
|
85
|
+
offset: 0,
|
|
86
|
+
getTotals: false
|
|
87
|
+
});
|
|
88
|
+
return existingRecord.data.length > 0;
|
|
89
|
+
}
|
|
90
|
+
let error: string | null = null;
|
|
91
|
+
await Promise.race(
|
|
92
|
+
resource.dataSourceColumns.map(async (col) => {
|
|
93
|
+
if (col.isUnique && !col.virtual && !error) {
|
|
94
|
+
const exists = await checkUnique(col, recordWithOriginalValues[col.name]);
|
|
95
|
+
if (exists) {
|
|
96
|
+
error = `Record with ${col.name} ${recordWithOriginalValues[col.name]} already exists`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
if (error) {
|
|
102
|
+
return { error, ok: false };
|
|
103
|
+
}
|
|
104
|
+
|
|
78
105
|
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record', recordWithOriginalValues);
|
|
79
106
|
await this.createRecordOriginalValues({ resource, record: recordWithOriginalValues });
|
|
80
107
|
|
|
81
|
-
return
|
|
108
|
+
return {
|
|
109
|
+
ok: true,
|
|
110
|
+
createdRecord: recordWithOriginalValues,
|
|
111
|
+
}
|
|
82
112
|
}
|
|
83
113
|
|
|
84
114
|
updateRecord({ resource, recordId, newValues }: { resource: AdminForthResource; recordId: string; newValues: any; }): Promise<void> {
|
|
@@ -66,9 +66,37 @@ export default class AdminForthBaseConnector {
|
|
|
66
66
|
}
|
|
67
67
|
recordWithOriginalValues[col.name] = this.setFieldValue(col, filledRecord[col.name]);
|
|
68
68
|
}
|
|
69
|
+
function checkUnique(column, value) {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const existingRecord = yield this.getData({
|
|
72
|
+
resource,
|
|
73
|
+
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value }],
|
|
74
|
+
limit: 1,
|
|
75
|
+
sort: [],
|
|
76
|
+
offset: 0,
|
|
77
|
+
getTotals: false
|
|
78
|
+
});
|
|
79
|
+
return existingRecord.data.length > 0;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
let error = null;
|
|
83
|
+
yield Promise.race(resource.dataSourceColumns.map((col) => __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
if (col.isUnique && !col.virtual && !error) {
|
|
85
|
+
const exists = yield checkUnique(col, recordWithOriginalValues[col.name]);
|
|
86
|
+
if (exists) {
|
|
87
|
+
error = `Record with ${col.name} ${recordWithOriginalValues[col.name]} already exists`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
})));
|
|
91
|
+
if (error) {
|
|
92
|
+
return { error, ok: false };
|
|
93
|
+
}
|
|
69
94
|
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record', recordWithOriginalValues);
|
|
70
95
|
yield this.createRecordOriginalValues({ resource, record: recordWithOriginalValues });
|
|
71
|
-
return
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
createdRecord: recordWithOriginalValues,
|
|
99
|
+
};
|
|
72
100
|
});
|
|
73
101
|
}
|
|
74
102
|
updateRecord({ resource, recordId, newValues }) {
|
package/dist/index.js
CHANGED
|
@@ -159,22 +159,11 @@ class AdminForth {
|
|
|
159
159
|
return __awaiter(this, arguments, void 0, function* ({ resource, record, adminUser }) {
|
|
160
160
|
var _c, _d, _e, _f, _g;
|
|
161
161
|
for (const column of resource.columns) {
|
|
162
|
+
// TODO: assuming specifity for AdminForthResourcePages.create better to move it to api for this button
|
|
162
163
|
if (((_c = column.required) === null || _c === void 0 ? void 0 : _c.create) &&
|
|
163
164
|
record[column.name] === undefined &&
|
|
164
165
|
column.showIn.includes(AdminForthResourcePages.create)) {
|
|
165
|
-
return { error: `Column '${column.name}' is required
|
|
166
|
-
}
|
|
167
|
-
if (column.isUnique) {
|
|
168
|
-
const existingRecord = yield this.connectors[resource.dataSource].getData({
|
|
169
|
-
resource,
|
|
170
|
-
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value: record[column.name] }],
|
|
171
|
-
limit: 1,
|
|
172
|
-
sort: [],
|
|
173
|
-
offset: 0
|
|
174
|
-
});
|
|
175
|
-
if (existingRecord.data.length > 0) {
|
|
176
|
-
return { error: `Record with ${column.name} ${record[column.name]} already exists` };
|
|
177
|
-
}
|
|
166
|
+
return { error: `Column '${column.name}' is required`, ok: false };
|
|
178
167
|
}
|
|
179
168
|
}
|
|
180
169
|
// execute hook if needed
|
|
@@ -184,7 +173,7 @@ class AdminForth {
|
|
|
184
173
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
185
174
|
}
|
|
186
175
|
if (resp.error) {
|
|
187
|
-
return { error: resp.error };
|
|
176
|
+
return { error: resp.error, ok: false };
|
|
188
177
|
}
|
|
189
178
|
}
|
|
190
179
|
// remove virtual columns from record
|
|
@@ -195,20 +184,25 @@ class AdminForth {
|
|
|
195
184
|
}
|
|
196
185
|
const connector = this.connectors[resource.dataSource];
|
|
197
186
|
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record createResourceRecord', record);
|
|
198
|
-
yield connector.createRecord({ resource, record, adminUser });
|
|
187
|
+
const { ok, error, createdRecord } = yield connector.createRecord({ resource, record, adminUser });
|
|
199
188
|
const primaryKey = record[resource.columns.find((col) => col.primaryKey).name];
|
|
200
189
|
// execute hook if needed
|
|
201
190
|
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)) {
|
|
202
191
|
console.log('Hook afterSave', hook);
|
|
203
|
-
const resp = yield hook({
|
|
192
|
+
const resp = yield hook({
|
|
193
|
+
recordId: primaryKey,
|
|
194
|
+
resource,
|
|
195
|
+
record: createdRecord,
|
|
196
|
+
adminUser
|
|
197
|
+
});
|
|
204
198
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
205
199
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
206
200
|
}
|
|
207
201
|
if (resp.error) {
|
|
208
|
-
return { error: resp.error };
|
|
202
|
+
return { error: resp.error, ok: false };
|
|
209
203
|
}
|
|
210
204
|
}
|
|
211
|
-
return { ok
|
|
205
|
+
return { ok, error, createdRecord };
|
|
212
206
|
});
|
|
213
207
|
}
|
|
214
208
|
resource(resourceId) {
|
|
@@ -122,6 +122,9 @@ export default class ConfigValidator {
|
|
|
122
122
|
if (this.config.customization.brandLogo) {
|
|
123
123
|
errors.push(...this.checkCustomFileExists(this.config.customization.brandLogo));
|
|
124
124
|
}
|
|
125
|
+
if (this.config.customization.showBrandNameInSidebar === undefined) {
|
|
126
|
+
this.config.customization.showBrandNameInSidebar = true;
|
|
127
|
+
}
|
|
125
128
|
if (this.config.customization.favicon) {
|
|
126
129
|
errors.push(...this.checkCustomFileExists(this.config.customization.favicon));
|
|
127
130
|
}
|
|
@@ -58,9 +58,14 @@ export default class OperationalResource {
|
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
-
create(
|
|
61
|
+
create(recordValues) {
|
|
62
62
|
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
-
|
|
63
|
+
const { ok, createdRecord, error } = yield this.dataConnector.createRecord({
|
|
64
|
+
resource: this.resourceConfig,
|
|
65
|
+
record: recordValues,
|
|
66
|
+
adminUser: null
|
|
67
|
+
});
|
|
68
|
+
return { ok, createdRecord, error };
|
|
64
69
|
});
|
|
65
70
|
}
|
|
66
71
|
update(primaryKey, record) {
|
package/dist/modules/restApi.js
CHANGED
|
@@ -135,6 +135,7 @@ export default class AdminForthRestAPI {
|
|
|
135
135
|
brandName: this.adminforth.config.customization.brandName,
|
|
136
136
|
usernameFieldName: usernameColumn.label,
|
|
137
137
|
loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
|
|
138
|
+
loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
|
|
138
139
|
title: (_j = this.adminforth.config.customization) === null || _j === void 0 ? void 0 : _j.title,
|
|
139
140
|
demoCredentials: this.adminforth.config.auth.demoCredentials,
|
|
140
141
|
loginPromptHTML: this.adminforth.config.auth.loginPromptHTML,
|
|
@@ -208,6 +209,7 @@ export default class AdminForthRestAPI {
|
|
|
208
209
|
menu: newMenu,
|
|
209
210
|
config: {
|
|
210
211
|
brandName: this.adminforth.config.customization.brandName,
|
|
212
|
+
showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
|
|
211
213
|
brandLogo: this.adminforth.config.customization.brandLogo,
|
|
212
214
|
datesFormat: this.adminforth.config.customization.datesFormat,
|
|
213
215
|
deleteConfirmation: this.adminforth.config.deleteConfirmation,
|
|
@@ -486,11 +488,12 @@ export default class AdminForthRestAPI {
|
|
|
486
488
|
const { record } = body;
|
|
487
489
|
const response = yield this.adminforth.createResourceRecord({ resource, record, adminUser });
|
|
488
490
|
if (response.error) {
|
|
489
|
-
return { error: response.error };
|
|
491
|
+
return { error: response.error, ok: false };
|
|
490
492
|
}
|
|
491
493
|
const connector = this.adminforth.connectors[resource.dataSource];
|
|
492
494
|
return {
|
|
493
|
-
newRecordId:
|
|
495
|
+
newRecordId: response.createdRecord[connector.getPrimaryKey(resource)],
|
|
496
|
+
ok: true
|
|
494
497
|
};
|
|
495
498
|
})
|
|
496
499
|
});
|
package/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import ConfigValidator from './modules/configValidator.js';
|
|
|
23
23
|
import AdminForthRestAPI, { interpretResource } from './modules/restApi.js';
|
|
24
24
|
import ClickhouseConnector from './dataConnectors/clickhouse.js';
|
|
25
25
|
import OperationalResource from './modules/operationalResource.js';
|
|
26
|
+
import { error } from 'console';
|
|
26
27
|
|
|
27
28
|
// exports
|
|
28
29
|
export * from './types/AdminForthConfig.js';
|
|
@@ -198,28 +199,20 @@ class AdminForth implements IAdminForth {
|
|
|
198
199
|
return users.data[0] || null;
|
|
199
200
|
}
|
|
200
201
|
|
|
201
|
-
async createResourceRecord(
|
|
202
|
+
async createResourceRecord(
|
|
203
|
+
{ resource, record, adminUser }:
|
|
204
|
+
{ resource: AdminForthResource, record: any, adminUser: AdminUser }
|
|
205
|
+
): Promise<{ ok: boolean, error?: string, createdRecord?: any }> {
|
|
206
|
+
|
|
202
207
|
for (const column of resource.columns) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (column.isUnique) {
|
|
212
|
-
const existingRecord = await this.connectors[resource.dataSource].getData({
|
|
213
|
-
resource,
|
|
214
|
-
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value: record[column.name] }],
|
|
215
|
-
limit: 1,
|
|
216
|
-
sort: [],
|
|
217
|
-
offset: 0
|
|
218
|
-
});
|
|
219
|
-
if (existingRecord.data.length > 0) {
|
|
220
|
-
return { error: `Record with ${column.name} ${record[column.name]} already exists` };
|
|
221
|
-
}
|
|
222
|
-
}
|
|
208
|
+
// TODO: assuming specifity for AdminForthResourcePages.create better to move it to api for this button
|
|
209
|
+
if (
|
|
210
|
+
(column.required as {create?: boolean, edit?: boolean}) ?.create &&
|
|
211
|
+
record[column.name] === undefined &&
|
|
212
|
+
column.showIn.includes(AdminForthResourcePages.create)
|
|
213
|
+
) {
|
|
214
|
+
return { error: `Column '${column.name}' is required`, ok: false };
|
|
215
|
+
}
|
|
223
216
|
}
|
|
224
217
|
|
|
225
218
|
// execute hook if needed
|
|
@@ -230,7 +223,7 @@ class AdminForth implements IAdminForth {
|
|
|
230
223
|
}
|
|
231
224
|
|
|
232
225
|
if (resp.error) {
|
|
233
|
-
return { error: resp.error };
|
|
226
|
+
return { error: resp.error, ok: false };
|
|
234
227
|
}
|
|
235
228
|
}
|
|
236
229
|
|
|
@@ -242,24 +235,30 @@ class AdminForth implements IAdminForth {
|
|
|
242
235
|
}
|
|
243
236
|
const connector = this.connectors[resource.dataSource];
|
|
244
237
|
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record createResourceRecord', record);
|
|
245
|
-
await connector.createRecord({ resource, record, adminUser });
|
|
238
|
+
const { ok, error, createdRecord } = await connector.createRecord({ resource, record, adminUser });
|
|
246
239
|
|
|
247
240
|
const primaryKey = record[resource.columns.find((col) => col.primaryKey).name];
|
|
248
241
|
|
|
249
242
|
// execute hook if needed
|
|
250
243
|
for (const hook of listify(resource.hooks?.create?.afterSave as AfterSaveFunction[])) {
|
|
251
244
|
console.log('Hook afterSave', hook);
|
|
252
|
-
const resp = await hook({
|
|
245
|
+
const resp = await hook({
|
|
246
|
+
recordId: primaryKey,
|
|
247
|
+
resource,
|
|
248
|
+
record: createdRecord,
|
|
249
|
+
adminUser
|
|
250
|
+
});
|
|
251
|
+
|
|
253
252
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
254
253
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
255
254
|
}
|
|
256
255
|
|
|
257
256
|
if (resp.error) {
|
|
258
|
-
return { error: resp.error };
|
|
257
|
+
return { error: resp.error, ok: false };
|
|
259
258
|
}
|
|
260
259
|
}
|
|
261
260
|
|
|
262
|
-
return { ok
|
|
261
|
+
return { ok, error, createdRecord };
|
|
263
262
|
}
|
|
264
263
|
|
|
265
264
|
resource(resourceId: string) {
|
|
@@ -130,7 +130,9 @@ export default class ConfigValidator implements IConfigValidator {
|
|
|
130
130
|
if (this.config.customization.brandLogo) {
|
|
131
131
|
errors.push(...this.checkCustomFileExists(this.config.customization.brandLogo));
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
if (this.config.customization.showBrandNameInSidebar === undefined) {
|
|
134
|
+
this.config.customization.showBrandNameInSidebar = true;
|
|
135
|
+
}
|
|
134
136
|
if (this.config.customization.favicon) {
|
|
135
137
|
errors.push(...this.checkCustomFileExists(this.config.customization.favicon));
|
|
136
138
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { error } from 'console';
|
|
1
2
|
import { IAdminForthFilter, IAdminForthSort, IOperationalResource, IAdminForthDataSourceConnectorBase, AdminForthResource, IAdminForth } from '../types/AdminForthConfig.js';
|
|
2
3
|
|
|
3
4
|
|
|
@@ -63,8 +64,13 @@ export default class OperationalResource implements IOperationalResource {
|
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
async create(
|
|
67
|
-
|
|
67
|
+
async create(recordValues: any): Promise<{ ok: boolean; createdRecord: any; error?: string; }> {
|
|
68
|
+
const { ok, createdRecord, error } = await this.dataConnector.createRecord({
|
|
69
|
+
resource: this.resourceConfig,
|
|
70
|
+
record: recordValues,
|
|
71
|
+
adminUser: null
|
|
72
|
+
});
|
|
73
|
+
return { ok, createdRecord, error };
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
async update(primaryKey: any, record: any): Promise<any> {
|
package/modules/restApi.ts
CHANGED
|
@@ -166,6 +166,7 @@ export default class AdminForthRestAPI {
|
|
|
166
166
|
brandName: this.adminforth.config.customization.brandName,
|
|
167
167
|
usernameFieldName: usernameColumn.label,
|
|
168
168
|
loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
|
|
169
|
+
loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
|
|
169
170
|
title: this.adminforth.config.customization?.title,
|
|
170
171
|
demoCredentials: this.adminforth.config.auth.demoCredentials,
|
|
171
172
|
loginPromptHTML: this.adminforth.config.auth.loginPromptHTML,
|
|
@@ -244,6 +245,7 @@ export default class AdminForthRestAPI {
|
|
|
244
245
|
menu: newMenu,
|
|
245
246
|
config: {
|
|
246
247
|
brandName: this.adminforth.config.customization.brandName,
|
|
248
|
+
showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
|
|
247
249
|
brandLogo: this.adminforth.config.customization.brandLogo,
|
|
248
250
|
datesFormat: this.adminforth.config.customization.datesFormat,
|
|
249
251
|
deleteConfirmation: this.adminforth.config.deleteConfirmation,
|
|
@@ -568,12 +570,13 @@ export default class AdminForthRestAPI {
|
|
|
568
570
|
|
|
569
571
|
const response = await this.adminforth.createResourceRecord({ resource, record, adminUser });
|
|
570
572
|
if (response.error) {
|
|
571
|
-
return { error: response.error };
|
|
573
|
+
return { error: response.error, ok: false };
|
|
572
574
|
}
|
|
573
575
|
const connector = this.adminforth.connectors[resource.dataSource];
|
|
574
576
|
|
|
575
577
|
return {
|
|
576
|
-
newRecordId:
|
|
578
|
+
newRecordId: response.createdRecord[connector.getPrimaryKey(resource)],
|
|
579
|
+
ok: true
|
|
577
580
|
}
|
|
578
581
|
}
|
|
579
582
|
});
|
package/package.json
CHANGED
package/spa/src/App.vue
CHANGED
|
@@ -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>
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
</td>
|
|
71
71
|
</tr>
|
|
72
72
|
|
|
73
|
-
<tr @click="onClick($event,row)" v-else v-for="(row, rowI) in rows" :key="row.
|
|
73
|
+
<tr @click="onClick($event,row)" v-else v-for="(row, rowI) in rows" :key="`row_${row._primaryKeyValue}`"
|
|
74
74
|
class="bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover cursor-pointer"
|
|
75
75
|
:class="{'border-b': rowI !== rows.length - 1}"
|
|
76
76
|
>
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
@click="(e)=>{e.stopPropagation()}"
|
|
81
81
|
id="checkbox-table-search-1"
|
|
82
82
|
type="checkbox"
|
|
83
|
-
:checked="checkboxesInternal.includes(row.
|
|
84
|
-
@change="(e)=>{addToCheckedValues(row.
|
|
83
|
+
:checked="checkboxesInternal.includes(row._primaryKeyValue)"
|
|
84
|
+
@change="(e)=>{addToCheckedValues(row._primaryKeyValue)}"
|
|
85
85
|
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 cursor-pointer">
|
|
86
86
|
<label for="checkbox-table-search-1" class="sr-only">checkbox</label>
|
|
87
87
|
</div>
|
|
@@ -299,6 +299,7 @@ watch(() => props.sort, (newSort) => {
|
|
|
299
299
|
});
|
|
300
300
|
|
|
301
301
|
function addToCheckedValues(id) {
|
|
302
|
+
console.log('checking', checkboxesInternal.value, 'id', id)
|
|
302
303
|
if (checkboxesInternal.value.includes(id)) {
|
|
303
304
|
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
|
|
304
305
|
} else {
|
|
@@ -312,13 +313,13 @@ const columnsListed = computed(() => props.resource?.columns?.filter(c => c.show
|
|
|
312
313
|
async function selectAll(value) {
|
|
313
314
|
if (!allFromThisPageChecked.value) {
|
|
314
315
|
props.rows.forEach((r) => {
|
|
315
|
-
if (!checkboxesInternal.value.includes(r.
|
|
316
|
-
checkboxesInternal.value.push(r.
|
|
316
|
+
if (!checkboxesInternal.value.includes(r._primaryKeyValue)) {
|
|
317
|
+
checkboxesInternal.value.push(r._primaryKeyValue)
|
|
317
318
|
}
|
|
318
319
|
});
|
|
319
320
|
} else {
|
|
320
321
|
props.rows.forEach((r) => {
|
|
321
|
-
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r.
|
|
322
|
+
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r._primaryKeyValue);
|
|
322
323
|
});
|
|
323
324
|
}
|
|
324
325
|
checkboxesInternal.value = [ ...checkboxesInternal.value ];
|
|
@@ -328,7 +329,7 @@ const totalPages = computed(() => Math.ceil(props.totalRows / props.pageSize));
|
|
|
328
329
|
|
|
329
330
|
const allFromThisPageChecked = computed(() => {
|
|
330
331
|
if (!props.rows) return false;
|
|
331
|
-
return props.rows.every((r) => checkboxesInternal.value.includes(r.
|
|
332
|
+
return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
|
|
332
333
|
});
|
|
333
334
|
const ascArr = computed(() => sort.value.filter((s) => s.direction === 'asc').map((s) => s.field));
|
|
334
335
|
const descArr = computed(() => sort.value.filter((s) => s.direction === 'desc').map((s) => s.field));
|
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800"
|
|
3
|
-
:style="coreStore.config?.loginBackgroundImage ? {
|
|
2
|
+
<div class="relative flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800 relative w-screen h-screen"
|
|
3
|
+
:style="coreStore.config?.loginBackgroundImage && backgroundPosition === 'over' ? {
|
|
4
4
|
'background-image': 'url(' + loadFile(coreStore.config?.loginBackgroundImage) + ')',
|
|
5
5
|
'background-size': 'cover',
|
|
6
6
|
'background-position': 'center',
|
|
7
7
|
'background-blend-mode': 'darken'
|
|
8
8
|
}: {}"
|
|
9
|
-
|
|
9
|
+
>
|
|
10
|
+
|
|
11
|
+
<img v-if="coreStore.config?.loginBackgroundImage && backgroundPosition !== 'over'"
|
|
12
|
+
:src="loadFile(coreStore.config?.loginBackgroundImage)"
|
|
13
|
+
class="position-absolute top-0 left-0 h-screen object-cover w-0"
|
|
14
|
+
:class="{
|
|
15
|
+
'1/2': 'md:w-1/2',
|
|
16
|
+
'1/3': 'md:w-1/3',
|
|
17
|
+
'2/3': 'md:w-2/3',
|
|
18
|
+
'3/4': 'md:w-3/4',
|
|
19
|
+
'2/5': 'md:w-2/5',
|
|
20
|
+
'3/5': 'md:w-3/5',
|
|
21
|
+
}[backgroundPosition]"
|
|
22
|
+
/>
|
|
10
23
|
|
|
11
24
|
<!-- Main modal -->
|
|
12
|
-
<div id="authentication-modal" tabindex="-1"
|
|
13
|
-
|
|
25
|
+
<div id="authentication-modal" tabindex="-1"
|
|
26
|
+
class="overflow-y-auto flex flex-grow
|
|
27
|
+
overflow-x-hidden z-50 min-w-[350px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
28
|
+
<div class="relative p-4 w-full max-h-full max-w-[400px]">
|
|
14
29
|
<!-- Modal content -->
|
|
15
30
|
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700 dark:shadow-black" >
|
|
16
31
|
<!-- Modal header -->
|
|
17
32
|
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
18
33
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
|
|
19
|
-
|
|
34
|
+
Sign in to {{ coreStore.config?.brandName }}
|
|
20
35
|
</h3>
|
|
21
|
-
|
|
22
36
|
</div>
|
|
23
37
|
<!-- Modal body -->
|
|
24
38
|
<div class="p-4 md:p-5">
|
|
@@ -87,7 +101,7 @@
|
|
|
87
101
|
|
|
88
102
|
<script setup>
|
|
89
103
|
|
|
90
|
-
import { onMounted, ref,
|
|
104
|
+
import { onMounted, ref, computed } from 'vue';
|
|
91
105
|
import { useCoreStore } from '@/stores/core';
|
|
92
106
|
import { useUserStore } from '@/stores/user';
|
|
93
107
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
@@ -103,11 +117,14 @@ const inProgress = ref(false);
|
|
|
103
117
|
const coreStore = useCoreStore();
|
|
104
118
|
const user = useUserStore();
|
|
105
119
|
|
|
106
|
-
|
|
107
120
|
const showPw = ref(false);
|
|
108
121
|
|
|
109
122
|
const error = ref(null);
|
|
110
123
|
|
|
124
|
+
const backgroundPosition = computed(() => {
|
|
125
|
+
return coreStore.config?.loginBackgroundPosition || '1/2';
|
|
126
|
+
});
|
|
127
|
+
|
|
111
128
|
onMounted(async () => {
|
|
112
129
|
await coreStore.getPublicConfig();
|
|
113
130
|
if (coreStore.config?.demoCredentials) {
|
|
@@ -226,7 +226,7 @@ export interface IAdminForthDataSourceConnectorBase extends IAdminForthDataSourc
|
|
|
226
226
|
resource: AdminForthResource,
|
|
227
227
|
record: any
|
|
228
228
|
adminUser: AdminUser
|
|
229
|
-
}): Promise<
|
|
229
|
+
}): Promise<{ok: boolean, error?: string, createdRecord?: any}>;
|
|
230
230
|
|
|
231
231
|
getMinMaxForColumns({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }>;
|
|
232
232
|
}
|
|
@@ -264,7 +264,9 @@ export interface IAdminForth {
|
|
|
264
264
|
[key: string]: IAdminForthDataSourceConnectorBase;
|
|
265
265
|
};
|
|
266
266
|
|
|
267
|
-
createResourceRecord(
|
|
267
|
+
createResourceRecord(
|
|
268
|
+
params: { resource: AdminForthResource, record: any, adminUser: AdminUser }
|
|
269
|
+
): Promise<{ ok: boolean, error?: string, createdRecord?: any }>;
|
|
268
270
|
|
|
269
271
|
auth: IAdminForthAuth;
|
|
270
272
|
|
|
@@ -1130,6 +1132,16 @@ export type AdminForthConfig = {
|
|
|
1130
1132
|
*/
|
|
1131
1133
|
loginBackgroundImage?: string,
|
|
1132
1134
|
|
|
1135
|
+
|
|
1136
|
+
/**
|
|
1137
|
+
* Position of background image on login page
|
|
1138
|
+
* 'over' - image will be displayed over full login page under login form
|
|
1139
|
+
* '1/2' - image will be displayed on left 1/2 of login page
|
|
1140
|
+
*
|
|
1141
|
+
* Default: '1/2'
|
|
1142
|
+
*/
|
|
1143
|
+
loginBackgroundPosition?: 'over' | '1/2' | '1/3' | '2/3' | '3/4' | '2/5' | '3/5',
|
|
1144
|
+
|
|
1133
1145
|
/**
|
|
1134
1146
|
* Function or functions which will be called before user try to login.
|
|
1135
1147
|
* Each function will resive User object as an argument
|
|
@@ -1193,6 +1205,13 @@ export type AdminForthConfig = {
|
|
|
1193
1205
|
*/
|
|
1194
1206
|
brandName?: string,
|
|
1195
1207
|
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Whether to show brand name in sidebar
|
|
1211
|
+
* default is true
|
|
1212
|
+
*/
|
|
1213
|
+
showBrandNameInSidebar?: boolean,
|
|
1214
|
+
|
|
1196
1215
|
/**
|
|
1197
1216
|
* Path to your app logo
|
|
1198
1217
|
*
|
|
@@ -1379,7 +1398,7 @@ export interface IOperationalResource {
|
|
|
1379
1398
|
|
|
1380
1399
|
count: (filter: IAdminForthFilter | IAdminForthFilter[]) => Promise<number>;
|
|
1381
1400
|
|
|
1382
|
-
create: (record: any) => Promise<any>;
|
|
1401
|
+
create: (record: any) => Promise<{ ok: boolean; createdRecord: any; error?: string; }>;
|
|
1383
1402
|
|
|
1384
1403
|
update: (primaryKey: any, record: any) => Promise<any>;
|
|
1385
1404
|
|