From 8ca347ca2112ed3452cd6ee58b6ee7821b78198c Mon Sep 17 00:00:00 2001 From: mshriver Date: Wed, 22 Jan 2025 08:21:02 -0500 Subject: [PATCH] Convert FileUpload to functional --- frontend/src/components/fileupload.js | 153 +++++++++++------------ frontend/src/components/ibutsu-header.js | 9 +- frontend/src/components/index.js | 1 - frontend/src/components/toast-wrapper.js | 2 +- 4 files changed, 77 insertions(+), 88 deletions(-) diff --git a/frontend/src/components/fileupload.js b/frontend/src/components/fileupload.js index 0335d81f..f6ebd2ed 100644 --- a/frontend/src/components/fileupload.js +++ b/frontend/src/components/fileupload.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon'; @@ -12,93 +12,82 @@ import ToastWrapper from './toast-wrapper'; import { Settings } from '../settings'; import { ALERT_TIMEOUT } from '../constants'; -export class FileUpload extends React.Component { - // TODO: refactor to functional - // TODO: Consider explicit project selection for upload instead of inferred from context - // TODO: support multiple upload - static contextType = IbutsuContext; - static propTypes = { - url: PropTypes.string.isRequired, - name: PropTypes.string, - children: PropTypes.node, - } - constructor(props) { - super(props); - this.state = { - url: props.url, - name: props.name ? props.name : 'file', - importId: null, - intervalId: null - }; - this.inputRef = React.createRef(); - this.importToastRef = React.createRef(); - this.intervalId = React.createRef(); +const FileUpload = (props) => { + const context = useContext(IbutsuContext); + + const [importId, setImportId] = useState(); + + const inputRef = useRef(); + const importToastRef = useRef(); + const intervalId = useRef(); + + const name = props?.name ? props.name : 'file'; - } - onClick = () => { - this.inputRef.current.click(); + function onClick() { + inputRef.current.click(); } - onFileChange = (e) => { + function onFileChange(e) { let files = e.target.files || e.dataTransfer.files; if (files.length > 0) { - this.uploadFile(files[0]); + uploadFile(files[0]); // Clear the upload field - this.inputRef.current.value = ''; + inputRef.current.value = ''; } } - checkImportStatus = (importId) => { - const { primaryObject } = this.context; - - HttpClient.get([Settings.serverUrl, 'import', importId]) - .then(response => HttpClient.handleResponse(response)) - .then(data => { - if (data['status'] === 'done') { - clearInterval(this.intervalId.current); - this.importId = null; - let action = null; - if (data.metadata.run_id) { - const RunButton = () => ( - - Go to Run - - ) - action = ; - } - toast.update(this.importToastRef.current, - { - data: { - type:'success', - title:'Import Complete', - message: `${data.filename} has been successfully imported as run ${data.metadata.run_id}`, - action: action - }, - type: 'success', - autoClose: ALERT_TIMEOUT + function checkImportStatus() { + const { primaryObject } = context; + if(importId) { + HttpClient.get([Settings.serverUrl, 'import', importId]) + .then(response => HttpClient.handleResponse(response)) + .then(data => { + if (data['status'] === 'done') { + clearInterval(intervalId.current); + setImportId(); + let action = null; + if (data.metadata.run_id) { + const RunButton = () => ( + + Go to Run + + ) + action = ; } - ) + toast.update(importToastRef.current, + { + data: { + type:'success', + title:'Import Complete', + message: `${data.filename} has been successfully imported as run ${data.metadata.run_id}`, + action: action + }, + type: 'success', + autoClose: ALERT_TIMEOUT + } + ) - } - }); + } + }); + } } - uploadFile = (file) => { + function uploadFile(file) { const files = {}; - const { primaryObject } = this.context; - files[this.state.name] = file; + const { primaryObject } = context; + files[name] = file; HttpClient.upload( - this.state.url, + Settings.serverUrl+'/import', files, {'project': primaryObject?.id} ) .then((response) => HttpClient.handleResponse(response, 'response')) .then(data => { data.json().then((importObject) => { - this.importToastRef.current = toast(, + importToastRef.current = toast(, { data: { type: 'info', @@ -109,11 +98,12 @@ export class FileUpload extends React.Component { theme: getDarkTheme() ? 'dark' : 'light' } ); - this.intervalId.current = setInterval(() => {this.checkImportStatus(importObject['id'])}, 5000); + setImportId(importObject['id']); + intervalId.current = setInterval(checkImportStatus, 5000); }) }) .catch(error => { - toast.update(this.importToastRef.current, + toast.update(importToastRef.current, { data: { type: 'danger', @@ -126,18 +116,21 @@ export class FileUpload extends React.Component { }); } - render() { - const { children } = this.props; - const { primaryObject } = this.context; - return ( - - - - - - - ); - } + const {primaryObject} = context; + return ( + + + + + + + ); + } + +FileUpload.propTypes = { + name: PropTypes.string, +}; + +export default FileUpload; diff --git a/frontend/src/components/ibutsu-header.js b/frontend/src/components/ibutsu-header.js index e17829d5..85522af9 100644 --- a/frontend/src/components/ibutsu-header.js +++ b/frontend/src/components/ibutsu-header.js @@ -39,7 +39,8 @@ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon'; import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon'; -import { FileUpload, UserDropdown } from '../components'; +import { UserDropdown } from '../components'; +import FileUpload from './fileupload'; import { VERSION } from '../constants'; import { HttpClient } from '../services/http'; import { Settings } from '../settings'; @@ -247,11 +248,7 @@ function IbutsuHeader (props) { icon={} /> - Import +