From b89e0668aabaf4dbb3192290d37f3da9333512a5 Mon Sep 17 00:00:00 2001 From: Petr Kachanovsky Date: Mon, 10 Feb 2025 12:32:39 +0200 Subject: [PATCH] feat: add suggestOnCreate field to column --- adminforth/modules/configValidator.ts | 45 +++++++++++++++++++++++++ adminforth/spa/src/views/CreateView.vue | 19 +++++++---- adminforth/types/Common.ts | 7 +++- dev-demo/resources/audit_log.ts | 3 ++ dev-demo/resources/description_image.ts | 6 ++++ dev-demo/resources/translation.ts | 3 ++ 6 files changed, 75 insertions(+), 8 deletions(-) diff --git a/adminforth/modules/configValidator.ts b/adminforth/modules/configValidator.ts index a7654b2db..e4ec96418 100644 --- a/adminforth/modules/configValidator.ts +++ b/adminforth/modules/configValidator.ts @@ -400,6 +400,10 @@ export default class ConfigValidator implements IConfigValidator { col.showIn = this.validateAndNormalizeShowIn(resInput, inCol, errors, warnings); + if (col.showIn.create && inCol.fillOnCreate !== undefined) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" is present on crate page and has fillOnCreate`); + } + // check col.required is boolean or object if (inCol.required && !((typeof inCol.required === 'boolean') || (typeof inCol.required === 'object'))) { errors.push(`Resource "${res.resourceId}" column "${col.name}" required must be a boolean or object`); @@ -459,6 +463,47 @@ export default class ConfigValidator implements IConfigValidator { } } } + + // check suggestOnCreate types + if (inCol.suggestOnCreate !== undefined) { + if (!col.showIn.create) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" suggestOnCreate is present, while column is hidden on create page`); + } + + if (inCol.suggestOnCreate === '' || inCol.suggestOnCreate === null) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" suggestOnCreate must not be empty`); + } + + if (!['string', 'number', 'boolean', 'object'].includes(typeof inCol.suggestOnCreate)) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" suggestOnCreate must be a string, number, boolean or object`); + } + + // if suggestOnCreate is string, column should be one of the types with text inputs + if (typeof inCol.suggestOnCreate === 'string' && ![AdminForthDataTypes.STRING, AdminForthDataTypes.DATE, AdminForthDataTypes.DATETIME, AdminForthDataTypes.TIME, AdminForthDataTypes.TEXT, AdminForthDataTypes.RICHTEXT, undefined].includes(inCol.type)) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" suggestOnCreate value does not match type of a column`); + } + + if (typeof inCol.suggestOnCreate === 'number' && ![AdminForthDataTypes.INTEGER, AdminForthDataTypes.FLOAT, AdminForthDataTypes.DECIMAL].includes(inCol.type)) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" suggestOnCreate value does not match type of a column`); + } + + if (typeof inCol.suggestOnCreate === 'boolean' && inCol.type !== AdminForthDataTypes.BOOLEAN) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" suggestOnCreate value does not match type of a column`); + } + + if (inCol.enum && !inCol.enum.map((ei) => ei.value).includes(inCol.suggestOnCreate)) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" suggestOnCreate value is not in enum`); + } + + if (typeof inCol.suggestOnCreate === 'object' && inCol.type !== AdminForthDataTypes.JSON) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" suggestOnCreate value does not match type of a column`); + } + + if (inCol.isArray?.enabled && !Array.isArray(inCol.suggestOnCreate)) { + errors.push(`Resource "${res.resourceId}" column "${col.name}" isArray is enabled but suggestOnCreate is not an array`); + } + } + if (col.foreignResource) { if (!col.foreignResource.resourceId) { diff --git a/adminforth/spa/src/views/CreateView.vue b/adminforth/spa/src/views/CreateView.vue index e20f69143..29caeb842 100644 --- a/adminforth/spa/src/views/CreateView.vue +++ b/adminforth/spa/src/views/CreateView.vue @@ -101,12 +101,7 @@ const record = ref({}); const coreStore = useCoreStore(); -const initalValues = computed(() => { - if (!route.query.values) { - return {}; - } - return JSON.parse(decodeURIComponent(route.query.values)); -}); +const initialValues = ref({}); async function onUpdateRecord(newRecord) { @@ -116,10 +111,20 @@ async function onUpdateRecord(newRecord) { onMounted(async () => { loading.value = true; - record.value = initalValues.value; await coreStore.fetchResourceFull({ resourceId: route.params.resourceId }); + if (route.query.values) { + initialValues.value = JSON.parse(decodeURIComponent(route.query.values)); + } else { + initialValues.value = (coreStore.resource?.columns || []).reduce((acc, column) => { + if (column.suggestOnCreate !== undefined) { + acc[column.name] = column.suggestOnCreate; + } + return acc; + }, {}); + } + record.value = initialValues.value; loading.value = false; checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'create'); initThreeDotsDropdown(); diff --git a/adminforth/types/Common.ts b/adminforth/types/Common.ts index 894e7cf2f..c62970b03 100644 --- a/adminforth/types/Common.ts +++ b/adminforth/types/Common.ts @@ -674,10 +674,15 @@ export interface AdminForthResourceColumnInputCommon { showIn?: ShowInResolved, /** - * Whether AdminForth will show this field in show view. + * Called on the backend when the record is saved to a database. Value returned by `fillOnCreate` will be saved to the database. */ fillOnCreate?: Function, + /** + * Single value that will be substituted in create form. User can change it before saving the record. + */ + suggestOnCreate?: string | number | boolean | object, + /** * Whether AdminForth will request user to enter unique value during creating or editing record. * This option causes AdminForth to make a request to database to check if value is unique. diff --git a/dev-demo/resources/audit_log.ts b/dev-demo/resources/audit_log.ts index 583fb634a..88cab0d07 100644 --- a/dev-demo/resources/audit_log.ts +++ b/dev-demo/resources/audit_log.ts @@ -10,6 +10,9 @@ export default { name: "id", primaryKey: true, required: false, + showIn: { + create: false, + }, fillOnCreate: ({ initialRecord }: any) => uuid(), }, { name: "created_at", required: false }, diff --git a/dev-demo/resources/description_image.ts b/dev-demo/resources/description_image.ts index a1cdcea8a..e289e2b95 100644 --- a/dev-demo/resources/description_image.ts +++ b/dev-demo/resources/description_image.ts @@ -13,11 +13,17 @@ export default { primaryKey: true, required: false, fillOnCreate: ({ initialRecord }: any) => uuid(), + showIn: { + create: false, + }, }, { name: "created_at", required: false, fillOnCreate: ({ initialRecord }: any) => new Date().toISOString(), + showIn: { + create: false, + }, }, { name: "resource_id", required: false }, { name: "record_id", required: false }, diff --git a/dev-demo/resources/translation.ts b/dev-demo/resources/translation.ts index 42ac46232..e94e49cbe 100644 --- a/dev-demo/resources/translation.ts +++ b/dev-demo/resources/translation.ts @@ -87,6 +87,9 @@ export default { { name: "created_at", fillOnCreate: ({ initialRecord, adminUser }: any) => new Date().toISOString(), + showIn: { + create: false, + }, }, { name: "uk_string", type: AdminForthDataTypes.STRING, label: "Ukrainian" }, { name: "ar_string", type: AdminForthDataTypes.STRING, label: "Arabic" },