import { Entity , EntityReference , ExportConfig , Properties , Property , ResolvedEntitySchema } from "../../models" ; import { getValueInPath } from "./objects" ; interface Header { key : string ; label : string ; } export function downloadCSV < M > ( data , additionalData , schema , path , exportConfig ) { const properties = schema . properties ; const headers = getExportHeaders ( properties , path , exportConfig ) ; const exportableData = getExportableData ( data , additionalData , properties , headers ) ; const headersData = entryToCSVRow ( headers . map ( h => h . label ) ) ; const csvData = exportableData . map ( entry => entryToCSVRow ( entry ) ) ; downloadBlob ( [ headersData , ... csvData ] , `${ schema . name }.csv` , "text/csv" ) ; } export function getExportableData ( data , additionalData , properties , headers ) { const mergedData = data . map ( e => ( { id : e . id , ... processCSVValues ( e . values as any , properties ) } ) ) ; if ( additionalData ) { additionalData . forEach ( ( additional , index ) => { mergedData [ index ] = { ... mergedData [ index ] , ... additional } ; } ) ; } return mergedData && mergedData . map ( ( entry ) => { return headers . map ( ( header ) => getValueInPath ( entry , header . key ) ) ; } ) ; } function getExportHeaders < M extends { [ Key ] : any ; } , UserType > ( properties , path , exportConfig ? ) { const headers = [ { label : "id" , key : "id" } , ... Object . entries ( properties ) . map ( ( [ childKey , property ] ) => getHeaders ( property , childKey , "" ) ) . flat ( ) ] ; if ( exportConfig ?. additionalColumns ) { headers . push ( ... exportConfig . additionalColumns . map ( ( column ) => ( { label : column . key , key : column . key } ) ) ) ; } return headers ; } param property param propertyKey param prefix function getHeaders ( property , propertyKey , prefix = "" ) { const currentKey = prefix ? `${ prefix }.${ propertyKey }` : propertyKey ; if ( property . dataType === "map" && property . properties ) { return Object . entries ( property . properties ) . map ( ( [ childKey , p ] ) => getHeaders ( p , childKey , currentKey ) ) . flat ( ) ; } else { return [ { label : currentKey , key : currentKey } ] ; } } function processCSVValue ( inputValue , property ) { let value ; if ( property . dataType === "map" && property . properties ) { value = processCSVValues ( inputValue , property . properties as Properties ) ; } else if ( property . dataType === "array" ) { if ( property . of && Array . isArray ( inputValue ) ) { if ( property . of . dataType === "map" ) { value = inputValue . map ( ( e ) => JSON . stringify ( e ) ) ; } else { value = inputValue . map ( ( e ) => processCSVValue ( e , property . of as Property ) ) ; } } else { value = inputValue ; } } else if ( property . dataType === "reference" ) { const ref = inputValue ? inputValue as EntityReference : undefined ; value = ref ? ref . pathWithId : null ; } else if ( property . dataType === "timestamp" ) { value = inputValue ? inputValue . getTime ( ) : null ; } else { value = inputValue ; } return value ; } function processCSVValues < M extends { [ Key ] : any ; } > ( inputValues , properties ) { const updatedValues = Object . entries ( properties ) . map ( ( [ key , property ] ) => { const inputValue = inputValues && ( inputValues as any ) [ key ] ; const updatedValue = processCSVValue ( inputValue , property as Property ) ; if ( updatedValue === undefined ) return { } ; return ( { [ key ] : updatedValue } ) ; } ) . reduce ( ( a , b ) => ( { ... a , ... b } ) , { } ) as Record < keyof M , any > ; return { ... inputValues , ... updatedValues } ; } function entryToCSVRow ( entry ) { return entry . map ( ( v ) => { if ( v === null || v === undefined ) return "" ; const s = String ( v ) ; return '"' + s . replaceAll ( '"' , '""' ) + '"' ; } ) . join ( "," ) + "\r\n" ; } export function downloadBlob ( content , filename , contentType ) { const blob = new Blob ( content , { type : contentType } ) ; const url = URL . createObjectURL ( blob ) ; const pom = document . createElement ( "a" ) ; pom . href = url ; pom . setAttribute ( "download" , filename ) ; pom . click ( ) ; }