@@ -138,98 +138,115 @@ const uploadToAWS = async (config, excelData) => {
138138}
139139
140140async function injectFormulasIntoSheet ( workbook , data , config ) {
141- const sheetName = config . dependentSheet
142- const sheet = workbook . getWorksheet ( sheetName ) || workbook . worksheets [ 0 ]
143- if ( ! sheet ) throw new Error ( 'Target sheet not found' )
144141 const lookupSheet = config . lookupSheet
145142 const hiddenSheet = workbook . getWorksheet ( lookupSheet ) || workbook . addWorksheet ( lookupSheet )
146143 hiddenSheet . state = 'veryHidden'
147- const { primaryKey, dependentKeys = [ ] , lookupKeys = [ ] } = config
148- if ( ! primaryKey ) throw new Error ( "Config must specify primaryKey" )
149- const allGroups = [ primaryKey , ...lookupKeys , ...dependentKeys ] . map ( k => Array . isArray ( k ) ? k : [ k ] )
150- const canonicalKeys = allGroups . map ( g => g [ 0 ] ) // always first alias = canonical key
151- hiddenSheet . getRows ( 1 , hiddenSheet . rowCount ) . forEach ( r => {
152- r . eachCell ( c => { c . value = null } )
153- } )
154- hiddenSheet . getRow ( 1 ) . values = canonicalKeys
155- let currentRow = 2
156- data . forEach ( ( row ) => {
157- const baseRow = { }
158- Object . keys ( row ) . forEach ( k => {
159- baseRow [ k . toLowerCase ( ) ] = row [ k ]
160- } )
161- const maxArrayLength = Math . max (
162- ...canonicalKeys . map ( key => Array . isArray ( baseRow [ key ] ) ? baseRow [ key ] . length : 1 )
163- )
164- for ( let i = 0 ; i < maxArrayLength ; i ++ ) {
165- const rowValues = canonicalKeys . map ( key => {
166- const val = baseRow [ key ]
167- if ( Array . isArray ( val ) ) {
168- return val [ i ] || ""
169- } else {
170- return i === 0 ? val || "" : ""
171- }
172- } )
173- hiddenSheet . getRow ( currentRow ) . values = rowValues
174- currentRow ++
175- }
176- } )
177- const lastRow = currentRow - 1
178- const primaryRange = `Lookups!$A$2:$A$${ lastRow } `
179144
180- const findCol = ( aliases ) => {
145+ const headerKeys = [
146+ ...new Set (
147+ data . flatMap ( row => Object . keys ( row ) )
148+ )
149+ ] ;
150+
151+ // Column-wise data insertion into hidden sheet
152+ headerKeys . forEach ( ( key , idx ) => {
153+ const values = data . flatMap ( row => row [ key ] ?? [ ] ) ;
154+ values . forEach ( ( val , rowIndex ) => {
155+ hiddenSheet . getRow ( rowIndex + 2 ) . getCell ( idx + 1 ) . value = val ?? "" ;
156+ } ) ;
157+ hiddenSheet . getRow ( 1 ) . getCell ( idx + 1 ) . value = key ;
158+ } ) ;
159+ const findCol = ( sheet , aliases ) => {
181160 const headerRow = sheet . getRow ( 1 )
182161 const lookupNames = Array . isArray ( aliases ) ? aliases : [ aliases ]
183162
184163 for ( let col = 1 ; col <= sheet . columnCount ; col ++ ) {
185164 const val = headerRow . getCell ( col ) ?. value
186- const text = typeof val === 'object'
187- ? ( val ?. richText ?. map ( rt => rt . text ) . join ( '' ) || val ?. result || '' )
188- : ( val || '' )
189- const normalized = String ( text ) . trim ( ) . toLowerCase ( )
165+ let text = '' ;
166+
167+ if ( typeof val === 'object' ) {
168+ if ( Array . isArray ( val . richText ) ) {
169+ text = val . richText . map ( rt => rt . text ) . join ( '' ) ;
170+ } else if ( val . result ) {
171+ text = val . result ;
172+ }
173+ } else if ( val !== null && val !== undefined ) {
174+ text = val ;
175+ }
176+
177+ const normalized = String ( text || '' ) . trim ( ) . toLowerCase ( ) ;
190178
191179 for ( const alias of lookupNames ) {
192- const normAlias = alias . trim ( ) . toLowerCase ( )
180+ if ( ! alias ) continue ;
181+ const normAlias = String ( alias ) . trim ( ) . toLowerCase ( )
193182 if ( normalized === normAlias ) return col
194- if ( normalized . replace ( / \s + / g, "_" ) === normAlias ) return col // "Country Code" -> country_code
195- if ( normalized . replace ( / [ ^ a - z 0 - 9 ] / gi, "" ) === normAlias . replace ( / [ ^ a - z 0 - 9 ] / gi, "" ) ) return col // remove *, etc.
183+ if ( normalized . replace ( / \s + / g, "_" ) === normAlias ) return col
184+ if ( normalized . replace ( / [ ^ a - z 0 - 9 ] / gi, "" ) === normAlias . replace ( / [ ^ a - z 0 - 9 ] / gi, "" ) ) return col
196185 }
197186 }
198187 return null
199188 }
200- // --- Primary key col ---
201- const colPrimary = findCol ( primaryKey )
202- if ( ! colPrimary ) throw new Error ( `Primary key column ${ primaryKey } not found in sheet` )
189+ for ( const sheetConfig of config . dependentSheet ) {
190+ const sheet = workbook . getWorksheet ( sheetConfig . name ) ;
191+ if ( ! sheet ) throw new Error ( `Sheet ${ sheetConfig . name } not found` ) ;
192+
193+ const lastRow = hiddenSheet . lastRow . number ;
194+
195+ //Primary key dropdown (if exists)
196+ let primaryCol = null ;
197+ let primaryCell = null ;
203198
204- const row = 2
205- const primaryCell = sheet . getRow ( row ) . getCell ( colPrimary )
199+ if ( sheetConfig . primaryKey ) {
200+ primaryCol = findCol ( sheet , sheetConfig . primaryKey ) ;
201+ if ( ! primaryCol ) throw new Error ( `Primary key not found in ${ sheetConfig . name } ` ) ;
202+ primaryCell = sheet . getRow ( 2 ) . getCell ( primaryCol ) ;
203+
204+ const lookupCol = findCol ( hiddenSheet , sheetConfig . primaryKey ) ;
205+ if ( ! lookupCol ) throw new Error ( `Primary key not found in Lookups sheet` ) ;
206+ const lookupColLetter = String . fromCharCode ( 64 + lookupCol ) ;
207+
208+ primaryCell . dataValidation = {
209+ type : "list" ,
210+ allowBlank : true ,
211+ formulae : [ `${ hiddenSheet . name } !$${ lookupColLetter } $2:$${ lookupColLetter } $${ lastRow } ` ] ,
212+ } ;
213+ }
206214 //Dependent dropdowns
207- for ( const depGroup of dependentKeys ) {
208- const colDep = findCol ( depGroup )
209- if ( ! colDep ) continue
210- const depKey = Array . isArray ( depGroup ) ? depGroup [ 0 ] : depGroup
211- const depCell = sheet . getRow ( row ) . getCell ( colDep )
212- const depColLetter = String . fromCharCode ( 65 + canonicalKeys . indexOf ( depKey ) )
215+ for ( const depGroup of sheetConfig . dependentKeys || [ ] ) {
216+ const depCol = findCol ( sheet , depGroup ) ;
217+ if ( ! depCol ) continue ;
218+
219+ const depCell = sheet . getRow ( 2 ) . getCell ( depCol ) ;
220+
221+ const lookupCol = findCol ( hiddenSheet , depGroup ) ;
222+ if ( ! lookupCol ) continue ;
223+ const lookupColLetter = String . fromCharCode ( 64 + lookupCol ) ;
224+
213225 depCell . dataValidation = {
214- type : ' list' ,
226+ type : " list" ,
215227 allowBlank : true ,
216- formulae : [ `Lookups !$${ depColLetter } $2:$${ depColLetter } $${ lastRow } ` ] ,
217- }
228+ formulae : [ `${ hiddenSheet . name } !$${ lookupColLetter } $2:$${ lookupColLetter } $${ lastRow } ` ] ,
229+ } ;
218230 }
219- //Lookup autofill
220- for ( const lookupGroup of lookupKeys ) {
221- const colLookup = findCol ( lookupGroup )
222- if ( ! colLookup ) continue
223- const lookupKey = Array . isArray ( lookupGroup ) ? lookupGroup [ 0 ] : lookupGroup
224- const lookupCell = sheet . getRow ( row ) . getCell ( colLookup )
225- const lookupColIndex = canonicalKeys . indexOf ( lookupKey ) + 1
226- lookupCell . value = {
227- formula : `IF(${ primaryCell . address } ="","",VLOOKUP(${ primaryCell . address } ,Lookups!$A$2:$${ String . fromCharCode ( 64 + canonicalKeys . length ) } $${ lastRow } ,${ lookupColIndex } ,FALSE))`
231+
232+ //Lookup autofill (only if primary key exists)
233+ if ( primaryCol && sheetConfig . lookupKeys ) {
234+ for ( const lookupGroup of sheetConfig . lookupKeys ) {
235+ const lookupCol = findCol ( sheet , lookupGroup ) ;
236+ if ( ! lookupCol ) continue ;
237+ const lookupKey = Array . isArray ( lookupGroup ) ? lookupGroup [ 0 ] : lookupGroup ;
238+ const lookupColIndex = headerKeys . indexOf ( lookupKey ) + 1 ;
239+ const lookupCell = sheet . getRow ( 2 ) . getCell ( lookupCol ) ;
240+
241+ lookupCell . value = {
242+ formula : `IF(${ primaryCell . address } ="","",VLOOKUP(${ primaryCell . address } ,${ hiddenSheet . name } !$A$2:$${ String . fromCharCode (
243+ 64 + headerKeys . length
244+ ) } $${ lastRow } ,${ lookupColIndex } ,FALSE))`,
245+ } ;
228246 }
229247 }
230-
231248 }
232-
249+ }
233250
234251const convertJsonToExcel = ( jsonData ) => {
235252 const workbook = xlsx . utils . book_new ( )
0 commit comments