--- a +++ b/frontend/src/home.js @@ -0,0 +1,298 @@ +import React, { useState, useEffect, useCallback } from "react"; +import { makeStyles, withStyles } from "@material-ui/core/styles"; +import AppBar from "@material-ui/core/AppBar"; +import Toolbar from "@material-ui/core/Toolbar"; +import Typography from "@material-ui/core/Typography"; +import Avatar from "@material-ui/core/Avatar"; +import Container from "@material-ui/core/Container"; +import cblogo from "./cblogo.PNG"; +import Image from "./bg.png"; +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import { Paper, CardActionArea, CardMedia, Grid, TableContainer, Table, TableBody, TableHead, TableRow, TableCell, Button, CircularProgress } from "@material-ui/core"; +import Clear from '@material-ui/icons/Clear'; +import { DropzoneArea } from 'material-ui-dropzone'; + +const ColorButton = withStyles((theme) => ({ + root: { + color: theme.palette.getContrastText(theme.palette.common.white), + backgroundColor: theme.palette.common.white, + '&:hover': { + backgroundColor: '#ffffff7a', + }, + }, +}))(Button); + +const axios = require("axios").default; + +const useStyles = makeStyles((theme) => ({ + grow: { + flexGrow: 1, + }, + clearButton: { + width: "-webkit-fill-available", + borderRadius: "15px", + padding: "15px 22px", + color: "#000000a6", + fontSize: "20px", + fontWeight: 900, + }, + root: { + maxWidth: 345, + flexGrow: 1, + }, + media: { + height: 400, + }, + paper: { + padding: theme.spacing(2), + margin: 'auto', + maxWidth: 500, + }, + gridContainer: { + justifycontent: "center", + padding: "4em 1em 0 1em", + }, + mainContainer: { + backgroundImage: `url(${Image})`, + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + backgroundSize: 'cover', + height: "93vh", + marginTop: "8px", + }, + ImageCard: { + margin: "auto", + maxWidth: 400, + height: 500, + backgroundColor: 'transparent', + boxShadow: '0px 9px 70px 0px rgb(0 0 0 / 30%) !important', + borderRadius: '15px', + }, + ImageCardEmpty: { + height: 'auto', + }, + noImage: { + margin: "auto", + width: 400, + height: "400 !important", + }, + input: { + display: 'none', + }, + uploadIcon: { + background: 'white', + }, + tableContainer: { + backgroundColor: 'transparent !important', + boxShadow: 'none !important', + }, + table: { + backgroundColor: 'transparent !important', + }, + tableHead: { + backgroundColor: 'transparent !important', + }, + tableRow: { + backgroundColor: 'transparent !important', + }, + tableCell: { + fontSize: '22px', + backgroundColor: 'transparent !important', + borderColor: 'transparent !important', + color: '#000000a6 !important', + fontWeight: 'bolder', + padding: '1px 24px 1px 16px', + }, + tableCell1: { + fontSize: '14px', + backgroundColor: 'transparent !important', + borderColor: 'transparent !important', + color: '#000000a6 !important', + fontWeight: 'bolder', + padding: '1px 24px 1px 16px', + }, + tableBody: { + backgroundColor: 'transparent !important', + }, + text: { + color: 'white !important', + textAlign: 'center', + }, + buttonGrid: { + maxWidth: "416px", + width: "100%", + }, + detail: { + backgroundColor: 'white', + display: 'flex', + justifycontent: 'center', + flexDirection: 'column', + alignItems: 'center', + }, + appbar: { + background: 'purple', + boxShadow: 'none', + color: 'white' + }, + loader: { + color: '#be6a77 !important', + } +})); + +export const ImageUpload = () => { + const classes = useStyles(); + const [selectedFile, setSelectedFile] = useState(); + const [preview, setPreview] = useState(); + const [data, setData] = useState(); + const [Image, setImage] = useState(false); + const [isLoading, setIsloading] = useState(false); + + let confidence = 0; + + const sendFile = useCallback(async () => { + if (Image) { + try { + let formData = new FormData(); + formData.append("file", selectedFile); + console.log("Before API request"); + let res = await axios({ + method: "post", + url: process.env.REACT_APP_API_URL, + data: formData, + }); + console.log("After API request"); + + if (res.status === 200) { + setData(res.data); + } else { + // Handle other status codes here + console.error(`API request failed with status code: ${res.status}`); + } + } catch (error) { + // Handle request error (e.g., network error, server not running) + console.error("API request failed:", error); + } finally { + setIsloading(false); + } + } + }, [Image, selectedFile, setData, setIsloading]); + + const clearData = () => { + setData(null); + setImage(false); + setSelectedFile(null); + setPreview(null); + }; + + useEffect(() => { + if (!selectedFile) { + setPreview(undefined); + return; + } + const objectUrl = URL.createObjectURL(selectedFile); + setPreview(objectUrl); + }, [selectedFile]); + + useEffect(() => { + if (!preview) { + return; + } + setIsloading(true); + sendFile(); + }, [preview, sendFile]); + + const onSelectFile = (files) => { + if (!files || files.length === 0) { + setSelectedFile(undefined); + setImage(false); + setData(undefined); + return; + } + setSelectedFile(files[0]); + setData(undefined); + setImage(true); + }; + + if (data) { + confidence = (parseFloat(data.confidence) * 100).toFixed(2); + } + + return ( + <React.Fragment> + <AppBar position="static" className={classes.appbar}> + <Toolbar> + <Typography className={classes.title} variant="h6" noWrap> + CSE499: Lukemia Blood cell Cancer Classification + </Typography> + <div className={classes.grow} /> + <Avatar src={cblogo}></Avatar> + </Toolbar> + </AppBar> + <Container maxWidth={false} className={classes.mainContainer} disableGutters={true}> + <Grid + className={classes.gridContainer} + container + direction="row" + justifycontent="center" + alignItems="center" + spacing={2} + > + <Grid item xs={12}> + <Card className={`${classes.ImageCard} ${!Image ? classes.ImageCardEmpty : ''}`}> + {Image && <CardActionArea> + <CardMedia + className={classes.media} + component="img" // Change to "img" + src={preview} // Change to "src" + title="Contemplative Reptile" + /> + </CardActionArea> + } + {!Image && <CardContent className={classes.content}> + <DropzoneArea + acceptedFiles={['image/*']} + dropzoneText={"Drag-drop or Upload an image of Blood Cell to proceed"} + onChange={onSelectFile} + /> + </CardContent>} + {data && <CardContent className={classes.detail}> + <TableContainer component={Paper} className={classes.tableContainer}> + <Table className={classes.table} size="small" aria-label="simple table"> + <TableHead className={classes.tableHead}> + <TableRow className={classes.tableRow}> + <TableCell className={classes.tableCell1}>Label:</TableCell> + <TableCell align="right" className={classes.tableCell1}>Confidence:</TableCell> + </TableRow> + </TableHead> + <TableBody className={classes.tableBody}> + <TableRow className={classes.tableRow}> + <TableCell component="th" scope="row" className={classes.tableCell}> + {data.class} + </TableCell> + <TableCell align="right" className={classes.tableCell}>{confidence}%</TableCell> + </TableRow> + </TableBody> + </Table> + </TableContainer> + </CardContent>} + {isLoading && <CardContent className={classes.detail}> + <CircularProgress color="secondary" className={classes.loader} /> + <Typography className={classes.title} variant="h6" noWrap> + Matching... Please Wait... + </Typography> + </CardContent>} + </Card> + </Grid> + {data && + <Grid item className={classes.buttonGrid} > + + <ColorButton variant="contained" className={classes.clearButton} color="primary" component="span" size="large" onClick={clearData} startIcon={<Clear fontSize="large" />}> + Clear + </ColorButton> + </Grid>} + </Grid > + </Container > + </React.Fragment > + ); +};