react-charts "stacked:true" is not working - reactjs

I've created a chart in ReactJS using react-charts, with multiple bars.
This is my component:
import React, { Component } from "react";
import { Chart } from "react-charts";
import axios from "axios";
const qs = require("qs");
class Volume extends Component {
state = {
load_vol_chart: true,
volume_chart_data: []
};
componentDidMount() {
this.getVolumeChartData();
}
getVolumeChartData() {
var name = this.props.name;
axios
.post(
`http://api.mydomain.in/details/`,
qs.stringify({ type: name })
)
.then(res => {
if (res.data.result === 1) {
this.setState({
volume_chart_data: [
{
label: "Label1",
data: res.data.data2.map(vol => ({
x: vol.created_at,
y: (vol.volume * vol.percentage) / 100
}))
},
{
label: "Label2",
data: res.data.data2.map(vol => ({
x: vol.created_at,
y: vol.volume
}))
}
],
load_vol_chart: false
});
}
});
}
render() {
return (
<div className="inner-content" style={{ marginTop: "12px" }}>
<h3>
<i className="fa fa-caret-right" /> {" "}
{this.props.symbol}: Volume
</h3>
{this.state.load_vol_chart ? null : (
<div
style={{
width: "100%",
height: "400px",
marginTop: "35px",
marginBottom: "10px"
}}
>
<Chart
data={this.state.volume_chart_data}
series={{ type: "bar" }}
axes={[
{ primary: true, type: "ordinal", position: "left" },
{ position: "bottom", type: "linear" }
]}
primaryCursor
secondaryCursor
tooltip
/>
</div>
)}
</div>
);
}
}
export default Volume;
What I want is:
The Label1 bar is calculated with some formula. In charts tooltip, I want to display a additional value like: vol.percent. Also Can I show this percent on bar as a label on it?
I want these charts to stacked. I've tried stacked:true, it's not working.
How can I achieve these?
Thanks in advance.

Related

React Select with avatar class component whats wrong here select not populating

Hi I had working select box with avatar on functional aporx. But I need build in functions to change selectbox properties like hidden: true/false, update option data function to add new option data to display selectbox with certain values on fly.
What I wrong here? Render part works as tested with functional version. The class factoring misses something as select box does not get info ether options and avatar to display and no width calculation happening.
Orginal functional code: https://codesandbox.io/s/react-select-longest-option-width-geunu?file=/src/App.js
Class based works but nodatin selectbox. Here is app.js with select.js fiddle: https://codesandbox.io/s/react-select-longest-option-width-forked-plqq0p?file=/src/Select.js
Source:
import React, { useRef } from "react";
import Select from "react-select";
class RtSelect extends React.Component {
constructor(props) {
super();
this.state = {
info: props.info,
options: props.options,
hidden: props.hidden,
menuIsOpen: false,
menuWidth: "",
IsCalculatingWidth: false
};
this.selectRef = React.createRef();
this.onMenuOpen = this.onMenuOpen.bind(this);
}
componentDidMount() {
if (!this.menuWidth && !this.isCalculatingWidth) {
setTimeout(() => {
this.setState({IsCalculatingWidth: true});
// setIsOpen doesn't trigger onOpenMenu, so calling internal method
this.selectRef.current.select.openMenu();
this.setState({MenuIsOpen: true});
}, 1);
}
}
onMenuOpen() {
if (!this.menuWidth && this.isCalculatingWidth) {
setTimeout(() => {
const width = this.selectRef.current.select.menuListRef.getBoundingClientRect()
.width;
this.setState({menuWidth: width});
this.setState({IsCalculatingWidth: false});
// setting isMenuOpen to undefined and closing menu
this.selectRef.current.select.onMenuClose();
this.setState({MenuIsOpen: true});
}, 1);
}
}
styles = {
menu: (css) => ({
...css,
width: "auto",
...(this.isCalculatingWidth && { height: 0, visibility: "hidden" })
}),
control: (css) => ({ ...css, display: "inline-flex " }),
valueContainer: (css) => ({
...css,
...(this.menuWidth && { width: this.menuWidth })
})
};
setData (props) {
this.setState({
info: props.info,
options: props.options,
hidden: props.hidden
})
}
render() {
return (
<div style={{ display: "flex" }}>
<div style={{ margin: "8px" }}>{this.info}</div>
<div>
<Select
ref={this.selectRef}
onMenuOpen={this.onMenuOpen}
options={this.options}
menuIsOpen={this.menuIsOpen}
styles={this.styles}
isDisabled={this.hidden}
formatOptionLabel={(options) => (
<div className="select-option" style={{ display: "flex" }}>
<div style={{ display: "inline", verticalAlign: "center" }}>
<img src={options.avatar} width="30px" alt="Avatar" />
</div>
<div style={{ display: "inline", marginLeft: "10px" }}>
<span>{options.label}</span>
</div>
</div>
)}
/>
</div>
</div>
);
}
}
export default RtSelect;
Got it working!
I had removed "undefined" from onOpen setState function. I compared those 2 fiddles and finally got it working.
// setting isMenuOpen to undefined and closing menu
this.selectRef.current.select.onMenuClose();
this.setState({MenuIsOpen: undefined});

