adminforth 1.0.31 → 1.0.33
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/postgres.ts +9 -6
- package/dataConnectors/sqlite.ts +1 -0
- package/dist/dataConnectors/postgres.js +9 -6
- package/dist/dataConnectors/sqlite.js +1 -0
- package/dist/index.js +87 -65
- package/dist/modules/utils.js +9 -0
- package/dist/spa/spa/src/App.vue +0 -7
- package/dist/spa/spa/src/components/CustomDatePicker.vue +3 -3
- package/dist/spa/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/dist/spa/spa/src/components/CustomRangePicker.vue +32 -19
- package/dist/spa/spa/src/components/Dropdown.vue +6 -2
- package/dist/spa/spa/src/components/ResourceForm.vue +125 -102
- package/dist/spa/spa/src/components/ValueRenderer.vue +11 -1
- package/dist/spa/spa/src/stores/core.ts +21 -19
- package/dist/spa/spa/src/views/CreateView.vue +16 -6
- package/dist/spa/spa/src/views/EditView.vue +15 -5
- package/dist/spa/spa/src/views/ListView.vue +31 -21
- package/dist/spa/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/spa/src/views/ShowView.vue +21 -6
- package/dist/types/AdminForthConfig.js +1 -0
- package/index.ts +73 -141
- package/modules/utils.ts +14 -0
- package/package.json +1 -1
- package/spa/src/App.vue +0 -7
- package/spa/src/components/CustomDatePicker.vue +3 -3
- package/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/spa/src/components/CustomRangePicker.vue +32 -19
- package/spa/src/components/Dropdown.vue +6 -2
- package/spa/src/components/ResourceForm.vue +125 -102
- package/spa/src/components/ValueRenderer.vue +11 -1
- package/spa/src/stores/core.ts +21 -19
- package/spa/src/views/CreateView.vue +16 -6
- package/spa/src/views/EditView.vue +15 -5
- package/spa/src/views/ListView.vue +31 -21
- package/spa/src/views/ResourceParent.vue +1 -1
- package/spa/src/views/ShowView.vue +21 -6
- package/types/AdminForthConfig.ts +159 -0
|
@@ -147,7 +147,7 @@ class PostgresConnector {
|
|
|
147
147
|
|
|
148
148
|
async getRecordByPrimaryKey(resource, key) {
|
|
149
149
|
const tableName = resource.table;
|
|
150
|
-
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
150
|
+
const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
|
|
151
151
|
const stmt = await this.db.query(`SELECT ${columns} FROM ${tableName} WHERE ${this.getPrimaryKey(resource)} = $1`, [key]);
|
|
152
152
|
const row = stmt.rows[0];
|
|
153
153
|
if (!row) {
|
|
@@ -177,7 +177,7 @@ class PostgresConnector {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
async getData({ resource, limit, offset, sort, filters }) {
|
|
180
|
-
const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', ');
|
|
180
|
+
const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => `"${col.name}"`).join(', ');
|
|
181
181
|
const tableName = resource.table;
|
|
182
182
|
|
|
183
183
|
for (const filter of filters) {
|
|
@@ -199,7 +199,7 @@ class PostgresConnector {
|
|
|
199
199
|
} else {
|
|
200
200
|
totalCounter += 1;
|
|
201
201
|
}
|
|
202
|
-
return
|
|
202
|
+
return `"${field}" ${operator} ${placeholder}`
|
|
203
203
|
}).join(' AND ')}` : '';
|
|
204
204
|
|
|
205
205
|
const filterValues = [];
|
|
@@ -267,17 +267,20 @@ class PostgresConnector {
|
|
|
267
267
|
return record[colName];
|
|
268
268
|
}
|
|
269
269
|
});
|
|
270
|
+
for (let i = 0; i < columns.length; i++) {
|
|
271
|
+
columns[i] = `"${columns[i]}"`;
|
|
272
|
+
}
|
|
270
273
|
await this.db.query(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`, values);
|
|
271
274
|
}
|
|
272
275
|
|
|
273
276
|
async updateRecord({ resource, recordId, record, newValues }) {
|
|
274
277
|
const values = [...Object.values(newValues), recordId];
|
|
275
|
-
const columnsWithPlaceholders = Object.keys(newValues).map((col, i) =>
|
|
276
|
-
await this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = $${values.length}`, values);
|
|
278
|
+
const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `"${col}" = $${i + 1}`).join(', ');
|
|
279
|
+
await this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`, values);
|
|
277
280
|
}
|
|
278
281
|
|
|
279
282
|
async deleteRecord({ resource, recordId }) {
|
|
280
|
-
await this.db.query(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = $1`, [recordId]);
|
|
283
|
+
await this.db.query(`DELETE FROM ${resource.table} WHERE "${this.getPrimaryKey(resource)}" = $1`, [recordId]);
|
|
281
284
|
}
|
|
282
285
|
|
|
283
286
|
async close() {
|
package/dataConnectors/sqlite.ts
CHANGED
|
@@ -138,6 +138,7 @@ class SQLiteConnector {
|
|
|
138
138
|
|
|
139
139
|
|
|
140
140
|
getData({ resource, limit, offset, sort, filters }) {
|
|
141
|
+
console.log('getDataREQ', { resource, limit, offset, sort, filters });
|
|
141
142
|
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
142
143
|
const tableName = resource.table;
|
|
143
144
|
|
|
@@ -148,7 +148,7 @@ class PostgresConnector {
|
|
|
148
148
|
getRecordByPrimaryKey(resource, key) {
|
|
149
149
|
return __awaiter(this, void 0, void 0, function* () {
|
|
150
150
|
const tableName = resource.table;
|
|
151
|
-
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
151
|
+
const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
|
|
152
152
|
const stmt = yield this.db.query(`SELECT ${columns} FROM ${tableName} WHERE ${this.getPrimaryKey(resource)} = $1`, [key]);
|
|
153
153
|
const row = stmt.rows[0];
|
|
154
154
|
if (!row) {
|
|
@@ -180,7 +180,7 @@ class PostgresConnector {
|
|
|
180
180
|
}
|
|
181
181
|
getData(_a) {
|
|
182
182
|
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
|
|
183
|
-
const columns = resource.dataSourceColumns.filter(c => !c.virtual).map((col) => col.name).join(', ');
|
|
183
|
+
const columns = resource.dataSourceColumns.filter(c => !c.virtual).map((col) => `"${col.name}"`).join(', ');
|
|
184
184
|
const tableName = resource.table;
|
|
185
185
|
for (const filter of filters) {
|
|
186
186
|
if (!this.OperatorsMap[filter.operator]) {
|
|
@@ -202,7 +202,7 @@ class PostgresConnector {
|
|
|
202
202
|
else {
|
|
203
203
|
totalCounter += 1;
|
|
204
204
|
}
|
|
205
|
-
return
|
|
205
|
+
return `"${field}" ${operator} ${placeholder}`;
|
|
206
206
|
}).join(' AND ')}` : '';
|
|
207
207
|
const filterValues = [];
|
|
208
208
|
filters.length ? filters.forEach((f) => {
|
|
@@ -272,19 +272,22 @@ class PostgresConnector {
|
|
|
272
272
|
return record[colName];
|
|
273
273
|
}
|
|
274
274
|
});
|
|
275
|
+
for (let i = 0; i < columns.length; i++) {
|
|
276
|
+
columns[i] = `"${columns[i]}"`;
|
|
277
|
+
}
|
|
275
278
|
yield this.db.query(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`, values);
|
|
276
279
|
});
|
|
277
280
|
}
|
|
278
281
|
updateRecord(_a) {
|
|
279
282
|
return __awaiter(this, arguments, void 0, function* ({ resource, recordId, record, newValues }) {
|
|
280
283
|
const values = [...Object.values(newValues), recordId];
|
|
281
|
-
const columnsWithPlaceholders = Object.keys(newValues).map((col, i) =>
|
|
282
|
-
yield this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = $${values.length}`, values);
|
|
284
|
+
const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `"${col}" = $${i + 1}`).join(', ');
|
|
285
|
+
yield this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`, values);
|
|
283
286
|
});
|
|
284
287
|
}
|
|
285
288
|
deleteRecord(_a) {
|
|
286
289
|
return __awaiter(this, arguments, void 0, function* ({ resource, recordId }) {
|
|
287
|
-
yield this.db.query(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = $1`, [recordId]);
|
|
290
|
+
yield this.db.query(`DELETE FROM ${resource.table} WHERE "${this.getPrimaryKey(resource)}" = $1`, [recordId]);
|
|
288
291
|
});
|
|
289
292
|
}
|
|
290
293
|
close() {
|
|
@@ -148,6 +148,7 @@ class SQLiteConnector {
|
|
|
148
148
|
return value;
|
|
149
149
|
}
|
|
150
150
|
getData({ resource, limit, offset, sort, filters }) {
|
|
151
|
+
console.log('getDataREQ', { resource, limit, offset, sort, filters });
|
|
151
152
|
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
152
153
|
const tableName = resource.table;
|
|
153
154
|
for (const filter of filters) {
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { guessLabelFromName } from './modules/utils.js';
|
|
|
22
22
|
import ExpressServer from './servers/express.js';
|
|
23
23
|
import { v1 as uuid } from 'uuid';
|
|
24
24
|
import fs from 'fs';
|
|
25
|
+
import { ADMINFORTH_VERSION } from './modules/utils.js';
|
|
25
26
|
import { AdminForthFilterOperators, AdminForthTypes } from './types.js';
|
|
26
27
|
const AVAILABLE_SHOW_IN = ['list', 'edit', 'create', 'filter', 'show'];
|
|
27
28
|
const DEFAULT_ALLOWED_ACTIONS = { create: true, edit: true, show: true, delete: true };
|
|
@@ -37,6 +38,7 @@ class AdminForth {
|
|
|
37
38
|
this.codeInjector = new CodeInjector(this);
|
|
38
39
|
this.connectors = {};
|
|
39
40
|
this.statuses = {};
|
|
41
|
+
console.log(`🚀 AdminForth v${ADMINFORTH_VERSION} starting up`);
|
|
40
42
|
}
|
|
41
43
|
validateConfig() {
|
|
42
44
|
if (this.config.rootUser) {
|
|
@@ -249,7 +251,7 @@ class AdminForth {
|
|
|
249
251
|
if (!this.config.databaseConnectors[dbType]) {
|
|
250
252
|
throw new Error(`Database type ${dbType} is not supported, consider using databaseConnectors in AdminForth config`);
|
|
251
253
|
}
|
|
252
|
-
this.connectors[ds.id] = new this.config.databaseConnectors[dbType]({ url: ds.url
|
|
254
|
+
this.connectors[ds.id] = new this.config.databaseConnectors[dbType]({ url: ds.url });
|
|
253
255
|
});
|
|
254
256
|
yield Promise.all(this.config.resources.map((res) => __awaiter(this, void 0, void 0, function* () {
|
|
255
257
|
if (!this.connectors[res.dataSource]) {
|
|
@@ -279,11 +281,6 @@ class AdminForth {
|
|
|
279
281
|
// console.log('⚙️⚙️⚙️ Database discovery done', JSON.stringify(this.config.resources, null, 2));
|
|
280
282
|
});
|
|
281
283
|
}
|
|
282
|
-
init() {
|
|
283
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
284
|
-
console.log('AdminForth init');
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
284
|
bundleNow(_b) {
|
|
288
285
|
return __awaiter(this, arguments, void 0, function* ({ hotReload = false, verbose = false }) {
|
|
289
286
|
this.codeInjector.bundleNow({ hotReload, verbose });
|
|
@@ -388,11 +385,11 @@ class AdminForth {
|
|
|
388
385
|
return { error: 'Unauthorized' };
|
|
389
386
|
}
|
|
390
387
|
username = user.data[0][this.config.auth.usernameField];
|
|
391
|
-
userFullName = user.data[0][this.config.auth.
|
|
388
|
+
userFullName = user.data[0][this.config.auth.userFullNameField];
|
|
392
389
|
}
|
|
393
390
|
const userData = {
|
|
394
391
|
[this.config.auth.usernameField]: username,
|
|
395
|
-
[this.config.auth.
|
|
392
|
+
[this.config.auth.userFullNameField]: userFullName
|
|
396
393
|
};
|
|
397
394
|
return {
|
|
398
395
|
user: userData,
|
|
@@ -409,6 +406,7 @@ class AdminForth {
|
|
|
409
406
|
usernameField: this.config.auth.usernameField,
|
|
410
407
|
},
|
|
411
408
|
adminUser,
|
|
409
|
+
version: ADMINFORTH_VERSION,
|
|
412
410
|
};
|
|
413
411
|
}),
|
|
414
412
|
});
|
|
@@ -433,8 +431,12 @@ class AdminForth {
|
|
|
433
431
|
server.endpoint({
|
|
434
432
|
method: 'POST',
|
|
435
433
|
path: '/get_resource_data',
|
|
436
|
-
handler: (_g) => __awaiter(this, [_g], void 0, function* ({ body }) {
|
|
437
|
-
|
|
434
|
+
handler: (_g) => __awaiter(this, [_g], void 0, function* ({ body, adminUser }) {
|
|
435
|
+
var _h, _j, _k, _l, _m, _o, _p, _q;
|
|
436
|
+
const { resourceId, source } = body;
|
|
437
|
+
if (['show', 'list'].includes(source) === false) {
|
|
438
|
+
return { error: 'Invalid source, should be list or show' };
|
|
439
|
+
}
|
|
438
440
|
if (!this.statuses.dbDiscover) {
|
|
439
441
|
return { error: 'Database discovery not started' };
|
|
440
442
|
}
|
|
@@ -445,6 +447,16 @@ class AdminForth {
|
|
|
445
447
|
if (!resource) {
|
|
446
448
|
return { error: `Resource ${resourceId} not found` };
|
|
447
449
|
}
|
|
450
|
+
if ((_j = (_h = resource.hooks) === null || _h === void 0 ? void 0 : _h[source]) === null || _j === void 0 ? void 0 : _j.beforeDatasourceRequest) {
|
|
451
|
+
const resp = yield ((_l = (_k = resource.hooks) === null || _k === void 0 ? void 0 : _k[source]) === null || _l === void 0 ? void 0 : _l.beforeDatasourceRequest({ resource, query: body, adminUser }));
|
|
452
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
453
|
+
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
454
|
+
}
|
|
455
|
+
if (resp.error) {
|
|
456
|
+
return { error: resp.error };
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const { limit, offset, filters, sort } = body;
|
|
448
460
|
const data = yield this.connectors[resource.dataSource].getData({
|
|
449
461
|
resource,
|
|
450
462
|
limit,
|
|
@@ -452,14 +464,52 @@ class AdminForth {
|
|
|
452
464
|
filters,
|
|
453
465
|
sort,
|
|
454
466
|
});
|
|
467
|
+
// for foreign keys, add references
|
|
468
|
+
yield Promise.all(resource.columns.filter((col) => col.foreignResource).map((col) => __awaiter(this, void 0, void 0, function* () {
|
|
469
|
+
const targetResource = this.config.resources.find((res) => res.resourceId == col.foreignResource.resourceId);
|
|
470
|
+
const targetConnector = this.connectors[targetResource.dataSource];
|
|
471
|
+
const targetResourcePkField = targetResource.columns.find((col) => col.primaryKey).name;
|
|
472
|
+
const targetData = yield targetConnector.getData({
|
|
473
|
+
resource: targetResource,
|
|
474
|
+
limit: limit,
|
|
475
|
+
offset: 0,
|
|
476
|
+
filters: [
|
|
477
|
+
{
|
|
478
|
+
field: targetResourcePkField,
|
|
479
|
+
operator: AdminForthFilterOperators.IN,
|
|
480
|
+
value: data.data.map((item) => item[col.name]),
|
|
481
|
+
}
|
|
482
|
+
],
|
|
483
|
+
sort: [],
|
|
484
|
+
});
|
|
485
|
+
const targetDataMap = targetData.data.reduce((acc, item) => {
|
|
486
|
+
acc[item[targetResourcePkField]] = {
|
|
487
|
+
label: targetResource.itemLabel ? targetResource.itemLabel(item) : item[targetResourcePkField],
|
|
488
|
+
pk: item[targetResourcePkField],
|
|
489
|
+
};
|
|
490
|
+
return acc;
|
|
491
|
+
}, {});
|
|
492
|
+
data.data.forEach((item) => {
|
|
493
|
+
item[col.name] = targetDataMap[item[col.name]];
|
|
494
|
+
});
|
|
495
|
+
})));
|
|
496
|
+
if ((_o = (_m = resource.hooks) === null || _m === void 0 ? void 0 : _m[source]) === null || _o === void 0 ? void 0 : _o.afterDatasourceRequest) {
|
|
497
|
+
const resp = yield ((_q = (_p = resource.hooks) === null || _p === void 0 ? void 0 : _p[source]) === null || _q === void 0 ? void 0 : _q.afterDatasourceRequest({ resource, response: data.data, adminUser }));
|
|
498
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
499
|
+
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
500
|
+
}
|
|
501
|
+
if (resp.error) {
|
|
502
|
+
return { error: resp.error };
|
|
503
|
+
}
|
|
504
|
+
}
|
|
455
505
|
return Object.assign(Object.assign({}, data), { options: resource === null || resource === void 0 ? void 0 : resource.options });
|
|
456
506
|
}),
|
|
457
507
|
});
|
|
458
508
|
server.endpoint({
|
|
459
509
|
method: 'POST',
|
|
460
510
|
path: '/get_resource_foreign_data',
|
|
461
|
-
handler: (
|
|
462
|
-
var
|
|
511
|
+
handler: (_r) => __awaiter(this, [_r], void 0, function* ({ body, adminUser }) {
|
|
512
|
+
var _s, _t, _u, _v;
|
|
463
513
|
const { resourceId, column } = body;
|
|
464
514
|
if (!this.statuses.dbDiscover) {
|
|
465
515
|
return { error: 'Database discovery not started' };
|
|
@@ -477,8 +527,8 @@ class AdminForth {
|
|
|
477
527
|
}
|
|
478
528
|
const targetResourceId = columnConfig.foreignResource.resourceId;
|
|
479
529
|
const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
|
|
480
|
-
if ((
|
|
481
|
-
const resp = yield ((
|
|
530
|
+
if ((_s = columnConfig.foreignResource.hooks) === null || _s === void 0 ? void 0 : _s.beforeDatasourceRequest) {
|
|
531
|
+
const resp = yield ((_t = column.foreignResource.hooks) === null || _t === void 0 ? void 0 : _t.beforeDatasourceRequest({ query: body, adminUser }));
|
|
482
532
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
483
533
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
484
534
|
}
|
|
@@ -506,8 +556,8 @@ class AdminForth {
|
|
|
506
556
|
const response = {
|
|
507
557
|
items
|
|
508
558
|
};
|
|
509
|
-
if ((
|
|
510
|
-
const resp = yield ((
|
|
559
|
+
if ((_u = columnConfig.foreignResource.hooks) === null || _u === void 0 ? void 0 : _u.afterDatasourceResponse) {
|
|
560
|
+
const resp = yield ((_v = column.foreignResource.hooks) === null || _v === void 0 ? void 0 : _v.afterDatasourceResponse({ response, adminUser }));
|
|
511
561
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
512
562
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
513
563
|
}
|
|
@@ -521,7 +571,7 @@ class AdminForth {
|
|
|
521
571
|
server.endpoint({
|
|
522
572
|
method: 'POST',
|
|
523
573
|
path: '/get_min_max_for_columns',
|
|
524
|
-
handler: (
|
|
574
|
+
handler: (_w) => __awaiter(this, [_w], void 0, function* ({ body }) {
|
|
525
575
|
const { resourceId } = body;
|
|
526
576
|
if (!this.statuses.dbDiscover) {
|
|
527
577
|
return { error: 'Database discovery not started' };
|
|
@@ -547,40 +597,12 @@ class AdminForth {
|
|
|
547
597
|
return item;
|
|
548
598
|
}),
|
|
549
599
|
});
|
|
550
|
-
server.endpoint({
|
|
551
|
-
method: 'POST',
|
|
552
|
-
path: '/get_record',
|
|
553
|
-
handler: (_p) => __awaiter(this, [_p], void 0, function* ({ body, adminUser }) {
|
|
554
|
-
var _q, _r;
|
|
555
|
-
const { resourceId, primaryKey } = body;
|
|
556
|
-
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
557
|
-
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
|
|
558
|
-
const connector = this.connectors[resource.dataSource];
|
|
559
|
-
const record = yield connector.getRecordByPrimaryKey(resource, primaryKey);
|
|
560
|
-
if (!record) {
|
|
561
|
-
return { error: `Record with ${primaryKeyColumn.name} ${primaryKey} not found` };
|
|
562
|
-
}
|
|
563
|
-
// execute hook if needed
|
|
564
|
-
if ((_q = resource.hooks) === null || _q === void 0 ? void 0 : _q.show) {
|
|
565
|
-
const resp = yield ((_r = resource.hooks) === null || _r === void 0 ? void 0 : _r.show({ resource, record, adminUser }));
|
|
566
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
567
|
-
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
568
|
-
}
|
|
569
|
-
if (resp.error) {
|
|
570
|
-
return { error: resp.error };
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
const labler = resource.itemLabel || ((record) => `${resource.label} ${record[primaryKeyColumn.name]}`);
|
|
574
|
-
record._label = labler(record);
|
|
575
|
-
return record;
|
|
576
|
-
})
|
|
577
|
-
});
|
|
578
600
|
server.endpoint({
|
|
579
601
|
noAuth: true, // TODO
|
|
580
602
|
method: 'POST',
|
|
581
603
|
path: '/create_record',
|
|
582
|
-
handler: (
|
|
583
|
-
var
|
|
604
|
+
handler: (_x) => __awaiter(this, [_x], void 0, function* ({ body, adminUser }) {
|
|
605
|
+
var _y, _z, _0, _1, _2, _3, _4, _5, _6;
|
|
584
606
|
console.log('create_record', body, this.config.resources);
|
|
585
607
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
586
608
|
if (!resource) {
|
|
@@ -588,8 +610,8 @@ class AdminForth {
|
|
|
588
610
|
}
|
|
589
611
|
const record = body['record'];
|
|
590
612
|
// execute hook if needed
|
|
591
|
-
if ((
|
|
592
|
-
const resp = yield ((
|
|
613
|
+
if ((_z = (_y = resource.hooks) === null || _y === void 0 ? void 0 : _y.create) === null || _z === void 0 ? void 0 : _z.beforeSave) {
|
|
614
|
+
const resp = yield ((_1 = (_0 = resource.hooks) === null || _0 === void 0 ? void 0 : _0.create) === null || _1 === void 0 ? void 0 : _1.beforeSave({ resource, record, adminUser }));
|
|
593
615
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
594
616
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
595
617
|
}
|
|
@@ -605,7 +627,7 @@ class AdminForth {
|
|
|
605
627
|
});
|
|
606
628
|
}
|
|
607
629
|
}
|
|
608
|
-
if (((
|
|
630
|
+
if (((_2 = column.required) === null || _2 === void 0 ? void 0 : _2.create) && body['record'][column.name] === undefined) {
|
|
609
631
|
return { error: `Column '${column.name}' is required` };
|
|
610
632
|
}
|
|
611
633
|
if (column.isUnique) {
|
|
@@ -630,8 +652,8 @@ class AdminForth {
|
|
|
630
652
|
const connector = this.connectors[resource.dataSource];
|
|
631
653
|
yield connector.createRecord({ resource, record });
|
|
632
654
|
// execute hook if needed
|
|
633
|
-
if ((
|
|
634
|
-
const resp = yield ((
|
|
655
|
+
if ((_4 = (_3 = resource.hooks) === null || _3 === void 0 ? void 0 : _3.create) === null || _4 === void 0 ? void 0 : _4.afterSave) {
|
|
656
|
+
const resp = yield ((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.create) === null || _6 === void 0 ? void 0 : _6.afterSave({ resource, record, adminUser }));
|
|
635
657
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
636
658
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
637
659
|
}
|
|
@@ -648,8 +670,8 @@ class AdminForth {
|
|
|
648
670
|
noAuth: true, // TODO
|
|
649
671
|
method: 'POST',
|
|
650
672
|
path: '/update_record',
|
|
651
|
-
handler: (
|
|
652
|
-
var
|
|
673
|
+
handler: (_7) => __awaiter(this, [_7], void 0, function* ({ body, adminUser }) {
|
|
674
|
+
var _8, _9, _10, _11, _12, _13, _14, _15;
|
|
653
675
|
console.log('update_record', body);
|
|
654
676
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
655
677
|
if (!resource) {
|
|
@@ -664,8 +686,8 @@ class AdminForth {
|
|
|
664
686
|
}
|
|
665
687
|
const record = body['record'];
|
|
666
688
|
// execute hook if needed
|
|
667
|
-
if ((
|
|
668
|
-
const resp = yield ((
|
|
689
|
+
if ((_9 = (_8 = resource.hooks) === null || _8 === void 0 ? void 0 : _8.edit) === null || _9 === void 0 ? void 0 : _9.beforeSave) {
|
|
690
|
+
const resp = yield ((_11 = (_10 = resource.hooks) === null || _10 === void 0 ? void 0 : _10.edit) === null || _11 === void 0 ? void 0 : _11.beforeSave({ resource, record, adminUser }));
|
|
669
691
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
670
692
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
671
693
|
}
|
|
@@ -683,8 +705,8 @@ class AdminForth {
|
|
|
683
705
|
yield connector.updateRecord({ resource, recordId, record, newValues });
|
|
684
706
|
}
|
|
685
707
|
// execute hook if needed
|
|
686
|
-
if ((
|
|
687
|
-
const resp = yield ((
|
|
708
|
+
if ((_13 = (_12 = resource.hooks) === null || _12 === void 0 ? void 0 : _12.edit) === null || _13 === void 0 ? void 0 : _13.afterSave) {
|
|
709
|
+
const resp = yield ((_15 = (_14 = resource.hooks) === null || _14 === void 0 ? void 0 : _14.edit) === null || _15 === void 0 ? void 0 : _15.afterSave({ resource, record, adminUser }));
|
|
688
710
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
689
711
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
690
712
|
}
|
|
@@ -701,16 +723,16 @@ class AdminForth {
|
|
|
701
723
|
noAuth: true, // TODO
|
|
702
724
|
method: 'POST',
|
|
703
725
|
path: '/delete_record',
|
|
704
|
-
handler: (
|
|
705
|
-
var
|
|
726
|
+
handler: (_16) => __awaiter(this, [_16], void 0, function* ({ body, adminUser }) {
|
|
727
|
+
var _17, _18, _19, _20, _21, _22, _23, _24;
|
|
706
728
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
707
729
|
const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
|
|
708
730
|
if (!resource) {
|
|
709
731
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
710
732
|
}
|
|
711
733
|
// execute hook if needed
|
|
712
|
-
if ((
|
|
713
|
-
const resp = yield ((
|
|
734
|
+
if ((_18 = (_17 = resource.hooks) === null || _17 === void 0 ? void 0 : _17.delete) === null || _18 === void 0 ? void 0 : _18.beforeSave) {
|
|
735
|
+
const resp = yield ((_20 = (_19 = resource.hooks) === null || _19 === void 0 ? void 0 : _19.delete) === null || _20 === void 0 ? void 0 : _20.beforeSave({ resource, record, adminUser }));
|
|
714
736
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
715
737
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
716
738
|
}
|
|
@@ -721,8 +743,8 @@ class AdminForth {
|
|
|
721
743
|
const connector = this.connectors[resource.dataSource];
|
|
722
744
|
yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
|
|
723
745
|
// execute hook if needed
|
|
724
|
-
if ((
|
|
725
|
-
const resp = yield ((
|
|
746
|
+
if ((_22 = (_21 = resource.hooks) === null || _21 === void 0 ? void 0 : _21.delete) === null || _22 === void 0 ? void 0 : _22.afterSave) {
|
|
747
|
+
const resp = yield ((_24 = (_23 = resource.hooks) === null || _23 === void 0 ? void 0 : _23.delete) === null || _24 === void 0 ? void 0 : _24.afterSave({ resource, record, adminUser }));
|
|
726
748
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
727
749
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
728
750
|
}
|
|
@@ -739,7 +761,7 @@ class AdminForth {
|
|
|
739
761
|
noAuth: true, // TODO
|
|
740
762
|
method: 'POST',
|
|
741
763
|
path: '/start_bulk_action',
|
|
742
|
-
handler: (
|
|
764
|
+
handler: (_25) => __awaiter(this, [_25], void 0, function* ({ body }) {
|
|
743
765
|
const { resourceId, actionId, recordIds } = body;
|
|
744
766
|
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
745
767
|
if (!resource) {
|
package/dist/modules/utils.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import fs from 'fs';
|
|
1
4
|
export function guessLabelFromName(name) {
|
|
2
5
|
if (name.includes('_')) {
|
|
3
6
|
return name.split('_').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
@@ -10,3 +13,9 @@ export function guessLabelFromName(name) {
|
|
|
10
13
|
return name.split(/(?=[A-Z])/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
11
14
|
}
|
|
12
15
|
}
|
|
16
|
+
let package_json;
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = path.join(path.dirname(__filename), '..');
|
|
19
|
+
export const ADMIN_FORTH_ABSOLUTE_PATH = __dirname;
|
|
20
|
+
package_json = JSON.parse(fs.readFileSync(path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'package.json'), 'utf8'));
|
|
21
|
+
export const ADMINFORTH_VERSION = package_json.version;
|
package/dist/spa/spa/src/App.vue
CHANGED
|
@@ -42,9 +42,6 @@
|
|
|
42
42
|
</p>
|
|
43
43
|
</div>
|
|
44
44
|
<ul class="py-1" role="none">
|
|
45
|
-
<li>
|
|
46
|
-
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white" role="menuitem">Dashboard</a>
|
|
47
|
-
</li>
|
|
48
45
|
<li >
|
|
49
46
|
<span @click="toggleTheme" class=" cursor-pointer flex items-center gap-1 block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white" role="menuitem">
|
|
50
47
|
{{ theme === 'dark' ? 'Light' : 'Dark' }}
|
|
@@ -141,10 +138,6 @@ const routerIsReady = ref(false);
|
|
|
141
138
|
|
|
142
139
|
const loggedIn = computed(() => route.name !== 'login' && routerIsReady.value);
|
|
143
140
|
|
|
144
|
-
watch(loggedIn, (value) => {
|
|
145
|
-
console.log('🔻🔻🔻 loggedIn', value);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
141
|
const theme = ref('light');
|
|
149
142
|
|
|
150
143
|
function toggleTheme() {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<label for="start-time" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{ label }}</label>
|
|
6
6
|
|
|
7
7
|
<div class="relative">
|
|
8
|
-
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5">
|
|
8
|
+
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
9
9
|
<IconCalendar class="w-4 h-4 text-gray-500 dark:text-gray-400"/>
|
|
10
10
|
</div>
|
|
11
11
|
|
|
@@ -97,7 +97,7 @@ const start = computed(() => {
|
|
|
97
97
|
})
|
|
98
98
|
|
|
99
99
|
function updateFromProps() {
|
|
100
|
-
if (props.valueStart
|
|
100
|
+
if (!props.valueStart) {
|
|
101
101
|
datepickerStartEl.value.value = '';
|
|
102
102
|
startTime.value = '';
|
|
103
103
|
}
|
|
@@ -157,5 +157,5 @@ onMounted(() => {
|
|
|
157
157
|
|
|
158
158
|
onBeforeUnmount(() => {
|
|
159
159
|
removeChangeDateListener();
|
|
160
|
-
})
|
|
160
|
+
});
|
|
161
161
|
</script>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<div class="mx-auto grid grid-cols-2 gap-4 mb-2" :class="{hidden: !showTimeInputs}">
|
|
27
27
|
<div>
|
|
28
28
|
<div class="relative">
|
|
29
|
-
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5">
|
|
29
|
+
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
30
30
|
<IconTime class="w-4 h-4 text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-700"/>
|
|
31
31
|
</div>
|
|
32
32
|
|
|
@@ -132,11 +132,11 @@ const end = computed(() => {
|
|
|
132
132
|
})
|
|
133
133
|
|
|
134
134
|
function updateFromProps() {
|
|
135
|
-
if (props.valueStart
|
|
135
|
+
if (!props.valueStart) {
|
|
136
136
|
datepickerStartEl.value.value = '';
|
|
137
137
|
startTime.value = '';
|
|
138
138
|
}
|
|
139
|
-
if (props.valueEnd
|
|
139
|
+
if (!props.valueEnd) {
|
|
140
140
|
datepickerEndEl.value.value = '';
|
|
141
141
|
endTime.value = '';
|
|
142
142
|
}
|
|
@@ -71,33 +71,52 @@ const sliderValue = ref([start.value, end.value]);
|
|
|
71
71
|
|
|
72
72
|
const updateFromSlider =
|
|
73
73
|
debounce((value: [number, number]) => {
|
|
74
|
+
console.log('start end', value)
|
|
74
75
|
start.value = value[0];
|
|
75
76
|
end.value = value[1];
|
|
76
77
|
}, 500);
|
|
77
78
|
|
|
78
|
-
function updateFromProps() {
|
|
79
|
-
if (props.valueStart || props.valueEnd) {
|
|
80
|
-
setFromProps(props.valueStart, props.valueEnd)
|
|
81
|
-
} else {
|
|
82
|
-
clear();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
79
|
onMounted(() => {
|
|
87
|
-
|
|
80
|
+
updateStartFromProps();
|
|
81
|
+
updateEndFromProps();
|
|
88
82
|
|
|
89
|
-
watch(() =>
|
|
90
|
-
|
|
83
|
+
watch(() => props.valueStart, (value) => {
|
|
84
|
+
updateStartFromProps();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
watch(() => props.valueEnd, (value) => {
|
|
88
|
+
updateEndFromProps();
|
|
91
89
|
});
|
|
92
90
|
})
|
|
93
91
|
|
|
92
|
+
function updateStartFromProps() {
|
|
93
|
+
if (props.valueStart || props.valueStart === 0) {
|
|
94
|
+
start.value = props.valueStart ? props.valueStart : minFormatted.value;
|
|
95
|
+
sliderValue.value = [start.value, end.value]
|
|
96
|
+
} else {
|
|
97
|
+
console.log(props.valueStart)
|
|
98
|
+
start.value = minFormatted.value;
|
|
99
|
+
sliderValue.value = [minFormatted.value, end.value];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function updateEndFromProps() {
|
|
104
|
+
if (props.valueEnd || props.valueStart === 0) {
|
|
105
|
+
end.value = props.valueEnd ? props.valueEnd : minFormatted.value;
|
|
106
|
+
sliderValue.value = [start.value, end.value]
|
|
107
|
+
} else {
|
|
108
|
+
end.value = maxFormatted.value;
|
|
109
|
+
sliderValue.value = [start.value, maxFormatted.value];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
94
113
|
watch(start, () => {
|
|
95
|
-
|
|
114
|
+
console.log('⚡ emit', start.value)
|
|
96
115
|
emit('update:valueStart', start.value)
|
|
97
116
|
})
|
|
98
117
|
|
|
99
118
|
watch(end, () => {
|
|
100
|
-
|
|
119
|
+
console.log('⚡ emit', end.value)
|
|
101
120
|
emit('update:valueEnd', end.value);
|
|
102
121
|
})
|
|
103
122
|
|
|
@@ -106,12 +125,6 @@ const clear = () => {
|
|
|
106
125
|
end.value = maxFormatted.value;
|
|
107
126
|
sliderValue.value = [start.value, end.value]
|
|
108
127
|
}
|
|
109
|
-
|
|
110
|
-
function setFromProps(startValue: number, endValue: number) {
|
|
111
|
-
start.value = startValue ? startValue : minFormatted.value
|
|
112
|
-
end.value = endValue ? endValue : maxFormatted.value
|
|
113
|
-
sliderValue.value = [start.value, end.value]
|
|
114
|
-
}
|
|
115
128
|
</script>
|
|
116
129
|
|
|
117
130
|
<style lang="scss" scoped>
|
|
@@ -76,10 +76,14 @@ const showDropdown = ref(false);
|
|
|
76
76
|
const selectedItems = ref([]);
|
|
77
77
|
|
|
78
78
|
function updateFromProps() {
|
|
79
|
-
console.log('⚡ updateFromProps', props.modelValue)
|
|
80
79
|
if (props.modelValue !== undefined) {
|
|
81
80
|
if (props.single) {
|
|
82
|
-
|
|
81
|
+
const el = props.options.find(item => item.value === props.modelValue);
|
|
82
|
+
if (el) {
|
|
83
|
+
selectedItems.value = [el];
|
|
84
|
+
} else {
|
|
85
|
+
selectedItems.value = [];
|
|
86
|
+
}
|
|
83
87
|
} else {
|
|
84
88
|
selectedItems.value = props.options.filter(item => props.modelValue.includes(item.value));
|
|
85
89
|
}
|