1
+ import { Alert , Box , Stack , Table , TableBody , TableCell , TableContainer , TableHead , TableRow } from '@mui/material' ;
2
+ import Papa , { ParseError , ParseMeta } from 'papaparse'
3
+ import React , { useCallback , useState } from 'react'
4
+ import { useDropzone } from 'react-dropzone'
5
+ import MapHeaders from './MapHeaders' ;
6
+ import { manditoryHeaders , normaliseHeader , normalisedHeaders } from './normaliseDataHeaders' ;
7
+ import { StepperState } from './LoadDataStepper' ;
8
+
9
+ export type Row = { [ key : string ] : string } ;
10
+ export type Data = Row [ ] ;
11
+ export type Field = string ;
12
+
13
+
14
+ const style = {
15
+ dropArea : {
16
+ width : "100%" ,
17
+ height : "150px" ,
18
+ border : "2px dashed #000" ,
19
+ marginBottom : "10px" ,
20
+ display : "flex" ,
21
+ justifyContent : "center" ,
22
+ alignItems : "center" ,
23
+ cursor : "pointer" ,
24
+ ':hover' : {
25
+ backgroundColor : "#f0f0f0"
26
+ }
27
+ } ,
28
+ dropAreaContainer : {
29
+ display : "flex" ,
30
+ justifyContent : "center" ,
31
+ alignItems : "center" ,
32
+ }
33
+ } ;
34
+
35
+ interface ILoadDataProps {
36
+ state : StepperState ;
37
+ firstTime : boolean ;
38
+ }
39
+
40
+ const validateNormalisedFields = ( fields : Field [ ] ) => {
41
+ const errors : string [ ] = [ ] ;
42
+ // check for mandatory fields
43
+ for ( const field of manditoryHeaders ) {
44
+ if ( ! fields . includes ( field ) ) {
45
+ errors . push ( `${ field } has not been defined` ) ;
46
+ }
47
+ }
48
+ return errors ;
49
+ }
50
+
51
+ const LoadData : React . FC < ILoadDataProps > = ( { state, firstTime} ) => {
52
+ const [ errors , setErrors ] = useState < string [ ] > ( firstTime ? [ ] : validateNormalisedFields ( state . normalisedFields ) ) ;
53
+ const [ showData , setShowData ] = useState < boolean > ( state . data . length > 0 && state . fields . length > 0 ) ;
54
+
55
+ const onDrop = useCallback ( ( acceptedFiles : File [ ] ) => {
56
+ acceptedFiles . forEach ( ( file ) => {
57
+ const reader = new FileReader ( )
58
+
59
+ reader . onabort = ( ) => setErrors ( [ 'file reading was aborted' ] )
60
+ reader . onerror = ( ) => setErrors ( [ 'file reading has failed' ] )
61
+ reader . onload = ( ) => {
62
+ // Parse the CSV data
63
+ const rawCsv = reader . result as string ;
64
+ const csvData = Papa . parse ( rawCsv . trim ( ) , { header : true } ) ;
65
+ const data = csvData . data as Data ;
66
+ const fields = csvData . meta . fields || [ ] ;
67
+ const normalisedFields = fields . map ( normaliseHeader ) ;
68
+ const errors = csvData . errors . map ( ( e ) => e . message ) . concat ( validateNormalisedFields ( normalisedFields ) ) ;
69
+ state . setData ( csvData . data as Data ) ;
70
+ state . setFields ( fields ) ;
71
+ state . setNormalisedFields ( normalisedFields )
72
+ setErrors ( errors ) ;
73
+ if ( csvData . data . length > 0 && csvData . meta . fields ) {
74
+ setShowData ( true ) ;
75
+ }
76
+ }
77
+ reader . readAsText ( file )
78
+ } )
79
+
80
+ } , [ ] )
81
+ const { getRootProps, getInputProps} = useDropzone ( { onDrop} )
82
+
83
+ const setNormalisedFields = ( fields : Field [ ] ) => {
84
+ state . setNormalisedFields ( fields ) ;
85
+ setErrors ( validateNormalisedFields ( fields ) ) ;
86
+ }
87
+
88
+
89
+ return (
90
+ < Stack spacing = { 2 } >
91
+ < div style = { style . dropAreaContainer } >
92
+ < div { ...getRootProps ( { style : style . dropArea } ) } >
93
+ < input { ...getInputProps ( ) } />
94
+ < p > Drag 'n' drop some files here, or click to select files</ p >
95
+ </ div >
96
+ </ div >
97
+ < Box sx = { { width : '100%' , maxHeight : "20vh" , overflow : 'auto' , whiteSpace : 'nowrap' } } >
98
+ { errors . map ( ( error , index ) => (
99
+ < Alert severity = "error" key = { index } > { error } </ Alert >
100
+ ) ) }
101
+ </ Box >
102
+ < Box component = "div" sx = { { maxHeight : "40vh" , overflow : 'auto' , overflowX : 'auto' } } >
103
+ { showData && < MapHeaders data = { state . data } fields = { state . fields } setNormalisedFields = { setNormalisedFields } normalisedFields = { state . normalisedFields } /> }
104
+ </ Box >
105
+ </ Stack >
106
+ )
107
+ }
108
+
109
+ export default LoadData ;
0 commit comments