React change state for all elements except for the chosen one

I have a bunch of buttons that change style on events such as hover, click etc.
I want to do a simple operation with onClick event to highlight the pressed button.
It's doing well but then other buttons need to get rid of the class that highlights them ('light-active' and 'sect-active'). These classes don't go away because the state isActive is true as the button was pressed before.
I can't come up with a solution.
Here's my code: (please note that I refer to buttons as 'pages')
App.js - Root
import React, { Component } from 'react';
import PageButtons from './components/PageButtons';
import { v4 as uuidv4 } from 'uuid';
import PageBody from './components/PageBody';
class App extends Component {
state = {
pages: [
{
id: uuidv4(),
title: 'Body',
active: false,
},
{
id: uuidv4(),
title: 'Parents',
active: false,
},
{
id: uuidv4(),
title: 'Head',
active: false,
},
{
id: uuidv4(),
title: 'Face',
active: false,
},
{
id: uuidv4(),
title: 'Hair',
active: false,
},
{
id: uuidv4(),
title: 'Accessories',
active: false,
},
],
};
render() {
return (
<div className='App'>
<div id='container' style={cont}>
<PageButtons pages={this.state.pages} />
<PageBody />
</div>
</div>
);
}
}
const cont = {
padding: '1em',
width: '40vw',
display: 'grid',
placeContent: 'center',
};
export default App;
PageButtons.js - Parent component for buttons
import React, { Component } from 'react';
import PageButton from './PageButton';
import PropTypes from 'prop-types';
class PageButtons extends Component {
render() {
return (
<div style={pagesStyle}>
{this.props.pages.map((page) => (
<PageButton
key={page.id}
page={page}
/>
))}
</div>
);
}
}
const pagesStyle = {
alignSelf: 'end',
display: 'flex',
flexWrap: 'nowrap',
overflowX: 'auto',
overflowY: 'hidden',
alignItems: 'center',
justifyContent: 'space-between',
width: '30vw',
marginBottom: '1vw',
cursor: 'pointer',
padding: '1.3vw',
};
// PropTypes
PageButtons.propTypes = {
pages: PropTypes.array.isRequired,
};
export default PageButtons;
PageButton.js - the buttons
import React, { Component } from 'react';
class PageButton extends Component {
state = {
isHovered: false,
isActive: false,
};
handleHover = () => {
this.setState({ isHovered: !this.state.isHovered });
};
handleMouseDown = () => {
this.setState({ isActive: !this.state.isActive });
};
render() {
const isHovered = this.state.isHovered;
const isActive = this.state.isActive;
const { id, title } = this.props.page;
return (
<div
id='singlePageBtnStyle'
className='page'
onMouseEnter={this.handleHover}
onMouseLeave={this.handleHover}
onMouseDown={this.handleMouseDown}
>
<div
className={`light ${
isHovered ? 'light-2' : [isActive ? 'light-active' : 'light']
}`}
></div>
<div
className={`sect ${
isHovered ? 'sect-2' : [isActive ? 'sect-active' : 'sect']
}`}
>
<p
dataText={title}
style={{ margin: 'auto' }}
className={`${isHovered ? 'glitch' : ''}`}
>
{title}
</p>
</div>
</div>
);
}
}
export default PageButton;
Since you want only a single button to be active at-a-time, IMO it make more sense to use a separate slice of state to hold an active id/index/etc... (The alternative would be to shallow copy the entire pages array just to toggle false all the active properties and the 1 single true active property)
Remove the active property from your App state. The state here should just drive the content for the menu items.
state = {
pages: [
{
id: uuidv4(),
title: 'Body',
},
{
id: uuidv4(),
title: 'Parents',
},
...
{
id: uuidv4(),
title: 'Accessories',
},
],
};
Let PageButtons hold the active page/button state. Create a toggle handler and pass as an onClick handler to PageButton along with a computed isActive prop.
class PageButtons extends Component {
state = {
activeId: null,
}
// NOTE: Curried function to consume id and return onClick handler function
toggleActive = id => e => this.setState(({ activeId }) => ({
activeId: activeId === id ? null : id, // toggle same id off, or set new
}));
render() {
const { activeId } = this.state;
return (
<div style={pagesStyle}>
{this.props.pages.map((page) => (
<PageButton
isActive={activeId === page.id} // <-- compute if active
onMouseOver={this.handleClick}
onClick={this.toggleActive(page.id)} // <-- enclose id in callback
key={page.id}
page={page}
/>
))}
</div>
);
}
}
Page button should consume these new props. Add the onClick handler where it makes sense and set the active classname by the isActive prop.
class PageButton extends Component {
state = {
isHovered: false,
};
handleHover = () => {
this.setState(({ isHovered }) => ({ isHovered: !isHovered }));
};
render() {
const { isHovered } = this.state;
const { isActive, onClick, page: { id, title } } = this.props;
return (
<div
id='singlePageBtnStyle'
className='page'
onMouseEnter={this.handleHover}
onMouseLeave={this.handleHover}
onClick={onClick} // <-- attach onClick handler
>
<div
className={`light ${
isHovered ? 'light-2' : [isActive ? 'light-active' : 'light']
}`}
></div>
<div
className={`sect ${
isHovered ? 'sect-2' : [isActive ? 'sect-active' : 'sect']
}`}
>
<p
dataText={title}
style={{ margin: 'auto' }}
className={`${isHovered ? 'glitch' : ''}`}
>
{title}
</p>
</div>
</div>
);
}
}

