11import type { Data , Fields , Info , RowHook , TableHook } from "../../../types"
2- import type { Meta , Errors } from "../types"
2+ import type { Meta , Error , Errors } from "../types"
33import { v4 } from "uuid"
4+ import { ErrorSources } from "../../../types"
45
56export const addErrorsAndRunHooks = async < T extends string > (
67 data : ( Data < T > & Partial < Meta > ) [ ] ,
@@ -11,25 +12,27 @@ export const addErrorsAndRunHooks = async <T extends string>(
1112) : Promise < ( Data < T > & Meta ) [ ] > => {
1213 const errors : Errors = { }
1314
14- const addHookError = ( rowIndex : number , fieldKey : T , error : Info ) => {
15+ const addError = ( source : ErrorSources , rowIndex : number , fieldKey : T , error : Info ) => {
1516 errors [ rowIndex ] = {
1617 ...errors [ rowIndex ] ,
17- [ fieldKey ] : error ,
18+ [ fieldKey ] : { ... error , source } ,
1819 }
1920 }
2021
2122 if ( tableHook ) {
22- data = await tableHook ( data , addHookError )
23+ data = await tableHook ( data , ( ... props ) => addError ( ErrorSources . Table , ... props ) )
2324 }
2425
2526 if ( rowHook ) {
26- if ( changedRowIndexes != null ) {
27+ if ( changedRowIndexes ) {
2728 for ( const index of changedRowIndexes ) {
28- data [ index ] = await rowHook ( data [ index ] , ( ...props ) => addHookError ( index , ...props ) , data )
29+ data [ index ] = await rowHook ( data [ index ] , ( ...props ) => addError ( ErrorSources . Row , index , ...props ) , data )
2930 }
3031 } else {
3132 data = await Promise . all (
32- data . map ( async ( value , index ) => rowHook ( value , ( ...props ) => addHookError ( index , ...props ) , data ) ) ,
33+ data . map ( async ( value , index ) =>
34+ rowHook ( value , ( ...props ) => addError ( ErrorSources . Row , index , ...props ) , data ) ,
35+ ) ,
3336 )
3437 }
3538 }
@@ -58,47 +61,43 @@ export const addErrorsAndRunHooks = async <T extends string>(
5861
5962 values . forEach ( ( value , index ) => {
6063 if ( duplicates . has ( value ) ) {
61- errors [ index ] = {
62- ...errors [ index ] ,
63- [ field . key ] : {
64- level : validation . level || "error" ,
65- message : validation . errorMessage || "Field must be unique" ,
66- } ,
67- }
64+ addError ( ErrorSources . Table , index , field . key as T , {
65+ level : validation . level || "error" ,
66+ message : validation . errorMessage || "Field must be unique" ,
67+ } )
6868 }
6969 } )
7070 break
7171 }
7272 case "required" : {
73- data . forEach ( ( entry , index ) => {
73+ const dataToValidate = changedRowIndexes ? changedRowIndexes . map ( ( index ) => data [ index ] ) : data
74+ dataToValidate . forEach ( ( entry , index ) => {
75+ const realIndex = changedRowIndexes ? changedRowIndexes [ index ] : index
7476 if ( entry [ field . key as T ] === null || entry [ field . key as T ] === undefined || entry [ field . key as T ] === "" ) {
75- errors [ index ] = {
76- ...errors [ index ] ,
77- [ field . key ] : {
78- level : validation . level || "error" ,
79- message : validation . errorMessage || "Field is required" ,
80- } ,
81- }
77+ addError ( ErrorSources . Row , realIndex , field . key as T , {
78+ level : validation . level || "error" ,
79+ message : validation . errorMessage || "Field is required" ,
80+ } )
8281 }
8382 } )
83+
8484 break
8585 }
8686 case "regex" : {
87+ const dataToValidate = changedRowIndexes ? changedRowIndexes . map ( ( index ) => data [ index ] ) : data
8788 const regex = new RegExp ( validation . value , validation . flags )
88- data . forEach ( ( entry , index ) => {
89+ dataToValidate . forEach ( ( entry , index ) => {
90+ const realIndex = changedRowIndexes ? changedRowIndexes [ index ] : index
8991 const value = entry [ field . key ] ?. toString ( ) ?? ""
9092 if ( ! value . match ( regex ) ) {
91- errors [ index ] = {
92- ...errors [ index ] ,
93- [ field . key ] : {
94- level : validation . level || "error" ,
95- message :
96- validation . errorMessage ||
97- `Field did not match the regex /${ validation . value } /${ validation . flags } ` ,
98- } ,
99- }
93+ addError ( ErrorSources . Row , realIndex , field . key as T , {
94+ level : validation . level || "error" ,
95+ message :
96+ validation . errorMessage || `Field did not match the regex /${ validation . value } /${ validation . flags } ` ,
97+ } )
10098 }
10199 } )
100+
102101 break
103102 }
104103 }
@@ -112,12 +111,41 @@ export const addErrorsAndRunHooks = async <T extends string>(
112111 }
113112 const newValue = value as Data < T > & Meta
114113
115- if ( errors [ index ] ) {
116- return { ...newValue , __errors : errors [ index ] }
114+ // If we are validating all indexes, or we did full validation on this row - apply all errors
115+ if ( ! changedRowIndexes || changedRowIndexes . includes ( index ) ) {
116+ if ( errors [ index ] ) {
117+ return { ...newValue , __errors : errors [ index ] }
118+ }
119+
120+ if ( ! errors [ index ] && value ?. __errors ) {
121+ return { ...newValue , __errors : null }
122+ }
117123 }
118- if ( ! errors [ index ] && value ?. __errors ) {
119- return { ...newValue , __errors : null }
124+ // if we have not validated this row, keep it's row errors but apply global error changes
125+ else {
126+ // at this point errors[index] contains only table source errors, previous row and table errors are in value.__errors
127+ const hasRowErrors =
128+ value . __errors && Object . values ( value . __errors ) . some ( ( error ) => error . source === ErrorSources . Row )
129+
130+ if ( ! hasRowErrors ) {
131+ if ( errors [ index ] ) {
132+ return { ...newValue , __errors : errors [ index ] }
133+ }
134+ return newValue
135+ }
136+
137+ const errorsWithoutTableError = Object . entries ( value . __errors ! ) . reduce ( ( acc , [ key , value ] ) => {
138+ if ( value . source === ErrorSources . Row ) {
139+ acc [ key ] = value
140+ }
141+ return acc
142+ } , { } as Error )
143+
144+ const newErrors = { ...errorsWithoutTableError , ...errors [ index ] }
145+
146+ return { ...newValue , __errors : newErrors }
120147 }
148+
121149 return newValue
122150 } )
123151}
0 commit comments