I am using URL.createObjectURL to create image and pass it to an other component. In my NotificationStepper URL.createObjectURL is working just fine but in another Createwebsite component is i am getting this error TypeError: Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided..
following is my NotificationStepper component.
const styles = {
transparentBar: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
},
dialogPaper: {
minHeight: '400px',
maxHeight: '400px',
},
};
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const useStyles = makeStyles((theme) =>
createStyles({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}),
);
function getSteps() {
return ['Create', 'Audience', 'Timing'];
}
function getStepContent(step, $this) {
return (
<div className="row">
<CardBox styleName="col-lg-12"
heading="">
<form className="row" noValidate autoComplete="off" style={{ "flex-wrap": "no-wrap", "flex-direction": "column" }}>
<div className="col-md-12 col-12" style={{ "margin-bottom": "15px", "display": "flex", "align-items": "center" }}>
<Tooltip title="Use featured image of the landing page here. Works on Chrome, Opera, Edge. Recommended aspect ratio 2:1. Jpg, Jpeg, PNG file types only.">
<IconButton aria-label="">
<HelpOutlineIcon />
</IconButton>
</Tooltip>
<span style={{ "width": "20%", "margin-right": "20px" }}>Banner: </span>
<div className="inputfile">
Upload
<input className='hide_file' type="file" onChange={$this.onBannerChange} />
</div>
{$this.state.banner ?
<span>{$this.state.icon.banner}</span>
: null
}
</div>
</form>
</div >
);
}
class NotificationStepper extends React.Component {
state = {
banner: '',
crop: { x: 0, y: 0 },
zoom: 1,
aspect: 1,
croppedAreaPixels: null,
croppedImage: null,
showDialog: false,
};
onBannerChange = event => {
this.setState({ banner: event.target.files[0] });
this.setState({showDialog: true})
};
onIconChange = event => {
this.setState({ icon: event.target.files[0] });
this.setState({showDialogIcon: true})
};
onCropChange = crop => {
this.setState({ crop })
};
onCropComplete = (croppedArea, croppedAreaPixels) => {
this.setState({croppedAreaPixels: croppedAreaPixels});
};
CropImageBanner = async ()=> {
const croppedImage = await getCroppedImg(
URL.createObjectURL(this.state.banner),
this.state.croppedAreaPixels,
0
);
this.setState({ banner: croppedImage });
this.closeDialog();
};
closeDialog = () => {
this.setState({showDialog: false});
}
onZoomChange = zoom => {
this.setState({ zoom })
};
render() {
const steps = getSteps();
const { activeStep } = this.state;
if (this.props.showSuccessMessage) {
console.log('dispatch');
setTimeout(() => {
this.props.hideMessage();
}, 100);
}
if (this.props.showMessage) {
console.log('dispatch');
setTimeout(() => {
this.props.hideCompaignAlert();
}, 100);
}
const { classes } = this.props;
return (
<div>
{this.state.banner ?
// <span>{$this.state.banner.name}</span>
<Dialog
classes={{paper: classes.dialogPaper }}
fullWidth={true}
maxWidth='sm'
open={this.state.showDialog}
onClose={this.closeDialog}
>
<DialogTitle>
</DialogTitle>
<DialogContent>
<div className="App-crop" >
<div className="crop-container-div">
<Cropper
image={URL.createObjectURL(this.state.banner)}
crop={this.state.crop}
zoom={this.state.zoom}
aspect={this.state.aspect}
onCropChange={this.onCropChange}
onCropComplete={this.onCropComplete}
onZoomChange={this.onZoomChange}
/>
</div>
<div className="controls">
<Slider
value={this.state.zoom}
min={1}
max={3}
step={0.1}
aria-labelledby="Zoom"
onChange={(e, zoom) => this.onZoomChange(zoom)}
/>
</div>
</div>
</DialogContent>
<DialogActions>
<Button onClick={() => this.closeDialog()} color="secondary">
Cancel
</Button>
<Button onClick={() =>
this.CropImageBanner()
} color="primary">
Crop
</Button>
</DialogActions>
</Dialog>
:
null
}
<Stepper className="MuiPaper-root-custom" activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => {
return (
<Step key={label}>
<StepLabel>{label}</StepLabel>
<StepContent className="pb-3">
<Typography>{getStepContent(index, this)}</Typography>
<div className="mt-2">
<div>
<Button
disabled={activeStep === 0}
onClick={this.handleBack}
className="jr-btn"
>
Back
</Button>
<Button
variant="contained"
color="primary"
onClick={this.handleNext}
className="jr-btn"
>
{activeStep === steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
</StepContent>
</Step>
);
})}
</Stepper>
{activeStep === steps.length && (
// <Paper square elevation={0} className="p-2">
/*<Typography>All steps completed - you"re finished</Typography>*/
<div>
<Button onClick={this.sendNotification} disabled={!this.isFormValid} color="primary" style={{ "align-self": "flex-end", "border": "1px solid", "margin-left": "10px", "margin-bottom": "40px" }} size="small" className="col-md-2 col-2">Create</Button>
<Button onClick={this.handleReset} color="primary" style={{ "align-self": "flex-end", "border": "1px solid", "margin-left": "10px", "margin-bottom": "40px" }} size="small" className="col-md-2 col-2" color="primary">Reset</Button>
)}
{this.props.showMessage && NotificationManager.error(this.props.alertMessage) ?
<NotificationContainer /> : null
}
{this.props.showSuccessMessage && NotificationManager.success(this.props.successMessage)}
<NotificationContainer />
</div>
);
}
}
const mapDispatchToProps = dispatch => ({
showCompaignAlert: (message) => dispatch(showCompaignAlert(message)),
CreateCampaign: (data) => dispatch(CreateCampaign(data)),
hideMessage: () => dispatch(hideMessage()),
hideCompaignAlert: () => dispatch(hideCompaignAlert()),
showCampaignLoader: () => dispatch(showCampaignLoader())
});
const mapStateToProps = state => ({
loader: state.Campaigns.loader,
allWebsites: state.Websites.allWebsites,
alertMessage: state.Campaigns.alertMessage,
showMessage: state.Campaigns.showMessage,
successMessage: state.Campaigns.successMessage,
showSuccessMessage: state.Campaigns.showSuccessMessage,
});
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles) (NotificationStepper)));
and following is my CreateWebsite
import React from "react";
import Button from "#material-ui/core/Button";
import Dialog from "#material-ui/core/Dialog";
import Slide from "#material-ui/core/Slide";
import DialogTitle from "#material-ui/core/DialogTitle";
import DialogContent from "#material-ui/core/DialogContent";
import DialogActions from "#material-ui/core/DialogActions";
import FormControl from "#material-ui/core/FormControl";
import TextField from "#material-ui/core/TextField";
import {connect} from "react-redux";
import {createWebsite, hideSuccessMessage, hideAlertCreateWebsite} from "../../../actions/Websites";
import {NotificationContainer, NotificationManager} from "react-notifications";
import InputAdornment from "#material-ui/core/InputAdornment";
import IconButton from "#material-ui/core/IconButton";
import getCroppedImg from "../Send Notification/cropImage";
import Cropper from "react-easy-crop";
import Slider from "#material-ui/core/Slider";
import {withStyles} from "#material-ui/core/styles";
const styles = {
transparentBar: {
backgroundColor: 'transparent !important',
boxShadow: 'none',
paddingTop: '25px',
color: '#FFFFFF'
},
dialogPaper: {
minHeight: '400px',
maxHeight: '400px',
},
};
class CreateWebsiteComponent extends React.Component {
state = {
title: '',
url: '',
logo: '',
showDialog: false,
crop: { x: 0, y: 0 },
zoom: 1,
aspect: 1,
croppedAreaPixels: null,
croppedImage: null,
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
onFileChange = event => {
this.setState({ logo: event.target.files[0] });
this.setState({ showDialog: true})
};
createWebsite = (title, url, logo) => {
let temp = 'https://' + url;
this.props.createWebsite({title, url , logo})
};
onCropChange = crop => {
this.setState({ crop })
};
onCropComplete = (croppedArea, croppedAreaPixels) => {
this.setState({croppedAreaPixels: croppedAreaPixels});
};
closeDialog = () => {
this.setState({showDialog: false});
};
onZoomChange = zoom => {
this.setState({ zoom })
};
CropImageBanner = async ()=> {
const croppedImage = await getCroppedImg(
URL.createObjectURL(this.state.logo),
this.state.croppedAreaPixels,
0
);
this.setState({ logo: croppedImage });
this.closeDialog();
};
render() {
if (this.props.showSuccessMessage) {
setTimeout(() => {
this.props.hideSuccessMessage();
}, 100);
}
if (this.props.showAlertCreateWebsite_) {
setTimeout(() => {
this.props.hideAlertCreateWebsite();
}, 100);
}
const { title, url, logo} = this.state;
const openDialog = this.props.openDialog;
const { classes } = this.props;
return (
<div>
<Dialog
classes={{paper: classes.dialogPaper }}
fullWidth={true}
maxWidth='md'
open={this.state.showDialog}
onClose={this.closeDialog}
>
<DialogTitle>
</DialogTitle>
<DialogContent>
<div className="App-crop" >
<div className="crop-container-div">
<Cropper
image={this.state.log}
crop={this.state.crop}
zoom={this.state.zoom}
aspect={this.state.aspect}
onCropChange={this.onCropChange}
onCropComplete={this.onCropComplete}
onZoomChange={this.onZoomChange}
/>
</div>
<div className="controls">
<Slider
value={this.state.zoom}
min={1}
max={3}
step={0.1}
aria-labelledby="Zoom"
onChange={(e, zoom) => this.onZoomChange(zoom)}
/>
</div>
</div>
</DialogContent>
<DialogActions>
<Button onClick={() => this.closeDialog()} color="secondary">
Cancel
</Button>
<Button onClick={() =>
this.CropImageBanner()
} color="primary">
Crop
</Button>
</DialogActions>
</Dialog>
<Dialog
open={openDialog}
onClose={this.props.closeDialog}
>
<DialogTitle>
{"Create Website"}
</DialogTitle>
<DialogContent>
<FormControl className="w-100">
<TextField
style={{'margin-right': "10px"}}
className="col-md-11 col-11"
id="name"
label="Title"
value={this.state.title}
onChange={this.handleChange('title')}
margin="normal"
fullWidth
/>
<TextField
style={{'margin-right': "10px"}}
className="col-md-11 col-11"
id="name"
label="URL"
value={this.state.url}
onChange={this.handleChange('url')}
margin="normal"
fullWidth
InputProps={{
startAdornment: (
<InputAdornment className='positionEnd' position="start">
https://
</InputAdornment>
),
}}
/>
<div style={{"margin-top": "20px"}} className='col-md-11 col-sm-12 col-12 flex-class-custom'>
<span style={{"margin-right": "20px"}}>Icon: </span>
<div className="inputfile">
Upload
<input className='hide_file' style={{"width": "80%"}} type="file" onChange={this.onFileChange} />
</div>
{this.state.logo ?
<span>{this.state.logo.name}</span>
: null
}
</div>
</FormControl>
</DialogContent>
<DialogActions>
<Button onClick={this.props.closeDialog} color="secondary">
Cancel
</Button>
<Button onClick={() => this.createWebsite(title, url , logo)
} color="primary">
Create
</Button>
</DialogActions>
</Dialog>
{this.props.showAlertCreateWebsite_ && NotificationManager.error(this.props.alertMessage)}
<NotificationContainer/>
{this.props.showSuccessMessage && NotificationManager.success(this.props.successMessage)}
<NotificationContainer/>
</div>
)
}
}
const mapDispatchToProps = dispatch => ({
createWebsite: (payload) => dispatch(createWebsite(payload)),
hideSuccessMessage: () => dispatch(hideSuccessMessage()),
hideAlertCreateWebsite: () => dispatch(hideAlertCreateWebsite())
});
const mapStateToProps = state => ({
loader : state.Websites.loader ,
alertMessage : state.Websites.alertMessage ,
showAlertCreateWebsite_ : state.Websites.showAlertCreateWebsite_,
showSuccessMessage : state.Websites.showSuccessMessage,
successMessage : state.Websites.successMessage
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(CreateWebsiteComponent));
// export default CreateWebsiteComponent;
Related
I am facing the difficulty that I want to rewrite the component to use react hooks, yet suddenly the DropZone component stopped working. I need to change the Component to hooks due to the JWT authentication I use.
I have this (former) implementation of the component:
import React from "react";
import Dropzone from "react-dropzone";
import UploadFileService from "../../../services/upload-files.service";
import './FileUploadForm.css'
import {withTranslation} from "react-i18next";
import Button from "#mui/material/Button";
import LinearProgressWithLabel from "./LinearProgressWithLabel";
import Typography from "#mui/material/Typography";
import {Alert} from "#mui/material";
import AuthContext from "../../../context/AuthContext";
class FileUploadForm extends React.Component {
constructor(props) {
super(props);
this.upload = this.upload.bind(this);
this.removeFiles = this.removeFiles.bind(this);
this.onDrop = this.onDrop.bind(this);
this.uploadFiles = this.uploadFiles.bind(this);
this.state = {
selectedFiles: undefined,
progressInfos: [],
message: [],
fileInfos: [],
dropZoneVisible: true,
url: props.url,
multipleFiles: props.multipleFiles
};
}
onDrop(files) {
if (files.length > 0) {
this.setState({selectedFiles: files});
}
}
uploadFiles() {
const selectedFiles = this.state.selectedFiles;
let _progressInfos = [];
for (let i = 0; i < selectedFiles.length; i++) {
_progressInfos.push({percentage: 0, fileName: selectedFiles[i].name});
}
this.setState(
{
progressInfos: _progressInfos,
message: [],
},
() => {
for (let i = 0; i < selectedFiles.length; i++) {
this.upload(i, selectedFiles[i]);
}
}
);
}
upload(idx, file) {
let _progressInfos = [...this.state.progressInfos];
UploadFileService.upload(file, this.state.url, (event) => {
_progressInfos[idx].percentage = Math.round(
(100 * event.loaded) / event.total
);
this.setState({
_progressInfos,
});
})
.then((response) => {
this.setState((prev) => {
let nextMessage = [
...prev.message,
"Uploaded the file successfully: " + file.name,
];
return {
message: nextMessage,
dropZoneVisible: false
};
});
return UploadFileService.getFiles(this.state.url);
})
.then((files) => {
this.setState({
fileInfos: files.data,
});
})
.catch(() => {
_progressInfos[idx].percentage = 0;
this.setState((prev) => {
let nextMessage = [
...prev.message,
"Could not upload the file: " + file.name,
];
return {
progressInfos: _progressInfos,
message: nextMessage,
};
});
});
}
render() {
const {t} = this.props;
const {selectedFiles, progressInfos, message, fileInfos} = this.state;
return (
<div style={{marginTop: '1em'}}>
{progressInfos &&
progressInfos.map((progressInfo, index) => (
<div className="mb-2" key={index} style={{
textAlign: "center",
fontWeight: "bold"
}}>
<Typography component="span" variant="string" align="center">
{progressInfo.fileName}
</Typography>
<LinearProgressWithLabel variant="determinate" value={progressInfo.percentage}/>
</div>
))}
<Dropzone onDrop={this.onDrop} multiple={this.state.multipleFiles}>
{({getRootProps, getInputProps}) => (
<section>
<div {...getRootProps({className: "dropzone"})}>
<input {...getInputProps()} />
{selectedFiles &&
Array.isArray(selectedFiles) &&
selectedFiles.length ? (
<div className="selected-file">
{selectedFiles.length > 3
? `${selectedFiles.length} files`
: selectedFiles.map((file) => file.name).join(", ")}
</div>
) : (
t("label", "Drag and drop file here, or click to select file", {ns: "fileUploadForm"})
)}
</div>
<aside className="selected-file-wrapper"
style={{
display: "flex",
justifyContent: "space-evenly"
}}
>
<Button
className="btn btn-success"
disabled={!selectedFiles}
variant="contained"
onClick={this.uploadFiles}
>
{t("button", "Submit", {ns: "fileUploadForm"})}
</Button>
<Button
className="btn"
color="error"
disabled={!selectedFiles}
variant="contained"
onClick={this.removeFiles}
>
{/*{t("button", "Submit", {ns: "fileUploadForm"})}*/}
Beheben
</Button>
</aside>
</section>
)}
</Dropzone>
{message.length > 0 && (
<div style={{
marginTop: "1em"
}}>
{message.map((item, i) => {
return (
<Alert severity={
item.includes("success") ? "success" : "warning"}>
{item}
</Alert>);
})}
</div>
)}
{fileInfos.length > 0 && (
<div className="card">
<div className="card-header">List of Files</div>
<ul className="list-group list-group-flush">
{fileInfos && fileInfos.map((file, index) => (
<li className="list-group-item" key={index}>
<a href={file.url}>{file.name}</a>
</li>
))}
</ul>
</div>
)}
</div>
);
}
}
export default withTranslation()(FileUploadForm);
This component works perfectly, meaning that the selectedFiles get propagated so the users can see what they submit. However, when changed to the functional implementation using hooks:
import React, {useCallback, useEffect, useRef, useState} from "react";
import Dropzone from "react-dropzone";
import './FileUploadForm.css'
import {useTranslation, withTranslation} from "react-i18next";
import Button from "#mui/material/Button";
import LinearProgressWithLabel from "./LinearProgressWithLabel";
import Typography from "#mui/material/Typography";
import {Alert} from "#mui/material";
import useAxios from "../../../utils/useAxios";
function FileUploadForm(url, multipleFiles, handleUploadReload) {
const api = useAxios();
const {t, i18n} = useTranslation()
const {selectedFiles, setSelectedFiles} = useState(undefined);
const {progressInfos, setProgressInfos} = useState({val: []});
const {message, setMessage} = useState([]);
const {targetUrl, setTargetUrl} = useState(url);
const {multipleFilesEnabled, setMultipleFilesEnabled} = useState(multipleFiles);
const {formUploaded, setFormUploaded} = useState(false);
const progressInfosRef = useRef(null)
const onDrop = (files) => {
if (files && files.length > 0) {
setSelectedFiles(files);
setProgressInfos({ val: [] });
}
}
const resetFormState = () => {
setFormUploaded(false);
setSelectedFiles(undefined);
setProgressInfos([]);
setMessage([]);
}
const removeFiles = () => {
setSelectedFiles(undefined);
setProgressInfos([]);
setMessage([]);
}
const uploadFiles = () => {
const files = Array.from(selectedFiles);
let _progressInfos = files.map(file => ({percentage: 0, fileName: file.name}));
progressInfosRef.current = {
val: _progressInfos,
}
files.map((file, i) => upload(i, file));
setMessage([]);
};
const upload = (idx, file) => {
let _progressInfos = [...progressInfosRef.current.val];
let formData = new FormData();
formData.append("file", file)
const onUploadProgress = (event) => {
_progressInfos[idx].percentage = Math.round(
(100 * event.loaded) / event.total
);
setProgressInfos({val: _progressInfos});
}
return api.post(targetUrl, formData, {
headers: {
"Content-Type": "multipart/form-data",
}, onUploadProgress
}).then((response) => {
handleUploadReload(response.data["files"]);
setMessage((prev) => ([
...prev.message,
"Uploaded the file successfully: " + file.name,
]
));
setFormUploaded(true);
})
.catch(() => {
_progressInfos[idx].percentage = 0;
setProgressInfos({val: _progressInfos});
setMessage((prev) => ([
...prev.message,
"Could not upload the file: " + file.name,
]));
});
}
return (
<div style={{marginTop: '1em'}}>
{progressInfos &&
progressInfos.val.length > 0 &&
progressInfos.val.map((progressInfo, index) => (
<div className="mb-2" key={index} style={{
textAlign: "center",
fontWeight: "bold"
}}>
<Typography component="span" variant="string" align="center">
{progressInfo.fileName}
</Typography>
<LinearProgressWithLabel variant="determinate" value={progressInfo.percentage}/>
</div>
))}
<Dropzone onDrop={onDrop} multiple={multipleFilesEnabled}>
{({getRootProps, getInputProps}) => (
<section>
<div {...getRootProps({className: "dropzone"})}>
<input {...getInputProps()} />
{selectedFiles &&
Array.isArray(selectedFiles) &&
selectedFiles.length ? (
<div className="selected-file">
{selectedFiles.length > 3
? `${selectedFiles.length} files`
: selectedFiles.map((file) => file.name).join(", ")}
</div>
) : (
t("label", "Drag and drop file here, or click to select file", {ns: "fileUploadForm"})
)}
</div>
<aside className="selected-file-wrapper"
style={{
display: "flex",
justifyContent: "space-evenly"
}}
>
{!formUploaded ? [
<Button
className="btn btn-success"
disabled={!selectedFiles}
variant="contained"
onClick={uploadFiles}
>
{t("button", "Submit", {ns: "fileUploadForm"})}
</Button>,
<Button
className="btn"
color="error"
disabled={!selectedFiles}
variant="contained"
onClick={removeFiles}
>
{/* TODO: {t("button", "Submit", {ns: "fileUploadForm"})}*/}
Beheben
</Button>] : (
<Button
className="btn"
color="error"
variant="contained"
onClick={resetFormState}
>
{/*TODO: {t("button", "Submit", {ns: "fileUploadForm"})}*/}
Neu Hochladen
</Button>
)}
</aside>
</section>
)}
</Dropzone>
{message && message.length > 0 && (
<div style={{
marginTop: "1em"
}}>
{message.map((item, i) => {
return (
<Alert severity={
item.includes("success") ? "success" : "warning"}>
{item}
</Alert>);
})}
</div>
)}
</div>
);
}
export default withTranslation()(FileUploadForm);
where the utilization of useAxios is essential. Nevertheless, users cannot see what they uploaded (preload view before push request). Any clue what could be wrong?
TLDR
DropZone shows uploaded files in the browser (before post request) when FileUploadForm is class and does not in case of a function.
why is my code showing wrong output for
as the rows has been already populated after adding it with the handletabledata function
My code is having a table and there is a add button where we add users but on the delete function when I am trying to see the rows I am getting only that row and rows above it.
please help.
import React from 'react'
import './server.scss'
import Paper from '#mui/material/Paper';
import Table from '#mui/material/Table';
import TableBody from '#mui/material/TableBody';
import TableCell from '#mui/material/TableCell';
import TableContainer from '#mui/material/TableContainer';
import TableHead from '#mui/material/TableHead';
import TablePagination from '#mui/material/TablePagination';
import TableRow from '#mui/material/TableRow';
import { Button } from '#mui/material';
import DeleteOutlineIcon from '#mui/icons-material/DeleteOutline';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import DialogTitle from '#mui/material/DialogTitle';
import Slide from '#mui/material/Slide';
import PersonAddAltIcon from '#mui/icons-material/PersonAddAlt';
import TextField from '#mui/material/TextField';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import Select from '#mui/material/Select';
const columns = [
{ id: 'key', label: '#', minWidth: 70 },
{ id: 'user', label: 'User', minWidth: 100 },
{
id: 'signed',
label: 'Last Signed in',
minWidth: 170,
align: 'right',
},
{
id: 'role',
label: 'Role',
minWidth: 170,
align: 'right',
},
{
id: 'icon',
label: '',
minWidth: 170,
align: 'right',
},
];
function createData(user, role, key, icon, signed) {
signed = signed.getTime()
return { key, user, signed, role, icon };
}
//modal code
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
const Server = () => {
//final insertion of data
const [rows, setrow] = React.useState([]);
// console.log(rows)
//pagination
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
//modal code
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
//setting data for row
const [id, setid] = React.useState(0);
const handletabledata = () => {
setid(id + 1)
let row = [...rows]
row.push(createData(email, role, id, <DeleteOutlineIcon onClick={console.log(rows)}/>, new Date()))
setrow(row)
setemail('')
setrole('')
setOpen(false);
}
//input field
const [email, setemail] = React.useState('');
const handleEmail = (event) => {
setemail(event.target.value);
};
//dropdown list
const [role, setrole] = React.useState('');
const handleChange = (event) => {
setrole(event.target.value);
};
return (
<div className='server_main'>
<div className='addUser'>
<Button variant="contained" onClick={handleClickOpen}>Add User</Button>
<Dialog
open={open}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-describedby="alert-dialog-slide-description"
fullWidth={true}
>
<DialogTitle>{"Add User"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-slide-description">
<div style={{ display: 'flex', height: '250px', justifyContent: 'space-around' }}>
<div >
<PersonAddAltIcon style={{ fontSize: '7rem', position: 'relative', top: '50px' }} />
</div>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
<span style={{ padding: '10px', position: 'relative', left: '10px', top: '10px' }}>User Information</span>
<TextField placeholder="Email Address" onChange={handleEmail} value={email} />
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Role</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={role}
label="role"
onChange={handleChange}
>
<MenuItem value={'Owner'}>Owner</MenuItem>
<MenuItem value={'Sales'}>Sales</MenuItem>
<MenuItem value={'Admin'}>Admin</MenuItem>
</Select>
</FormControl>
</div>
</div>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handletabledata}>Add</Button>
</DialogActions>
</Dialog>
</div>
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
<TableContainer sx={{ maxHeight: 440 }}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{ minWidth: column.minWidth }}
>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={row.code}>
{columns.map((column) => {
const value = row[column.id];
return (
<TableCell key={column.id} align={column.align}>
{column.format && typeof value === 'number'
? column.format(value)
: value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[10, 25, 100]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</Paper>
</div>
);
}
export default Server
As you can see from the image I have a page where,
the part of the drawer and the dark mode change is found on the parent page.
Where the word Page1 and the input field appear, in the child page.
When the theme is changed, then switched to dark mode, a prop with the darkState state is passed from parent to child.
As you can see from the image if I have an input field in which I am writing, so with some text, then I switch to dark mode or I open the drawer.
The component updates everything, losing all its internal state.
I thought about using useMemo, but I don't know where I should use it.
Can you give me a hand?
Link: https://codesandbox.io/s/competent-sara-dru7w?file=/src/page/Page1.js
App.js
import React from "react";
import PropTypes from "prop-types";
import { Switch, Route, Link, useLocation } from "react-router-dom";
import {
AppBar,
CssBaseline,
Drawer,
Hidden,
IconButton,
List,
ListItem,
ListItemIcon,
ListItemText,
Toolbar,
Chip
} from "#material-ui/core";
import { GTranslate, Menu } from "#material-ui/icons";
import {
makeStyles,
useTheme,
createMuiTheme,
ThemeProvider
} from "#material-ui/core/styles";
import { blue, grey } from "#material-ui/core/colors";
import DarkModeToggle from "react-dark-mode-toggle";
import { Page1, Page2, Error } from "./page";
import "./styles/main.css";
import "./App.css";
const drawerWidth = 240;
function App(props) {
const { wind } = props;
const container = wind !== undefined ? () => wind().document.body : undefined;
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const localDark = localStorage.getItem("dark");
const isDark = localDark === null ? prefersDark : localDark === "true";
let location = useLocation();
let pathname = location.pathname.replace("/", "");
if (pathname === "") pathname = "page1";
const [state, setState] = React.useState({
mobileOpen: false,
darkState: isDark,
repo: []
});
const { mobileOpen, darkState } = state;
const useStyles = makeStyles((theme) => ({
root: {
display: "flex"
},
drawer: {
[theme.breakpoints.up("sm")]: {
width: drawerWidth,
flexShrink: 0
}
},
appBar: {
[theme.breakpoints.up("sm")]: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth
}
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up("sm")]: {
display: "none"
},
backgroundColor: darkState ? grey[900] : blue[500]
},
// necessary for content to be below app bar
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
color: "#ffffff",
backgroundColor: darkState ? grey[900] : blue[500]
},
content: {
flexGrow: 1,
padding: theme.spacing(3)
}
}));
const palletType = darkState ? "dark" : "light";
const mainPrimaryColor = darkState ? grey[900] : blue[500];
const mainSecondaryColor = darkState ? grey[800] : blue[300];
const darkTheme = createMuiTheme({
palette: {
type: palletType,
primary: {
main: mainPrimaryColor
},
secondary: {
main: mainSecondaryColor
}
}
});
const classes = useStyles();
const theme = useTheme();
const handleDrawerToggle = () =>
setState((prev) => ({ ...prev, mobileOpen: !mobileOpen }));
const changePage = (page) => setState((prev) => ({ ...prev, page }));
const handleThemeChange = React.useCallback(() => {
localStorage.setItem("dark", !darkState);
setState((prev) => ({ ...prev, darkState: !prev.darkState }));
}, []);
const menu = [
{ title: "Page1", path: "page1", icon: <GTranslate /> },
{ title: "Page2", path: "page2", icon: <GTranslate /> }
];
const routeObj = [
{ path: "/", obj: <Page1 darkState={darkState} /> },
{ path: "page1", obj: <Page1 darkState={darkState} /> },
{ path: "page2", obj: <Page2 darkState={darkState} /> }
];
const drawer = (
<div className="mt-32">
<div className={classes.toolbar} />
<List>
{menu.map(({ title, path, icon, badge }, index) => (
<Link to={`/${path}`} key={title}>
<ListItem button key={title} onClick={() => changePage(path)}>
<ListItemIcon
style={{ color: path === pathname ? "#ffffff" : "#ffffff80" }}
>
{icon}
</ListItemIcon>
<ListItemText
primary={<span className="font-bold">{title}</span>}
style={{ color: path === pathname ? "#ffffff" : "#ffffff80" }}
/>
{badge && (
<Chip
label={badge}
size="small"
color="secondary"
className="font-bold"
style={{ color: "#ffffff" }}
/>
)}
</ListItem>
</Link>
))}
</List>
</div>
);
return (
<ThemeProvider theme={darkTheme}>
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={classes.appBar}
style={{
backgroundColor: darkState ? "#303030" : grey[50],
boxShadow: "none"
}}
>
<Toolbar className={"shadow-none"}>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<Menu />
</IconButton>
<div className="ml-auto text-right flex">
<DarkModeToggle
onChange={handleThemeChange}
checked={darkState}
size={60}
/>
</div>
</Toolbar>
</AppBar>
<nav className={classes.drawer} aria-label="mailbox folders">
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer
container={container}
variant="temporary"
anchor={theme.direction === "rtl" ? "right" : "left"}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper
}}
ModalProps={{
keepMounted: true // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer
classes={{
paper: classes.drawerPaper
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
<div className={classes.toolbar} />
<Switch>
{routeObj.map(({ path, obj }, key) => (
<Route exact path={`/${path}`} component={() => obj} key={key} />
))}
<Route component={() => <Error darkState={darkState} />} />
</Switch>
</main>
</div>
</ThemeProvider>
);
}
App.propTypes = {
/**
* Injected by the documentation to work in an iframe.
* You won't need it on your project.
*/
wind: PropTypes.func
};
export default App;
Page1.js
import React, { useState, useEffect } from "react";
import { TextField, makeStyles } from "#material-ui/core";
import { className } from "../function";
import "../styles/main.css";
export default function Page1({ darkState }) {
const useStyles = makeStyles((theme) => ({
title: {
color: darkState ? "#ffffff" : "#343a40",
textShadow: `3px 3px 2px ${
darkState ? "rgba(0, 0, 0, 1)" : "rgba(150, 150, 150, 1)"
}`
},
button: {
margin: theme.spacing(1)
}
}));
const classes = useStyles();
const [state, setState] = useState({
name: ""
});
const { name } = state;
useEffect(() => {
console.log(darkState, state);
}, []);
useEffect(() => {
console.log("darkState", darkState, state);
}, [darkState]);
const onChange = ({ target: { value } }, name) => {
setState((prev) => ({ ...prev, [name]: value }));
};
console.log(state);
return (
<>
<h1 className={className(classes.title, "text-6xl font-bold hp")}>
Page
<span className="text-primary">1</span>
</h1>
<div
style={{
width: "50%",
minHeight: "600px"
}}
>
<div style={{ paddingBottom: 15 }}>
<TextField
fullWidth
id="outlined-basic"
label={"Name"}
variant="outlined"
size="small"
value={name}
onChange={(value) => onChange(value, "name")}
/>
</div>
</div>
</>
);
}
I need to update Parent table when data is changed from child edit form. Not getting the updated data in parent component.
I'm passing the data using all the passible ways as per my understanding. I'm not much experienced in react. So it would be really very helpful if someone can help me to understand where i'm making mistake.
PARENT Component is class component-
import Radium from 'radium';
import Dock from 'react-dock';
import {Divider} from '#material-ui/core';
import "react-tabulator/lib/styles.css";
import "react-tabulator/css/bootstrap/tabulator_bootstrap.min.css";
import { React15Tabulator, reactFormatter, MultiValueFormatter } from "react-tabulator";
import {Badge, Row, Button, Col,Card} from 'react-bootstrap';
import EditServer from '../EditServer';
import Confirm from '../../../ui-components/Confirm';
// import './index.scss';
import jsondata from '../../../constants/data/json/ApplicationServer.json';
const styles = {
root: {
fontSize: '16px',
color: '#999',
height: '100vh',
},
main: {
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
paddingTop: '30vh',
},
dockContent: {
// width: '400px',
height: '100%',
display: 'flex',
alignItems: 'left',
padding:'1rem',
overflowX:"hidden",
// justifyContent: 'center',
flexDirection: 'column',
},
remove: {
position: 'absolute',
zIndex: 1,
right: '10px',
top: '10px',
cursor: 'pointer'
}
}
const positions = ['left', 'top', 'right', 'bottom'];
const dimModes = ['transparent', 'none', 'opaque'];
// const dimStyle = ['all']
function SimpleBage (props: any) {
const rowData = props.cell._cell.row.data;
const cellValue = props.cell._cell.value || 'STANDALONE | DOWN | PRIMARY | BACKUP';
if(cellValue === 'STANDALONE'){
return <h5><Badge pill variant="warning" className="btn-size"
onClick={() => alert(rowData.serverStatus)}>
{cellValue}</Badge></h5>;
}
else if(cellValue === 'DOWN'){
return <h5><Badge pill variant="dark" className="btn-size">
{cellValue}</Badge></h5>;
}
else if(cellValue === 'PRIMARY'){
return <h5><Badge pill variant="success" className="btn-size">
{cellValue}</Badge></h5>;
}
else if(cellValue === 'BACKUP'){
return <h5><Badge pill variant="primary" className="btn-size">
{cellValue}</Badge></h5>;
}
else if(cellValue === 'STANDBY'){
return <h5><Badge pill variant="secondary" className="btn-size">
{cellValue}</Badge></h5>;
}
}
const editableColumns = [
{
title: "#",
field: "index",
width: 80,
dir:"asc",
// headerFilter: "input"
},
{
title: "Server Name",
field: "serverName",
hozAlign: "left",
dir:"asc",
},
{
title: "Server Status",
field: "serverStatus",
// width: 100,
hozAlign: 'left',
//formatter: MultiValueFormatter,
formatter: reactFormatter(<SimpleBage />),
},
{
title: "Server Group",
field: "serverGroup",
hozAlign: "left",
},
]
let data = [...jsondata];
const serverListArray =localStorage.getItem("configuration");
class ApplicationServer extends Component {
constructor(props) {
super(props);
this.state = {
data: data,
rowSelection:"",
stopBtnVisible:false,
startBtnVisble:true,
selectedServerName: "",
selectedServerGroup:"",
selectedServerStatus:"",
selectedServerStatusChange:this.state,
positionIdx: 2,
dimModeIdx: "transparent",
isVisible: false,
fluid: true,
customAnimation: true,
slow: true,
size: 0.30,
isServerStarted:this.state,
serverName:null,
rowIndex:null,
};
this.handleVisibleChange = this.handleVisibleChange.bind(this);
this.handleChange = this.handleChange.bind(this);
this.onClose= this.onClose.bind(this);
}
ref = null;
onClose = isVisible =>{
this.setState({ isVisible: false });
}
clearData = () => {
this.setState({ data: [] });
};
handleChange = (handler) =>{
// console.log("update handler data", serverGroup)
// const updatedServerGroup = event.target.value;
// console.log("handle form submit in table", this.state.selectedServerGroup, this.props.serverGroup);
console.log("handle form submit in table", handler);
this.setState({
...handler
//selectedServerGroup:handler.target.serverGroup
})
//console.log("handle form submit in table", this.state);
console.log("handle form submit in table", handler);
}
render(){
const options = {
height: 215,
movableRows: true,
history: true,
movableColumns: true,
resizableRows: true ,
reactiveData:true,
};
const duration = this.state.slow ? 1000 : 100;
const dur = duration / 1000;
const transitions = ['left', 'top', 'width', 'height']
.map(p => `${p} ${dur}s cubic-bezier(0, 1.5, 0.5, 1)`)
.join(',');
console.log("Render data:", serverListArray);
if(Array.isArray(serverListArray) && serverListArray.length>0){
data=[...serverListArray];
//jsondata= serverListArray;
}
return(
<div style={[styles.root]}>
<div style={{
position: 'relative',
height: 'calc(100vh - 50px)',
}}>
<Row className="table-control-bar">
<Col sm={8}><h5>Server Controls</h5></Col>
<Col sm={4} className="text-right">
<Button variant={this.state.failoverButtonVariant}
className="mr-2"
id="serverFailover"
onClick={this.handleServerFailover}
selectedserverName={this.selectedserverName}
disabled={this.state.failoverButtonDisabled}>Failover</Button>
<Button variant={this.state.editButtonVariant}
className="mr-2"
id="serverEdit"
selectedServerName={this.selectedServerName}
disabled={this.state.editButtonDisabled}
onClick={this.handleVisibleChange}>Edit</Button>
{this.state.stopBtnVisible === false &&
(
<Button
onClick={this.handleServerStart}
className="mr-2"
id="serverStart"
selectedserverName={this.selectedserverName}
visible={this.state.startBtnVisible}
variant={this.state.startButtonVariant}
disabled={this.state.startButtonDisabled}>Start
</Button>
)}
{this.state.startBtnVisible === false &&
(<Button onClick={this.handleServerStop}
className="mr-2"
id="serverStop"
selectedserverName={this.selectedserverName}
variant="primary">Stop</Button>)}
<Button variant={this.state.restartButtonVariant}
id="serverRestart"
onClick={this.handleServerRestart}
selectedserverName={this.selectedserverName}
disabled={this.state.restartButtonDisabled}>Restart</Button>
</Col>
</Row>
<div>
<React15Tabulator
columns={editableColumns}
data={data}
selectable={1}
rowClick={this.rowClick}
options={options}
data-custom-attr="test-custom-attribute"
className={"custom-css-class" + this.state.rowSelection}
tooltips={true}
layout={"fitData"}
/>
</div>
</div>
<div>
<Dock position={positions[this.state.positionIdx]}
size={this.state.size}
dimMode={dimModes[this.state.dimModeIdx]}
isVisible={this.state.isVisible}
onVisibleChange={this.handleVisibleChange}
fluid={this.state.fluid}
dimStyle={{ background: 'rgba(0, 0, 100, 0.2)'}, {pointerEvents:'all !important'}}
dockStyle={this.state.customAnimation ? { transition: transitions } : null, {top:'6%'} }
// ,{top:'13%'}
dockHiddenStyle={this.state.customAnimation ? {
transition: [transitions, `opacity 0.01s linear ${dur}s`].join(',')
} : null}
duration={duration}>
{({ position, isResizing }) =>
<div style={[styles.dockContent]} className="modal-container">
<h4>Edit Server Group</h4>
<Divider />
<EditServer
handleInputChange={this.handleChange}
// handleChange = {e => console.log(e)}
serverName={this.state.selectedServerName}
serverGroup= {this.state.selectedServerGroup}
onClose={this.onClose} />
<span onClick={() => this.setState({ isVisible: false })}
style={styles.remove} title="close">✖</span>
</div>
}
</Dock>
</div>
</div>
)
}
handleSizeChange = size => {
this.setState({ size });
}
}
export default ApplicationServer = Radium(ApplicationServer);
CHILD Component is functional component-
import {func, string} from 'prop-types';
import {makeStyles } from '#material-ui/core';
import {Row, Col, Button, Form} from 'react-bootstrap';
import toast from '../../../ui-components/Toaster';
import Input from '../../../ui-components/Input';
const useStyles = makeStyles((theme) => ({
root: {
'& > *': {
margin: theme.spacing(2),
},
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300,
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: 2,
},
noLabel: {
marginTop: theme.spacing(3),
},
}));
function getStyles(name, personName, theme) {
return {
fontWeight:
personName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
export default function EditServer(props) {
const {onClose} = props;
let serverGroup = props.serverGroup;
const initialFormState = {
id: null,
serverName: props.serverName,
serverGroup: props.serverGroup,
saveDisabled: true,
cancelDisabled:true,
showDockModal:false,
edited:false,
}
// console.log("inside child", initialFormState);
const [handler, setHandler] = useState();
//const ref = useRef(null);
const handleInputChange = (event) => {
//event.preventDefault();
console.log("edit server props input change", handler);
let { name, value } = event.target;
//handler.serverGroup = event.target.value;
setHandler({...handler, [name]: value, edited:true});
console.log("inside edit form change:", handler, value, name);
}
const handleFormSubmit = (event) => {
//console.log('inside handleFormSubmit', event, props);
event.preventDefault();
onClose();
if (handler.edited){
//let newServerGroup= handler.serverGroup;
//console.log("form submit", handler);
setHandler({
...handler,
edited:false,
});
console.log("inside edit form change:", handler, event)
// props.serverGroup=newServerGroup;
// return handleInputChange(handler.serverGroup.value);
return toast.success(initialFormState.serverGroup + " Server Group Changed to SIMULATION !");
}
}
return (
<React.Fragment>
<Form className="scrollY"
onSubmit={handleFormSubmit}
>
<Row>
<Col sm={4} className="text-align-right">
<label htmlFor="serverName">Server Name<span className="red">*</span></label>
</Col>
<Col sm={8}>
<Input
type="text"
required
id="severName"
name="severName"
// label="Handler Name"
fullWidth
autoComplete="given-name"
value={initialFormState.serverName}
onChange={handleInputChange}
disabled="true"
/>
</Col>
</Row>
<Row>
<Col sm={4} className="text-align-right">
<label htmlFor="serverGroup" required>Server Group<span className="red">*</span></label>
</Col>
<Col sm={8}>
<Input
type="string"
required
id="serverGroup"
name="serverGroup"
// label="Handler Speed"
fullWidth
autoComplete="family-name"
defaultValue={initialFormState.serverGroup}
onChange={handleInputChange}
/>
</Col>
</Row>
<Row>
<Col sm={4}>
{/* <Button variant="secondary" disabled className="btn-custom" onClick={handleClose}>Cancel</Button>*/}
</Col>
<Col sm={8}>
<Button type="submit"
variant="primary"
className="btn-custom"
>
Submit
</Button>
</Col>
</Row>
</Form>
</React.Fragment>
);
}
EditServer.propTypes = {
onSuccess: func,
};
I'm using react tabulator.
Appreciate the help. Thank you.
I have a couple blocks of code that sets the state of by the user enter a value into and input and even checking a checkbox. They are both throwing the classic SyntheticEvent warning:
Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property buttons on a released/nullified synthetic event.
I have tried chacheing and e.persist() and its still throwing the warning on the onChange events:
const handleChange = (e) => {
e.persist();
const { name, checked } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: checked }));
};
const updateValues = (e) => {
e.persist();
const { name, value } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: value }));
};
These change events are on the inputs and check boxes respectively. What am i doing wrong here that the warnings are still being thrown?
Entire Component Code:
import React, { useState, useEffect, useCallback } from 'react';
import { makeStyles, withStyles } from "#material-ui/core/styles";
import _ from "lodash";
import Slide from "#material-ui/core/Slide";
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import MuiDialogTitle from '#material-ui/core/DialogTitle';
import MuiDialogContent from '#material-ui/core/DialogContent';
import MuiDialogActions from '#material-ui/core/DialogActions';
import IconButton from '#material-ui/core/IconButton';
import CloseIcon from '#material-ui/icons/Close';
import Typography from '#material-ui/core/Typography';
import TextField from '#material-ui/core/TextField';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import Checkbox from '#material-ui/core/Checkbox';
import DateFnsUtils from '#date-io/date-fns';
import {
MuiPickersUtilsProvider,
KeyboardDatePicker,
} from '#material-ui/pickers';
import moment from 'moment';
import { toast } from 'react-toastify';
import * as actions from "../../../Redux/Actions";
import { useDispatch, useSelector } from 'react-redux';
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="down" ref={ref} {...props} />;
});
const styles = (theme) => ({
root: {
margin: 0,
padding: theme.spacing(2),
},
closeButton: {
position: 'absolute',
right: theme.spacing(1),
top: theme.spacing(1),
color: theme.palette.grey[500],
},
});
const DialogTitle = withStyles(styles)((props) => {
const { children, classes, onClose, ...other } = props;
return (
<MuiDialogTitle disableTypography className={classes.root} {...other}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton aria-label="close" className={classes.closeButton} onClick={onClose}>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
);
});
const DialogContent = withStyles((theme) => ({
root: {
padding: theme.spacing(2),
},
}))(MuiDialogContent);
const DialogActions = withStyles((theme) => ({
root: {
margin: 0,
padding: theme.spacing(1),
},
}))(MuiDialogActions);
const HolidayDialog = (props) => {
const [noticeModal, setNoticeModal] = useState(props.open);
const [selectedDate, setSelectedDate] = useState(new Date());
const [holidayData, setHolidayData] = useState({});
const dispatch = useDispatch();
useEffect(() => {
setSelectedDate(moment(props.data.HolidayDate));
setNoticeModal(props.open);
setHolidayData(props.data);
}, [props.open]);
const user = useSelector(state => {
return JSON.stringify(state.main.user);
});
const handleDateChange = (date) => {
setSelectedDate(date);
};
const handleClose = () => {
props.onClose(false);
setNoticeModal(false);
};
const handleChange = (e) => {
e.persist();
const { name, checked } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: checked }));
};
const updateValues = (e) => {
e.persist();
const { name, value } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: value }));
};
const formSubmit = () => {
dispatch(actions.holiday_editHoliday(holidayData));
handleClose();
}
const handleSubmit = () => {
dispatch(actions.holiday_submitHoliday(holidayData));
handleClose();
}
const handleCreate = () => {
let createdHoliday = {
Active: true,
Branch: holidayData.Branch ? holidayData.Branch : '',
Close: holidayData.Close ? holidayData.Close : '',
CoOp: holidayData.CoOp ? holidayData.CoOp : false,
HolidayDate: selectedDate ? moment(selectedDate).format('MM/DD/YYYY') : moment().format('MM/DD/YYYY'),
HolidayName: holidayData.HolidayName ? holidayData.HolidayName : "",
Open: holidayData.Branch ? holidayData.Branch : '',
Phone: holidayData.Phone ? holidayData.Phone : true,
Web: holidayData.Web ? holidayData.Web : true,
Hours: holidayData.Hours ? holidayData.Hours : '',
}
dispatch(actions.holiday_createHoliday(createdHoliday));
handleClose();
}
const handlePublish = () => {
const userObj = JSON.parse(user);
const userName = `CCU\\${userObj.UserName}`;
if (userName != holidayData.SubmittedBy) {
dispatch(actions.holiday_publishHoliday(holidayData));
handleClose();
} else {
toast.error("Submitter & Publisher cannot be the same person", {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: false,
draggable: true,
progress: undefined,
});
handleClose();
}
}
const handleDecline = () => {
dispatch(actions.holiday_declineHoliday(holidayData));
handleClose();
}
return (
<Dialog
open={noticeModal}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-labelledby="notice-modal-slide-title"
aria-describedby="notice-modal-slide-description"
>
<DialogTitle id="customized-dialog-title" onClose={handleClose}>
{holidayData.HolidayName ? holidayData.HolidayName : 'Create New Holiday'}
</DialogTitle>
<form noValidate autoComplete="off" id="HolidayForm" onSubmit={(e) => { e.preventDefault(); formSubmit(); } }>
<DialogContent dividers>
<div className="row">
<div className="col">
<TextField required name="HolidayName" id="outlined-basic" label="Holiday Name" variant="outlined" onChange={updateValues} value={holidayData.HolidayName || ''} disabled={holidayData.Submitted != null}/>
</div>
<div className="col">
<TextField id="outlined-basic" name="Branch" label="Branch" variant="outlined" onChange={updateValues} value={holidayData.Branch || ''} disabled={holidayData.Submitted != null}/>
</div>
</div>
<div className="row mt-3">
<div className="col">
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
disableToolbar
variant="inline"
format="MM/dd/yyyy"
margin="normal"
id="date-picker-inline"
label="Holiday Date *"
value={selectedDate}
onChange={handleDateChange}
KeyboardButtonProps={{
'aria-label': 'change date',
}}
disabled={holidayData.Submitted != null}
/>
</MuiPickersUtilsProvider>
</div>
<div className="col">
<TextField id="outlined-basic" name="Hours" label="Hours" variant="outlined" onChange={updateValues} value={holidayData.Hours || ''} disabled={holidayData.Submitted != null}/>
</div>
</div>
{holidayData.Hours ? (
<div className="row mt-3">
<div className="col">
<TextField id="outlined-basic" name="Open" label="Open" variant="outlined" onChange={updateValues} value={holidayData.Open || ''} disabled={holidayData.Submitted != null}/>
</div>
<div className="col">
<TextField id="outlined-basic" name="Close" label="Close" variant="outlined" onChange={updateValues} value={holidayData.Close || ''} disabled={holidayData.Submitted != null}/>
</div>
</div>
) : (
<div></div>
)}
<div className="row mt-3">
<div className="col d-flex flex-column">
<FormControlLabel
control={
<Checkbox
checked={holidayData.Web || false}
value={holidayData.Web}
onChange={handleChange}
name="Web"
color="primary"
disabled={holidayData.Submitted != null}
/>
}
label="Show on Web?"
/>
<FormControlLabel
control={
<Checkbox
checked={holidayData.CoOp || false}
value={holidayData.CoOp}
onChange={handleChange}
name="CoOp"
color="primary"
disabled={holidayData.Submitted != null}
/>
}
label="CoOp Holiday?"
/>
</div>
<div className="col d-flex flex-column">
<FormControlLabel
control={
<Checkbox
checked={holidayData.Phone || false}
value={holidayData.Phone}
onChange={handleChange}
name="Phone"
color="primary"
disabled={holidayData.Submitted != null}
/>
}
label="Use in IVR?"
/>
<FormControlLabel
control={
<Checkbox
checked={holidayData.Active || true}
value={holidayData.Active}
onChange={handleChange}
disabled
name="Active"
color="primary"
/>
}
label="Active"
/>
</div>
</div>
</DialogContent>
<DialogActions className="d-flex">
{holidayData.Submitted ? (
<div className="mr-auto">
<Button variant="outlined" color="primary" onClick={handlePublish}>
Publish
</Button>
<Button variant="outlined" color="secondary" className="ml-2" onClick={handleDecline}>
Decline
</Button>
</div>
) : (
<div className="mr-auto">
<Button variant="outlined" color="primary" onClick={handleSubmit}>
Submit
</Button>
</div>
)}
<Button variant="outlined" onClick={handleClose} color="default">
Cancel
</Button>
{holidayData.Active ? (
<Button variant="outlined" color="primary" type="submit" form="HolidayForm" disabled={holidayData.Submitted != null}>
Update Holiday
</Button>
) : (
<Button variant="outlined" color="primary" onClick={handleCreate} form="HolidayForm">
Create Holiday
</Button>
)}
</DialogActions>
</form>
</Dialog>
)
}
export default HolidayDialog;