diff --git a/package.json b/package.json index e28783e..3a589c6 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@types/react-virtualized": "^9.21.2", "moment": "^2.22.2", "react": "^16.6.1", "react-dom": "^16.6.1", @@ -10,6 +11,7 @@ "react-moment": "^0.8.3", "react-router-dom": "^4.3.1", "react-scripts-ts": "3.1.0", + "react-virtualized": "^9.21.1", "semantic-ui-css": "^2.4.1", "semantic-ui-react": "^0.83.0", "uuid": "^3.3.2" diff --git a/src/App.css b/src/App.css index c6fec6b..6885677 100644 --- a/src/App.css +++ b/src/App.css @@ -1,7 +1,15 @@ -/*.App { - margin: 20px; -}*/ - +.headerRow, .evenRow, .oddRow { + border-bottom: 1px solid gray; +} +.evenRow{ + background-color:aliceblue; +} +.oddRow { + background-color: beige; +} +.ReactVirtualized__Table__rowColumn { + white-space: pre !important; +} .ui.container.container-position{ margin-left:15rem !important; margin-top:20px; diff --git a/src/components/DropUpload.tsx b/src/components/DropUpload.tsx index e9ba40f..2cfb054 100644 --- a/src/components/DropUpload.tsx +++ b/src/components/DropUpload.tsx @@ -1,6 +1,11 @@ import * as React from 'react'; +import '../App.css'; -import { Header, Icon, Placeholder, PlaceholderLine, Progress, Segment, Table } from 'semantic-ui-react'; +import { Header, Icon, Progress, Segment, } from 'semantic-ui-react'; + +import { AutoSizer, Column, Table as VTable } from 'react-virtualized'; + +import 'react-virtualized/styles.css'; import { withRouter } from 'react-router'; @@ -14,7 +19,6 @@ import { v4 } from 'uuid'; import Time from './Time'; -// QQQQ API doesn't specify this yet export interface IAcceptedFileTypes { audio: string[]; transcription: string[]; @@ -64,74 +68,12 @@ export interface IDropUploadState { acceptedFileTypes: IAcceptedFileTypes; } -interface IUnmatchedFileRowProps { - file: IUploadedFile -} - -class UnmatchedFileRow extends React.PureComponent { - render() { - const { file } = this.props - return ( - - - {file.fileType} - {file.state} - {file.name} - {file.fileT ? file.fileT.id : (file.state === RequestState.STARTED && )} - {file.fileT ? file.fileT.fileInfo!.id : (file.state === RequestState.STARTED && )} - {file.fileT ? - - ) - } -} - -interface IMatchedFileRowsProps { - match: Readonly -} - -// tslint:disable-next-line:max-classes-per-file -class MatchedFileRows extends React.PureComponent { - render () { - const { match } = this.props - return ( - - - - {match.state === RequestState.FAILED ? - - : - match.state === RequestState.COMPLETE ? - - : - - } - - {match.audio.fileType} - {match.audio.state} - {match.audio.name} - {match.audio.fileT ? match.audio.fileT.id : (match.audio.state === RequestState.STARTED && )} - {match.audio.fileT ? match.audio.fileT.fileInfo!.id : (match.audio.state === RequestState.STARTED && )} - {match.audio.fileT ? - - - {match.transcription.fileType} - {match.transcription.state} - {match.transcription.name} - {match.transcription.fileT ? match.transcription.fileT.id : (match.transcription.state === RequestState.STARTED && )} - {match.transcription.fileT ? match.transcription.fileT.fileInfo!.id : (match.transcription.state === RequestState.STARTED && )} - {match.transcription.fileT ? - - - ) - } -} - // tslint:disable-next-line:max-classes-per-file class DropUpload extends React.Component { constructor(props: any) { super(props); this.state = { - acceptedFileTypes: {audio: [], transcription: []} as IAcceptedFileTypes, + acceptedFileTypes: { audio: [], transcription: [] } as IAcceptedFileTypes, dragActive: false, formLoading: false, isLoading: true, @@ -145,10 +87,11 @@ class DropUpload extends React.Component { this.onDragLeave = this.onDragLeave.bind(this); this.match = this.match.bind(this); this.updateFile = this.updateFile.bind(this); + this.noRowsRenderer = this.noRowsRenderer.bind(this); } public getData() { - this.setState({isLoading: true}); + this.setState({ isLoading: true }); api.persephoneApiApiEndpointsBackendAcceptedFiletypes().then(res => res.json()).then(json => { this.setState({ acceptedFileTypes: json as IAcceptedFileTypes, @@ -162,7 +105,7 @@ class DropUpload extends React.Component { } public onDragEnter() { - this.setState({dragActive: true}) + this.setState({ dragActive: true }) } public match() { @@ -194,15 +137,15 @@ class DropUpload extends React.Component { utteranceInfo } api.utterancePost(requestData).then(res => { - this.setState({matches: this.state.matches.map(m => m.id === id ? {...m, state: RequestState.COMPLETE} : m)}) + this.setState({ matches: this.state.matches.map(m => m.id === id ? { ...m, state: RequestState.COMPLETE } : m) }) }).catch(err => { - this.setState({matches: this.state.matches.map(m => m.id === id ? {...m, state: RequestState.FAILED} : m)}) + this.setState({ matches: this.state.matches.map(m => m.id === id ? { ...m, state: RequestState.FAILED } : m) }) }); } } this.setState({ matches: this.state.matches.concat(newMatches), - uploadedFiles: this.state.uploadedFiles.map(f => ({...f, matched: f.matched || matchedIds.indexOf(f.id) > -1})) + uploadedFiles: this.state.uploadedFiles.map(f => ({ ...f, matched: f.matched || matchedIds.indexOf(f.id) > -1 })) }) } @@ -230,7 +173,7 @@ class DropUpload extends React.Component { public onDrop(accepted: File[], rejected: File[], event: React.DragEvent) { this.setState({ - dragActive: false + dragActive: false, }) const uploadedFiles: Array> = []; for (const file of accepted) { @@ -254,11 +197,14 @@ class DropUpload extends React.Component { const requestData: AudioPostRequest = { audioFile: file } - api.audioPost(requestData).then(res => { - this.updateFile(id, {state: RequestState.COMPLETE, fileT: res}) - console.log("successfully uploaded", file) - }).catch(err => { - this.updateFile(id, {state: RequestState.FAILED}) + this.setState({ isLoading: true }, () => { + api.audioPost(requestData).then(res => { + this.updateFile(id, { state: RequestState.COMPLETE, fileT: res }) + this.setState({ isLoading: false }) + }).catch(err => { + this.updateFile(id, { state: RequestState.FAILED }) + this.setState({ isLoading: false }) + }); }); } else if (this.state.acceptedFileTypes.transcription.indexOf(extension) > -1) { uploadedFiles.push({ @@ -274,11 +220,14 @@ class DropUpload extends React.Component { const requestData: PersephoneApiApiEndpointsTranscriptionFromFileRequest = { transcriptionFile: file } - api.persephoneApiApiEndpointsTranscriptionFromFile(requestData).then(res => { - this.updateFile(id, {state: RequestState.COMPLETE, fileT: res}) - console.log("successfully uploaded", file) - }).catch(err => { - this.updateFile(id, {state: RequestState.FAILED}) + this.setState({ isLoading: true }, () => { + api.persephoneApiApiEndpointsTranscriptionFromFile(requestData).then(res => { + this.updateFile(id, { state: RequestState.COMPLETE, fileT: res }) + this.setState({ isLoading: false }) + }).catch(err => { + this.updateFile(id, { state: RequestState.FAILED }) + this.setState({ isLoading: false }) + }); }); } else { console.log("unknown filetype, not uploaded") @@ -297,18 +246,28 @@ class DropUpload extends React.Component { }) } } - this.setState({uploadedFiles: this.state.uploadedFiles.concat(uploadedFiles)}) + this.setState({ + uploadedFiles: this.state.uploadedFiles.concat(uploadedFiles) + }) } public onDragLeave() { - this.setState({dragActive: false}) + this.setState({ dragActive: false }) + } + + noRowsRenderer() { + return ( +
+ No uploaded files +
+ ) } public render() { return (
Drag 'n' drop upload
- +
@@ -319,46 +278,153 @@ class DropUpload extends React.Component {
Uploaded files
- - - - Match - Type - Upload state - File name - ID - File ID - File created at - - - - - {this.state.uploadedFiles.length > 0 ? - - {[...this.state.matches] - .sort((one, two) => (one.name === two.name) ? 0 : (one.name < two.name ? -1 : 1)) - .map(m => ( - - ))} - {this.state.uploadedFiles - .filter(f => !f.matched) - .sort((one, two) => { - const stateRank = [RequestState.COMPLETE, RequestState.STARTED, RequestState.NOT_STARTED, RequestState.FAILED]; - return (one.state !== two.state) ? stateRank.indexOf(one.state) - stateRank.indexOf(two.state) : (one.name < two.name ? -1 : 1);}) - .map(file => ( - - ))} - - : - - This table is empty - - } - -
+ {this.state.uploadedFiles.length > 0 && +

{this.state.uploadedFiles.filter(f => f.fileType === 'Audio').length} audios found, {this.state.uploadedFiles.filter(f => f.fileType === 'Transcription').length} transcriptions found, {this.state.uploadedFiles.filter(f => f.matched === true).length / 2} matches found, {this.state.uploadedFiles.filter(f => f.fileType === 'Unknown').length} unknown files have not been uploaded

} +
Matched files
+ + {({ width }) => ( + { + if (index < 0) { + return 'headerRow'; + } else { + return index % 2 === 0 ? 'evenRow' : 'oddRow'; + } + }} + rowCount={this.state.matches.length} + rowGetter={({ index }) => this.state.matches[index]} + rowHeight={50} + width={width} > + ( + cellData === RequestState.FAILED ? + + : cellData === RequestState.COMPLETE ? + + : + )} /> + ( + rowData.audio.fileType + '\n' + rowData.transcription.fileType + )} /> + + ( + rowData.audio.name + '\n' + rowData.transcription.name + )} /> + ( + rowData.audio.fileT || rowData.transcription.fileT ? rowData.audio.fileT.id + '\n' + rowData.transcription.fileT.id : 'finding id' + )} /> + ( + rowData.audio.fileT || rowData.transcription.fileT ? rowData.audio.fileT.fileInfo.id + '\n' + rowData.transcription.fileT.fileInfo.id : 'finding fileid' + )} /> + ( + rowData.audio.fileT || rowData.transcription.fileT ? <> + + )} + +
All files
+ + {({ width }) => ( + { + if (index < 0) { + return 'headerRow'; + } else { + return index % 2 === 0 ? 'evenRow' : 'oddRow'; + } + }} + rowCount={this.state.uploadedFiles.length} + rowGetter={({ index }) => this.state.uploadedFiles[index]} + rowHeight={30} + width={width} > + ( + cellData ? + : + )} /> + + { + const highlightRed = cellData.indexOf(RequestState.FAILED) === 0 + return highlightRed ? {cellData} + : cellData + }} /> + + ( + rowData.fileT ? rowData.fileT.id : 'finding id' + )} /> + ( + rowData.fileT ? rowData.fileT.fileInfo.id : 'finding fileid' + )} /> + ( + rowData.fileT ? + + )} +
) } } - export default withRouter(DropUpload); \ No newline at end of file