How to apply color to barchar in react-bar-chart

I have installed react-bar-chart using
npm i react-bar-chart --save
And I was using below code to display the bar chart, I can see the bar chart rendering on UI, but color is defaulted to black, I tried adding style={{color: 'blue'}}, but it's not working.
import React, { Component } from "react";
import BarChart from "react-bar-chart";
class Metrics extends Component {
render() {
const data = [
{ text: "DOB", value: 500 },
{ text: "Address", value: 300 },
{ text: "Email", value: 900 },
{ text: "Phone", value: 100 },
{ text: "Name", value: 700 }
];
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
return (
<div>
<div style={{ width: "50%" }}>
<BarChart
ylabel="Quantity"
width={500}
height={500}
margin={margin}
data={data}
style={{ color: "blue" }}
/>
</div>
</div>
);
}
}
export default Metrics;
As it renders as svg, you need to use fill property instead of color:
.bar { fill: blue; }

Is there is way to set pagination arrows to be enabled even there is no data?

how can i set pagination arrows to be enabled even there is no data or even if i'll use a condition to switch disable/enablePagination Arrows
import React from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
import { NavLink } from 'react-router-dom';
import Truncate from 'react-truncate';
// Material-UI
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import AddIcon from '#material-ui/icons/Add';
import EditIcon from '#material-ui/icons/Edit';
import MaterialTable, { MTableToolbar, TablePagination } from 'material-table';
import IconButton from '#material-ui/core/IconButton';
import Tooltip from '#material-ui/core/Tooltip';
import Typography from '#material-ui/core/Typography';
import Zoom from '#material-ui/core/Zoom';
// Components
import Entity from '~/Components/Entity';
import violationsStyles from './styles';
import Strings from '~/Services/Strings';
// Services
import Navigate from '~/Services/Navigate';
#withStyles(violationsStyles)
class Violations extends React.Component {
state = {
data : [],
pageIndex: 0,
pageSize: 1,
totalCount: 0
}
componentDidMount() {
this.get();
}
get() {
const { pageIndex, pageSize } = this.state;
this.entity.get({ pageIndex, pageSize });
}
get options() {
return {
actionsColumnIndex: -1,
pageSize: 10,
filtering: true,
columnsButton: true,
maxBodyHeight: 550,
doubleHorizontalScroll: true,
headerStyle: {
color: '#434343',
fontSize: 13
}
};
}
get localization() {
return {
header: {
actions: '',
},
body: {
emptyDataSourceMessage: Strings.listEmptyLabel,
},
pagination: {
labelRowsPerPage: Strings.rowsPerPageLabel,
labelDisplayedRows: `{from}-{to} ${Strings.fromText} {count}`,
},
toolbar: {
addRemoveColumns: Strings.addRemoveColumns,
showColumnsTitle: Strings.showColumnsTitle
},
};
}
get columns() {
const { classes } = this.props;
return [
{ title: Strings.violationReferenceNumber, field: 'referenceNumber', cellStyle: { width: 110 } },
{
title: Strings.violationDescription,
field: 'description',
render: rowData => (
<Typography>
<Truncate lines={1} ellipsis={<span>... </span>}>
{rowData.description}
</Truncate>
</Typography>
),
cellStyle: { paddingLeft: 0 }
},
{ title: Strings.violationPenalty,
field: 'penaltyTypeId',
lookup: {
1: Strings.inform,
2: Strings.alert,
3: Strings.suspension,
},
cellStyle: { width: '120px' }
},
{
title: Strings.violationStatus,
field: 'isArchived',
lookup: {
false: Strings.violationIsNotSettled,
true: Strings.violationIsSettled,
},
cellStyle: { width: '130px' },
defaultFilter: [ 'false' ]
},
{
title: Strings.listActionsLabel,
field: 'isArchived',
render: rowData => (
<div className={classes.iconWrapper}>
<Choose>
<When condition={rowData.isArchived === 'true'}>
<Tooltip TransitionComponent={Zoom} title={Strings.violationEditActionOn}>
<span>
<IconButton disabled={rowData.isArchived === 'true'}>
<EditIcon fontSize="default"/>
</IconButton>
</span>
</Tooltip>
</When>
<Otherwise>
<IconButton disabled={rowData.isArchived === 'true' ? true : false} onClick={() => Navigate.go(`/violations/editor/${rowData.id}`)}>
<Tooltip TransitionComponent={Zoom} title={Strings.violationListEditLabel}>
<EditIcon fontSize="default"/>
</Tooltip>
</IconButton>
</Otherwise>
</Choose>
</div>
),
filtering: false,
cellStyle: { paddingLeft: 35, paddingRight: 75, textAlign: 'left', justifyContent: 'flex-end', display: 'flex' },
headerStyle: { paddingLeft: 35, textAlign: 'left', }
},
];
}
get components() {
const { classes } = this.props;
return {
Toolbar: props => (
<div className={classes.toolbar}>
<MTableToolbar {...props} />
<div className={classes.customActionsBar}>
<Button component={NavLink} to={'/violations/editor'} variant={'outlined'} color={'primary'}>
<AddIcon className={classes.rightIcon} />
{Strings.addNewViolation}
</Button>
</div>
<div className={classes.tabelSecondHeader}>
<Typography variant='h6'>
{Strings.listOfViolations}
</Typography>
</div>
</div>
),
Pagination: props => (
<TablePagination {...props}
count={this.state.totalCount}
/>
),
};
}
onEntityReceived(data) {
const arr = data.result;
const mutableList = [];
if(arr && arr.length > 0) {
arr.map(item => {
mutableList.push({
...item,
isArchived: item.isArchived.toString()
});
});
this.setState({
data: mutableList,
totalCount: data.totalCount
});
}
}
render() {
const { data } = this.state;
return (
<React.Fragment>
<Helmet>
<title>
{Strings.violationsManegment}
</title>
</Helmet>
<Entity
storeId={'Supervision-Violations'}
entityRef={ref => { this.entity = ref; }}
onEntityPosted={() => this.onEntityPosted()}
onEntityReceived={data => this.onEntityReceived(data)}
render={store => (
<MaterialTable
title={Strings.violationsManegment}
data={data}
isLoading={store.loading}
options={this.options}
localization={this.localization}
columns={this.columns}
components={this.components}
onChangePage={pageIndex => this.setState({ pageIndex })}
onChangeRowsPerPage={pageSize => this.setState({ pageSize })}
/>
)}
/>
</React.Fragment>
);
}
}
Violations.propTypes = {
classes: PropTypes.object,
};
export default Violations;
i need to update the count of table because i'm getting the data from back-end and i'm using server side pagination technique and it appears that the total counts automatically be the total rows received unless i mutated it to be be total count that i received from endpoint because i receive some data per page
Use nextIconButtonProps={{disabled:false}} and backIconButtonProps={{disabled:false}}
could you try using the override that is explained here https://material-table.com/#/docs/features/component-overriding.
Use the code above to try to find a way of that you want. Right now there isnt a property that you can do that in a easy way.
components={{
Pagination: props => (
<TablePagination
{...props}
rowsPerPageOptions={[5, 10, 20, 30]}
rowsPerPage={this.state.numberRowPerPage}
count={this.state.totalRow}
page={
firstLoad
? this.state.pageNumber
: this.state.pageNumber - 1
}
onChangePage={(e, page) =>
this.handleChangePage(page + 1)
}
onChangeRowsPerPage={event => {
props.onChangeRowsPerPage(event);
this.handleChangeRowPerPage(event.target.value);
}}
/>
),
}}

