6767
6868 <ResourceForm
6969 v-else
70+ ref =" resourceFormRef"
7071 :record =" record"
7172 :resource =" coreStore.resource!"
7273 @update:record =" onUpdateRecord"
@@ -98,14 +99,15 @@ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
9899import { useCoreStore } from ' @/stores/core' ;
99100import { callAdminForthApi , getCustomComponent ,checkAcessByAllowedActions , initThreeDotsDropdown } from ' @/utils' ;
100101import { IconFloppyDiskSolid } from ' @iconify-prerendered/vue-flowbite' ;
101- import { onMounted , ref , watch } from ' vue' ;
102+ import { onMounted , ref , watch , nextTick } from ' vue' ;
102103import { useRoute , useRouter } from ' vue-router' ;
103104import { computed } from ' vue' ;
104105import { showErrorTost } from ' @/composables/useFrontendApi' ;
105106import ThreeDotsMenu from ' @/components/ThreeDotsMenu.vue' ;
106107import adminforth from ' @/adminforth' ;
107108import { useI18n } from ' vue-i18n' ;
108109import { type AdminForthComponentDeclarationFull } from ' @/types/Common.js' ;
110+ import type { AdminForthResourceColumn } from ' @/types/Back' ;
109111
110112const isValid = ref (false );
111113const validating = ref (false );
@@ -122,6 +124,8 @@ const coreStore = useCoreStore();
122124
123125const { t } = useI18n ();
124126
127+ const resourceFormRef = ref <InstanceType <typeof ResourceForm > | null >(null );
128+
125129const createSaveButtonInjection = computed <AdminForthComponentDeclarationFull | null >(() => {
126130 const raw: any = coreStore .resourceOptions ?.pageInjections ?.create ?.saveButton as any ;
127131 if (! raw ) return null ;
@@ -180,6 +184,8 @@ onMounted(async () => {
180184async function saveRecord(opts ? : { confirmationResult? : any }) {
181185 if (! isValid .value ) {
182186 validating .value = true ;
187+ await nextTick ();
188+ scrollToInvalidField ();
183189 return ;
184190 } else {
185191 validating .value = false ;
@@ -217,5 +223,23 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
217223 }
218224}
219225
226+ function scrollToInvalidField() {
227+ let columnsWithErrors: {column: AdminForthResourceColumn , error: string }[] = [];
228+ for (const column of resourceFormRef .value ?.editableColumns || []) {
229+ const error = resourceFormRef .value ?.columnError (column );
230+ if (error ) {
231+ columnsWithErrors .push ({column , error });
232+ }
233+ }
234+ const errorMessage = t (' Failed to save. Please fix errors for the following fields:' ) + ' <ul class="mt-2 list-disc list-inside">' + columnsWithErrors .map (c => ` <li><strong>${c .column .label || c .column .name }</strong>: ${c .error }</li> ` ).join (' ' ) + ' </ul>' ;
235+ adminforth .alert ({
236+ messageHtml: errorMessage ,
237+ variant: ' danger'
238+ });
239+ const firstInvalidElement = document .querySelector (' .af-invalid-field-message' );
240+ if (firstInvalidElement ) {
241+ firstInvalidElement .scrollIntoView ({ behavior: ' smooth' , block: ' center' });
242+ }
243+ }
220244
221245 </script >
0 commit comments