adminforth 1.3.14 → 1.3.16
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 +21 -13
- package/dist/dataConnectors/baseConnector.js +18 -15
- package/dist/index.js +3 -0
- package/dist/modules/restApi.js +1 -0
- package/index.ts +4 -1
- package/modules/restApi.ts +1 -2
- package/package.json +2 -2
- package/spa/src/components/Filters.vue +1 -1
- package/spa/src/components/ResourceForm.vue +3 -4
- package/spa/src/router/index.ts +0 -8
- package/spa/src/stores/filters.ts +3 -2
- package/spa/src/views/ListView.vue +16 -25
|
@@ -57,6 +57,21 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
|
|
|
57
57
|
throw new Error('Method not implemented.');
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
async checkUnique(resource: AdminForthResource, column: AdminForthResourceColumn, value: any) {
|
|
61
|
+
process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 checkUnique|||', column, value);
|
|
62
|
+
const existingRecord = await this.getData({
|
|
63
|
+
resource,
|
|
64
|
+
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value }],
|
|
65
|
+
limit: 1,
|
|
66
|
+
sort: [],
|
|
67
|
+
offset: 0,
|
|
68
|
+
getTotals: false
|
|
69
|
+
});
|
|
70
|
+
process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 existingRecord|||', existingRecord);
|
|
71
|
+
|
|
72
|
+
return existingRecord.data.length > 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
60
75
|
async createRecord({ resource, record, adminUser }: {
|
|
61
76
|
resource: AdminForthResource; record: any; adminUser: any;
|
|
62
77
|
}): Promise<{ error?: string; ok: boolean; createdRecord?: any; }> {
|
|
@@ -76,28 +91,21 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
|
|
|
76
91
|
recordWithOriginalValues[col.name] = this.setFieldValue(col, filledRecord[col.name]);
|
|
77
92
|
}
|
|
78
93
|
|
|
79
|
-
|
|
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
|
-
}
|
|
94
|
+
|
|
90
95
|
let error: string | null = null;
|
|
91
|
-
await Promise.
|
|
96
|
+
await Promise.all(
|
|
92
97
|
resource.dataSourceColumns.map(async (col) => {
|
|
98
|
+
|
|
93
99
|
if (col.isUnique && !col.virtual && !error) {
|
|
94
|
-
|
|
100
|
+
|
|
101
|
+
const exists = await this.checkUnique(resource, col, recordWithOriginalValues[col.name]);
|
|
95
102
|
if (exists) {
|
|
96
103
|
error = `Record with ${col.name} ${recordWithOriginalValues[col.name]} already exists`;
|
|
97
104
|
}
|
|
98
105
|
}
|
|
99
106
|
})
|
|
100
107
|
);
|
|
108
|
+
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 error', error);
|
|
101
109
|
if (error) {
|
|
102
110
|
return { error, ok: false };
|
|
103
111
|
}
|
|
@@ -50,6 +50,21 @@ export default class AdminForthBaseConnector {
|
|
|
50
50
|
createRecordOriginalValues({ resource, record }) {
|
|
51
51
|
throw new Error('Method not implemented.');
|
|
52
52
|
}
|
|
53
|
+
checkUnique(resource, column, value) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 checkUnique|||', column, value);
|
|
56
|
+
const existingRecord = yield this.getData({
|
|
57
|
+
resource,
|
|
58
|
+
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value }],
|
|
59
|
+
limit: 1,
|
|
60
|
+
sort: [],
|
|
61
|
+
offset: 0,
|
|
62
|
+
getTotals: false
|
|
63
|
+
});
|
|
64
|
+
process.env.HEAVY_DEBUG && console.log('☝️🪲🪲🪲🪲 existingRecord|||', existingRecord);
|
|
65
|
+
return existingRecord.data.length > 0;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
53
68
|
createRecord(_a) {
|
|
54
69
|
return __awaiter(this, arguments, void 0, function* ({ resource, record, adminUser }) {
|
|
55
70
|
// transform value using setFieldValue and call createRecordOriginalValues
|
|
@@ -66,28 +81,16 @@ export default class AdminForthBaseConnector {
|
|
|
66
81
|
}
|
|
67
82
|
recordWithOriginalValues[col.name] = this.setFieldValue(col, filledRecord[col.name]);
|
|
68
83
|
}
|
|
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
84
|
let error = null;
|
|
83
|
-
yield Promise.
|
|
85
|
+
yield Promise.all(resource.dataSourceColumns.map((col) => __awaiter(this, void 0, void 0, function* () {
|
|
84
86
|
if (col.isUnique && !col.virtual && !error) {
|
|
85
|
-
const exists = yield checkUnique(col, recordWithOriginalValues[col.name]);
|
|
87
|
+
const exists = yield this.checkUnique(resource, col, recordWithOriginalValues[col.name]);
|
|
86
88
|
if (exists) {
|
|
87
89
|
error = `Record with ${col.name} ${recordWithOriginalValues[col.name]} already exists`;
|
|
88
90
|
}
|
|
89
91
|
}
|
|
90
92
|
})));
|
|
93
|
+
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 error', error);
|
|
91
94
|
if (error) {
|
|
92
95
|
return { error, ok: false };
|
|
93
96
|
}
|
package/dist/index.js
CHANGED
|
@@ -185,6 +185,9 @@ class AdminForth {
|
|
|
185
185
|
const connector = this.connectors[resource.dataSource];
|
|
186
186
|
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record createResourceRecord', record);
|
|
187
187
|
const { ok, error, createdRecord } = yield connector.createRecord({ resource, record, adminUser });
|
|
188
|
+
if (!ok) {
|
|
189
|
+
return { ok, error };
|
|
190
|
+
}
|
|
188
191
|
const primaryKey = record[resource.columns.find((col) => col.primaryKey).name];
|
|
189
192
|
// execute hook if needed
|
|
190
193
|
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)) {
|
package/dist/modules/restApi.js
CHANGED
|
@@ -319,6 +319,7 @@ export default class AdminForthRestAPI {
|
|
|
319
319
|
offset,
|
|
320
320
|
filters,
|
|
321
321
|
sort,
|
|
322
|
+
getTotals: true,
|
|
322
323
|
});
|
|
323
324
|
// for foreign keys, add references
|
|
324
325
|
yield Promise.all(resource.columns.filter((col) => col.foreignResource).map((col) => __awaiter(this, void 0, void 0, function* () {
|
package/index.ts
CHANGED
|
@@ -236,7 +236,10 @@ class AdminForth implements IAdminForth {
|
|
|
236
236
|
const connector = this.connectors[resource.dataSource];
|
|
237
237
|
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record createResourceRecord', record);
|
|
238
238
|
const { ok, error, createdRecord } = await connector.createRecord({ resource, record, adminUser });
|
|
239
|
-
|
|
239
|
+
if (!ok) {
|
|
240
|
+
return { ok, error };
|
|
241
|
+
}
|
|
242
|
+
|
|
240
243
|
const primaryKey = record[resource.columns.find((col) => col.primaryKey).name];
|
|
241
244
|
|
|
242
245
|
// execute hook if needed
|
package/modules/restApi.ts
CHANGED
|
@@ -261,8 +261,6 @@ export default class AdminForthRestAPI {
|
|
|
261
261
|
},
|
|
262
262
|
});
|
|
263
263
|
|
|
264
|
-
|
|
265
|
-
|
|
266
264
|
function checkAccess(action: AllowedActionsEnum, allowedActions: AllowedActions): { allowed: boolean, error?: string } {
|
|
267
265
|
const allowed = (allowedActions[action] as boolean | string | undefined);
|
|
268
266
|
if (allowed !== true) {
|
|
@@ -383,6 +381,7 @@ export default class AdminForthRestAPI {
|
|
|
383
381
|
offset,
|
|
384
382
|
filters,
|
|
385
383
|
sort,
|
|
384
|
+
getTotals: true,
|
|
386
385
|
});
|
|
387
386
|
// for foreign keys, add references
|
|
388
387
|
await Promise.all(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adminforth",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.16",
|
|
4
4
|
"description": "OpenSource Vue3 powered forth-generation admin panel",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
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
26
|
"fuse.js": "^7.0.0",
|
|
@@ -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
|
|
package/spa/src/router/index.ts
CHANGED
|
@@ -3,11 +3,12 @@ import { defineStore } from 'pinia';
|
|
|
3
3
|
|
|
4
4
|
export const useFiltersStore = defineStore('filters', () => {
|
|
5
5
|
const filters: Ref<any[]> = ref([]);
|
|
6
|
+
|
|
6
7
|
const setFilter = (filter: any) => {
|
|
7
|
-
filters.value
|
|
8
|
+
filters.value.push(filter);
|
|
8
9
|
}
|
|
9
10
|
const setFilters = (f: any) => {
|
|
10
|
-
filters.value =
|
|
11
|
+
filters.value = f;
|
|
11
12
|
}
|
|
12
13
|
const getFilters = () => {
|
|
13
14
|
return filters.value;
|
|
@@ -117,8 +117,6 @@ import { initFlowbite } from 'flowbite';
|
|
|
117
117
|
import { computed, onMounted, ref, watch } from 'vue';
|
|
118
118
|
import { useRoute } from 'vue-router';
|
|
119
119
|
import { showErrorTost } from '@/composables/useFrontendApi'
|
|
120
|
-
|
|
121
|
-
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
122
120
|
import { getCustomComponent } from '@/utils';
|
|
123
121
|
|
|
124
122
|
|
|
@@ -151,20 +149,7 @@ const DEFAULT_PAGE_SIZE = 10;
|
|
|
151
149
|
const pageSize = computed(() => coreStore.resource?.options?.listPageSize || DEFAULT_PAGE_SIZE);
|
|
152
150
|
|
|
153
151
|
|
|
154
|
-
watch([page], async () => {
|
|
155
|
-
await getList();
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
watch(()=>filtersStore.filters, async () => {
|
|
159
|
-
page.value = 1;
|
|
160
|
-
checkboxes.value = [];
|
|
161
|
-
|
|
162
|
-
await getList();
|
|
163
|
-
}, {deep: true});
|
|
164
152
|
|
|
165
|
-
watch([sort], async () => {
|
|
166
|
-
await getList();
|
|
167
|
-
}, {deep: true});
|
|
168
153
|
|
|
169
154
|
|
|
170
155
|
async function getList() {
|
|
@@ -222,6 +207,8 @@ async function init() {
|
|
|
222
207
|
resourceId: route.params.resourceId
|
|
223
208
|
});
|
|
224
209
|
|
|
210
|
+
// !!! clear filters should be in same tick with sort assignment so that watch can catch it
|
|
211
|
+
filtersStore.clearFilters();
|
|
225
212
|
if (coreStore.resource.options?.defaultSort) {
|
|
226
213
|
sort.value = [{
|
|
227
214
|
field: coreStore.resource.options.defaultSort.columnName,
|
|
@@ -230,8 +217,8 @@ async function init() {
|
|
|
230
217
|
} else {
|
|
231
218
|
sort.value = [];
|
|
232
219
|
}
|
|
233
|
-
|
|
234
|
-
await getList();
|
|
220
|
+
console.log('↘️init fired');
|
|
221
|
+
// await getList();
|
|
235
222
|
columnsMinMax.value = await callAdminForthApi({
|
|
236
223
|
path: '/get_min_max_for_columns',
|
|
237
224
|
method: 'POST',
|
|
@@ -241,18 +228,22 @@ async function init() {
|
|
|
241
228
|
});
|
|
242
229
|
}
|
|
243
230
|
|
|
231
|
+
watch([page, sort, () => filtersStore.filters], async () => {
|
|
232
|
+
console.log('↘️watch fired getList');
|
|
233
|
+
await getList();
|
|
234
|
+
}, { deep: true });
|
|
235
|
+
|
|
236
|
+
watch(() => filtersStore.filters, async (to, from) => {
|
|
237
|
+
page.value = 1;
|
|
238
|
+
checkboxes.value = [];
|
|
239
|
+
}, {deep: true});
|
|
240
|
+
|
|
244
241
|
onMounted(async () => {
|
|
242
|
+
console.log('🧱onMounted fired');
|
|
245
243
|
initFlowbite();
|
|
246
|
-
|
|
247
244
|
await init();
|
|
248
|
-
|
|
249
245
|
});
|
|
250
246
|
|
|
251
|
-
|
|
252
|
-
watch(() => route.params.resourceId, async () => {
|
|
253
|
-
filtersStore.setFilters([]);
|
|
254
|
-
checkboxes.value = [];
|
|
255
|
-
await init();
|
|
256
|
-
});
|
|
247
|
+
|
|
257
248
|
|
|
258
249
|
</script>
|