How to update other component when change in single component

I am creating timer component and implement with every task. So when I start my timer for a single task then other task timer will be disabled or hidden. I am trying to disable other timer component on start timer but it gives me only value for current component. So how can I update all components when I start a single timer?
DeveloperTasks.js
import { Mutation, Query } from "react-apollo";
import gql from "graphql-tag";
import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import TaskTimer from "./TaskTimer";
import Note from "./Note";
import getCDTime from "../util/commonfunc";
import Button from "#material-ui/core/Button";
import IconButton from "#material-ui/core/IconButton";
import Paper from "#material-ui/core/Paper";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import CircularProgress from "#material-ui/core/CircularProgress";
import Avatar from "#material-ui/core/Avatar";
import FormControl from "#material-ui/core/FormControl";
import InputLabel from "#material-ui/core/InputLabel";
import Select from "#material-ui/core/Select";
import MenuItem from "#material-ui/core/MenuItem";
import TextField from "#material-ui/core/TextField";
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 Popover from "#material-ui/core/Popover";
import DeleteIcon from "#material-ui/icons/Delete";
import AssignmentIcon from "#material-ui/icons/Assignment";
import NotesIcon from "#material-ui/icons/EventNote";
import AssignmentInd from "#material-ui/icons/AssignmentInd";
import CheckCircleOutline from "#material-ui/icons/CheckCircleOutline";
import CheckCircle from "#material-ui/icons/CheckCircle";
import CloseIcon from "#material-ui/icons/Close";
import Typography from "#material-ui/core/Typography";
import EditIcon from "#material-ui/icons/Edit";
import DateFnsUtils from "#date-io/date-fns";
import {
MuiPickersUtilsProvider,
TimePicker,
DatePicker
} from "material-ui-pickers";
import UserList from "../components/UserList";
import emails from "../components/UserList";
import TodoInlineForm from "../components/TodoInlineForm";
const ms = require("pretty-ms");
//Kanban Quearies
export const tasksQuery = gql`
query Developertasklist($contact_id_c: String) {
Developertasklist(contact_id_c: $contact_id_c) {
id
name
due_date
dtask_start_time
time_tracking_flag
dtask_total_time
status
}
}
`;
//Delete Task Mutation
export const DELETE_TODO = gql`
mutation todo_operations($id: String, $deleted: String) {
todo_operations(id: $id, deleted: $deleted) {
id
}
}
`;
//Complete Task Mutation
const COMPLETE_TASK_OPERATIONS = gql`
mutation todo_operations(
$id: String
$status: String
$actual_due_date: String
) {
todo_operations(
id: $id
status: $status
actual_due_date: $actual_due_date
) {
id
}
}
`;
const styles = theme => ({
root: {
width: "100%",
marginTop: theme.spacing(3),
overflowX: "auto"
},
icon: {
margin: theme.spacing.unit,
fontSize: 20
},
button: {
margin: theme.spacing.unit
},
listroot: {
width: "100%",
minWidth: 900,
backgroundColor: theme.palette.background.paper
},
tasklist: {
marginTop: 30
},
taskwidth: {
width: "55%",
display: "inline-flex"
},
timerwidth: {
width: "25%"
},
width5: {
width: "5%"
},
margin: {
margin: theme.spacing.unit
},
input: {
display: "none"
},
datepadding: {
"padding-right": "10px;",
width: "17%"
},
formControl: {
minWidth: 120
},
elementpadding: {
"padding-right": "10px;"
},
completeIcon: {
color: "Green"
},
popover: {
pointerEvents: "none"
},
label: {
display: "inline",
padding: ".2em .6em .3em",
"font-size": "75%",
"font-weight": "700",
"line-height": 1,
color: "#fff",
"text-align": "center",
"white-space": "nowrap",
"vertical-align": "baseline",
"border-radius": ".25em"
},
labelcomplete: {
"background-color": "#5cb85c"
},
labelprogress: {
"background-color": "#5bc0de"
},
labelonhold: {
"background-color": "#d9534f"
},
labelqafail: {
"background-color": "#d9534f"
},
labelnotstated: {
"background-color": "#777"
},
labelqa: {
"background-color": "#337ab7"
},
labelqapassed: {
"background-color": "#777"
},
labeldefered: {
"background-color": "#f0ad4e"
},
hideelement: {
display: "none"
},
showelement: {
display: "block"
}
});
const DialogTitle = withStyles(theme => ({
root: {
borderBottom: `1px solid ${theme.palette.divider}`,
margin: 0,
padding: theme.spacing.unit * 2
},
closeButton: {
position: "absolute",
right: theme.spacing.unit,
top: theme.spacing.unit,
color: theme.palette.grey[500]
}
}))(props => {
const { children, classes, onClose } = props;
return (
<MuiDialogTitle disableTypography className={classes.root}>
<Typography variant="h6">{children}</Typography>
{onClose ? (
<IconButton
aria-label="Close"
className={classes.closeButton}
onClick={onClose}
>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
);
});
const DialogContent = withStyles(theme => ({
root: {
margin: 0,
padding: theme.spacing.unit * 2
}
}))(MuiDialogContent);
const DialogActions = withStyles(theme => ({
root: {
borderTop: `1px solid ${theme.palette.divider}`,
margin: 0,
padding: theme.spacing.unit
}
}))(MuiDialogActions);
class DeveloperTasks extends React.Component {
state = {
start_date: new Date(),
end_date: new Date(),
status: "",
task: "",
searchTerm: "",
open: false,
anchorEl: null,
selectedValue: emails[1],
openreport: false,
openTodoForm: false,
taskid: ""
};
constructor(props) {
super(props);
this.searchUpdated = this.searchUpdated.bind(this);
}
handleDateChange = name => date => {
this.setState({ [name]: date });
};
handleChange = name => event => {
this.setState({ [name]: event.target.value });
};
handleClickOpen = name => event => {
this.setState({
open: true
});
};
handleClose = () => {
this.setState({ open: false });
};
handleClickDialogOpen = () => {
this.setState({ openreport: true });
};
handleDialogClose = value => {
this.setState({ selectedValue: value, openreport: false });
};
searchUpdated(term) {
this.setState({ searchTerm: term });
}
handlePopoverOpen = event => {
this.setState({ anchorEl: event.currentTarget });
};
handlePopoverClose = () => {
this.setState({ anchorEl: null });
};
handleClickTodoOpen(taskid) {
this.setState({ taskid: taskid, openTodoForm: true });
}
componentWillReceiveProps(newProps) {
this.setState({ openTodoForm: newProps.open });
}
render() {
let todoinlineform = "";
const { classes, contact_id } = this.props;
const { anchorEl } = this.state;
const open = Boolean(anchorEl);
let currdatetime = getCDTime.getCurrentDateTime();
let shownbutton = {
display: "block"
};
if (
this.state.openTodoForm &&
this.state.openTodoForm === true &&
this.state.taskid != ""
) {
todoinlineform = (
<TodoInlineForm
open={this.state.openTodoForm}
taskid={this.state.taskid}
modaltitle="Edit Todo"
/>
);
}
return contact_id === "" ? (
""
) : (
<Query query={tasksQuery} variables={{ contact_id_c: contact_id }}>
{({ loading, error, data: { Developertasklist } }) => {
if (error) return <p>{error}</p>;
if (loading) return <CircularProgress className={classes.progress} />;
//Filter with task name
if (this.state.task && this.state.task != "") {
Developertasklist = Developertasklist.filter(
developertasklist =>
developertasklist.name
.toLowerCase()
.indexOf(this.state.task.toLowerCase()) != -1
);
}
//Task status wise filter
if (this.state.status && this.state.status != "") {
Developertasklist = Developertasklist.filter(
developertasklist => developertasklist.status == this.state.status
);
}
//Label array for apply class on status label
let labelcolor = [
{ status: "In Progress", class: classes.labelprogress },
{ status: "Completed", class: classes.labelcomplete },
{ status: "On Hold", class: classes.labelonhold },
{ status: "QA Fail", class: classes.labelqafail },
{ status: "Not Started", class: classes.labelnotstated },
{ status: "QA", class: classes.labelqa },
{ status: "QA Passed", class: classes.labelqapassed },
{ status: "Deferred", class: classes.labeldefered }
];
return (
<Fragment>
<br />
<div className={classes.tasklist}>
<div className="picker">
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<DatePicker
label="Start Date"
name="start_date"
value={this.state.start_date}
format="yyyy-MM-dd"
onChange={this.handleDateChange("start_date")}
className={classes.datepadding}
animateYearScrolling
/>
<DatePicker
label="End Date"
name="end_date"
value={this.state.end_date}
format="yyyy-MM-dd"
onChange={this.handleDateChange("end_date")}
className={classes.datepadding}
animateYearScrolling
/>
</MuiPickersUtilsProvider>
<Button
type="submit"
variant="contained"
color="primary"
className={classes.button}
>
Search
</Button>
<Button
variant="contained"
color="secondary"
className={classes.button}
>
Reset
</Button>
</div>
<FormControl className={classes.formControl}>
<InputLabel htmlFor="status-simple">Status</InputLabel>
<Select
value={this.state.status}
onChange={this.handleChange("status")}
className={classes.elementpadding}
inputProps={{
name: "status",
id: "status"
}}
>
<MenuItem value="">
<em>Please Select</em>
</MenuItem>
<MenuItem value="Not Started">Not Started</MenuItem>
<MenuItem value="In Progress">In Progress</MenuItem>
<MenuItem value="On Hold">On Hold</MenuItem>
<MenuItem value="Deferred">Deferred</MenuItem>
<MenuItem value="Completed">Completed</MenuItem>
<MenuItem value="QA">QA</MenuItem>
<MenuItem value="QA Passed">QA Passed</MenuItem>
<MenuItem value="QA Fail">QA Fail</MenuItem>
</Select>
</FormControl>
<TextField
id="standard-name"
label="Task"
className={classes.textField}
value={this.state.task}
onChange={this.handleChange("task")}
/>
</div>
<div className={classes.tasklist}>
<Paper className={classes.listroot}>
<List className={classes.listroot}>
{Developertasklist.map((task, index) => {
let statusLabel = labelcolor.filter(
obj => obj.status == task.status
);
let completeStatusClass = classes.hideelement;
let pendingStatusClass = "";
let hidetimer = "";
if (task.status === "Completed") {
pendingStatusClass = classes.hideelement;
hidetimer = "hide";
completeStatusClass = "";
}
if (statusLabel.length > 0)
statusLabel = statusLabel[0].class;
return (
<ListItem key={index} divider="true">
<div className={classes.taskwidth}>
<Avatar>
<AssignmentIcon />
</Avatar>
<ListItemText
primary={
<React.Fragment>
{task.name} - {task.due_date}
</React.Fragment>
}
secondary={
<React.Fragment>
<Typography
component="span"
className={[classes.label, statusLabel]}
color="textPrimary"
>
{task.status}
</Typography>
</React.Fragment>
}
/>
</div>
<div className={classes.timerwidth}>
<div>
<TaskTimer
developerlist={task}
hidetimer={hidetimer}
/>
</div>
</div>
<div className={classes.width5}>
<EditIcon
onClick={event => {
this.handleClickTodoOpen(task.id);
}}
/>
</div>
<div className={classes.width5}>
<Mutation mutation={COMPLETE_TASK_OPERATIONS}>
{(todo_operations, { loading, error }) => (
<CheckCircleOutline
className={pendingStatusClass}
aria-owns={
open ? "mouse-over-popover" : undefined
}
aria-haspopup="true"
onMouseEnter={this.handlePopoverOpen}
onMouseLeave={this.handlePopoverClose}
onClick={event => {
todo_operations({
variables: {
id: task.id,
actual_due_date: currdatetime,
status: "Completed"
}
});
}}
/>
)}
</Mutation>
<Popover
id="mouse-over-popover"
className={classes.popover}
classes={{
paper: classes.paper
}}
open={open}
anchorEl={anchorEl}
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
transformOrigin={{
vertical: "top",
horizontal: "left"
}}
onClose={this.handlePopoverClose}
disableRestoreFocus
>
<Typography>Mark as completed.</Typography>
</Popover>
<CheckCircle
className={[
classes.completeIcon,
completeStatusClass
]}
/>
</div>
<div className={classes.width5}>
<div className={pendingStatusClass}>
{/* <Typography variant="subtitle1">
Selected: {this.state.selectedValue}
</Typography> */}
<AssignmentInd
onClick={this.handleClickDialogOpen}
/>
<UserList
selectedValue={this.state.selectedValue}
open={this.state.openreport}
onClose={this.handleDialogClose}
/>
</div>
</div>
<div className={classes.width5}>
<NotesIcon onClick={this.handleClickOpen()} />
<Dialog
onClose={this.handleClose}
aria-labelledby="customized-dialog-title"
open={this.state.open}
>
<DialogTitle
id="customized-dialog-title"
onClose={this.handleClose}
>
Notes
</DialogTitle>
<DialogContent>
<Note />
</DialogContent>
</Dialog>
</div>
<div className={classes.width5}>
<Mutation mutation={DELETE_TODO}>
{(todo_operations, { loading, error }) => (
<DeleteIcon
aria-label="Delete"
onClick={event => {
todo_operations({
variables: {
id: task.id,
deleted: "1"
}
});
}}
/>
)}
</Mutation>
</div>
</ListItem>
);
})}
</List>
</Paper>
</div>
{todoinlineform}
</Fragment>
);
}}
</Query>
);
}
}
export default withStyles(styles, { withTheme: true })(DeveloperTasks);
TaskTimer.js
import { Mutation, Query } from "react-apollo";
import gql from "graphql-tag";
const React = require("react");
const ms = require("pretty-ms");
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import StartIcon from "#material-ui/icons/PlayCircleFilled";
import StopIcon from "#material-ui/icons/Stop";
import getCDTime from "../util/commonfunc";
//Start timer mutation
const TODO_OPERATIONS = gql`
mutation todo_operations(
$id: String
$status: String
$dtask_start_time: String
$time_tracking_flag: String
$developer_daily_hours: String
$is_task_started: String
$actual_start_date: String
) {
todo_operations(
id: $id
status: $status
dtask_start_time: $dtask_start_time
time_tracking_flag: $time_tracking_flag
developer_daily_hours: $developer_daily_hours
is_task_started: $is_task_started
actual_start_date: $actual_start_date
) {
id
}
}
`;
//Stop timer mutation
const STOP_TIMER = gql`
mutation todo_operations(
$id: String
$dtask_stop_time: String
$dtask_total_time: String
$time_tracking_flag: String
) {
todo_operations(
id: $id
dtask_stop_time: $dtask_stop_time
dtask_total_time: $dtask_total_time
time_tracking_flag: $time_tracking_flag
) {
id
}
}
`;
const styles = theme => ({
button: {
margin: theme.spacing.unit
},
stopbutton: {
margin: theme.spacing.unit,
color: "Red"
},
input: {
display: "none"
},
clock: {
color: "Green",
fontWeight: "700",
fontSize: "15px"
},
hideelement: {
display: "none"
},
timerClass: {
display: "none"
}
});
class TaskTimer extends React.Component {
constructor(props) {
const total_time = !props.developerlist.dtask_total_time
? parseInt(0)
: parseInt(props.developerlist.dtask_total_time);
let statetime = total_time;
let stateison = false;
super(props);
if (props.developerlist.time_tracking_flag === "yes") {
let currentdatetime = new Date(getCDTime.getCurrentDateTime());
let start_time = new Date(props.developerlist.dtask_start_time);
let time_diff = Math.abs(currentdatetime - start_time) / 1000;
statetime = time_diff + total_time;
stateison = true;
this.state = {
time: statetime,
isOn: stateison
};
this.startTimer();
}
this.state = {
time: statetime,
isOn: stateison,
timerClass: "test"
};
this.startTimer = this.startTimer.bind(this);
this.stopTimer = this.stopTimer.bind(this);
}
startTimer(next) {
this.setState({
isOn: true,
time: this.state.time,
timerClass: "abc"
});
this.timer = setInterval(
() =>
this.setState({
time: parseInt(this.state.time + 1)
}),
1000
);
}
stopTimer() {
this.state.time = parseInt(this.state.time);
this.setState({ isOn: false });
clearInterval(this.timer);
}
render() {
let totalTaskTime = parseInt(this.state.time) * 1000;
const { classes, theme } = this.props;
let hideTimerClass =
this.props.hidetimer === "hide" ? classes.hideelement : "";
let currdatetime = getCDTime.getCurrentDateTime();
let start =
(this.state.time == 0 || this.state.time > 0) && this.state.isOn == 0 ? (
<Mutation mutation={TODO_OPERATIONS}>
{(todo_operations, { loading, error }) => (
<StartIcon
variant="contained"
color="primary"
className={[
classes.button,
hideTimerClass,
this.state.timerClass
]}
//className={this.state.timerClass}
onClick={event => {
this.startTimer();
todo_operations({
variables: {
id: this.props.developerlist.id,
status: "In Progress",
dtask_start_time: currdatetime,
time_tracking_flag: "yes",
developer_daily_hours: dailyhours,
is_task_started: "yes",
actual_start_date: currdatetime
}
});
}}
/>
)}
</Mutation>
) : null;
let stop =
this.state.isOn && this.state.isOn == 1 ? (
<Mutation mutation={STOP_TIMER}>
{(todo_operations, { loading, error }) => (
<StopIcon
variant="contained"
className={[classes.stopbutton, hideTimerClass]}
disabled={true}
onClick={event => {
this.stopTimer();
todo_operations({
variables: {
id: this.props.developerlist.id,
dtask_stop_time: currdatetime,
dtask_total_time: this.state.time,
time_tracking_flag: "stop"
}
});
}}
/>
)}
</Mutation>
) : null;
return (
<div>
<div className={classes.clock}>{ms(totalTaskTime)}</div>
{start}
{stop}
</div>
);
}
}
export default withStyles(styles, { withTheme: true })(TaskTimer);
if I'm understanding your problem correctly you are trying to cause an update in sister components when a certain event happens.
I believe that the best way to do this would be to have a state in your parent component (DeveloperTasks) that holds whether or not each timer should be disabled. Then pass a callback into each timer that would update the disabled list in the way that you're looking for.
The way I'm imagining such a callback would work is:
function getDisableOtherTimersCallback(timerNum) {
return timerNum => {
return this.state.setState({disabledList: disabledList.map((value, index) => index == timerNum)})
// this line just goes through and makes sure that the index corresponding to timerNum is the only one that's true
};
}
Then when you render your component for timer n you would pass in the timer you get for timer n.
<TaskTimer
developerlist={task}
hidetimer={this.state.disabledList[n]}
disableOtherTimersCallback={getDisableOtherTimersCallback(n)}
/>
Then whenever you want to disable the other timers you would call this method and your done!
(Also note that you can now use the disabledList to show or hide each timer!)
(Also also note that you need to add the disabledList to your state and initiate it in the way you are looking for)
Hope this helps!

Resources