Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TaskDeadlineDrawer #1166

Merged
merged 4 commits into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react'
import Drawer from '../../molecules/drawer/drawer'
import TaskDeadlineForm from '../../../task/task-deadline-form'

type TaskDeadlineDrawerProps = {
open: boolean
onClose: () => void
taskId: number
task: any
onUpdate: (task: any) => void
classes?: any
}

const TaskDeadlineDrawer = ({
open,
onClose,
taskId,
task,
onUpdate,
classes
}: TaskDeadlineDrawerProps) => {
return (
<Drawer
open={open}
onClose={onClose}
title="Set task deadline"
>
<TaskDeadlineForm
match={{ params: { id: taskId } }}
classes={classes}
open={open}
task={task}
updateTask={(task) => {
onUpdate(task)
onClose()
}}
/>
</Drawer>
)
}

export default TaskDeadlineDrawer
184 changes: 112 additions & 72 deletions frontend/src/components/task/task-deadline-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl'
import MomentComponent from 'moment'
import { withStyles } from '@material-ui/core/styles'

import {
Card,
CardContent,
CardMedia,
Typography,
Button,
Expand Down Expand Up @@ -39,12 +38,32 @@ const messages = defineMessages({
},
})

const styles = theme => ({
btnClearDeadline: {
float: 'left',
marginTop: 10,
color: theme.palette.error.main,
borderColor: theme.palette.error.main
},
dayLabel: {
marginTop: 0,
marginLeft: '2em'
}
})

class TaskDeadlineForm extends Component {
constructor (props) {
constructor(props) {
super(props)
this.state = {
deadline: null,
open: false
deadline: props.task?.deadline || null,
open: false,
dateChanged: false
}
}

componentDidUpdate(prevProps) {
if (prevProps.task?.deadline !== this.props.task?.deadline) {
this.setState({ deadline: this.props.task?.deadline || null })
}
}

Expand All @@ -65,7 +84,10 @@ class TaskDeadlineForm extends Component {
id: this.props.match.params.id,
deadline: this.state.deadline
})
this.setState({ open: true })
this.setState({
open: true,
dateChanged: false
})
}

pickTaskDeadline = (time) => {
Expand All @@ -77,83 +99,100 @@ class TaskDeadlineForm extends Component {
}

handleInputChangeCalendar = (e) => {
this.setState({ deadline: e.target.value })
this.setState({ deadline: e.target.value, dateChanged: true })
}

handleClearDeadline = () => {
this.props.updateTask({
id: this.props.match.params.id,
deadline: null
})
this.setState({ deadline: null })
}

renderChip (label, value) {
renderChip(label, value) {
return (
<Chip
style={ { marginBottom: 20 } }
label={ label }
className={ this.props.classes.chip }
onClick={ () => this.pickTaskDeadline(value) }
style={{ marginBottom: 20 }}
label={label}
className={this.props.classes.chip}
onClick={() => {
this.pickTaskDeadline(value)
this.setState({ dateChanged: false })
}}
/>
)
}

render () {
render() {
const { classes, intl } = this.props

return (
<div>
<Collapse in={ !!this.props.open }>
<Card className={ classes.card }>
<FormattedMessage id='task.status.deadline.subheading.main' defaultMessage='Choose a date that this task should be finished'>
{ (msg) => (
<CardMedia
className={ classes.cover }
image={ timeIcon }
title={ msg }
/>
) }
</FormattedMessage>
<div className={ classes.details }>
<CardContent className={ classes.content }>
<Typography variant='h5'>
<FormattedMessage id='task.status.deadline.headline' defaultMessage='Finish date' />
</Typography>
<Typography variant='body1' color='textSecondary'>
<FormattedMessage id='task.status.deadline.subheading' defaultMessage='Choose a date that this task should be finished' />,
</Typography>
<div className={ classes.chipContainer }>
{ [{ label: intl.formatMessage(messages.deadlineLevel1), value: 7 }, { label: intl.formatMessage(messages.deadlineLevel2), value: 15 }, { label: intl.formatMessage(messages.deadlineLevel3), value: 20 }, { label: intl.formatMessage(messages.deadlineLevel4), value: 30 }].map((item, index) => {
return this.renderChip(item.label, item.value)
})
}
</div>
<form className={ classes.formPayment } action='POST'>
<FormControl fullWidth>
<FormattedMessage id='task.status.deadline.day.label' defaultMessage='Day'>
{ (msg) => (
<InputLabel htmlFor='adornment-amount'>{ msg }</InputLabel>
) }
</FormattedMessage>
<FormattedMessage id='task.status.deadline.day.insert.label' defaultMessage='Choose a date'>
{ (msg) => (
<Input
id='adornment-date'
startAdornment={ <InputAdornment position='start'><DateIcon /></InputAdornment> }
placeholder={ msg }
type='date'
value={ `${MomentComponent(this.state.deadline).format('YYYY-MM-DD')}` || `${MomentComponent().format('YYYY-MM-DD')}` }
onChange={ this.handleInputChangeCalendar }
/>
) }
</FormattedMessage>
</FormControl>
<Button disabled={ !this.state.deadline } onClick={ this.handleDeadline } variant='contained' color='primary' className={ classes.btnPayment }>
{ this.state.deadline
? <FormattedMessage id='task.status.deadline.set.target' defaultMessage='set to target date to {date}' values={ {
date: MomentComponent(this.state.deadline).format('MM/DD/YYYY')
} } />
: <FormattedMessage id='task.deadline.button.save' defaultMessage='Save date' />
}
</Button>
</form>
</CardContent>
<div className={ classes.controls } />
<Collapse in={!!this.props.open}>
<FormattedMessage id='task.status.deadline.subheading.main' defaultMessage='Choose a date that this task should be finished'>
{(msg) => (
<CardMedia
className={classes.cover}
image={timeIcon}
title={msg}
/>
)}
</FormattedMessage>
<div className={classes.details}>
<Typography variant='h5'>
<FormattedMessage id='task.status.deadline.headline' defaultMessage='Finish date' />
</Typography>
<Typography variant='body1' color='textSecondary'>
<FormattedMessage id='task.status.deadline.subheading' defaultMessage='Choose a date that this task should be finished' />,
</Typography>
<div className={classes.chipContainer}>
{[{ label: intl.formatMessage(messages.deadlineLevel1), value: 7 }, { label: intl.formatMessage(messages.deadlineLevel2), value: 15 }, { label: intl.formatMessage(messages.deadlineLevel3), value: 20 }, { label: intl.formatMessage(messages.deadlineLevel4), value: 30 }].map((item, index) => {
return this.renderChip(item.label, item.value)
})
}
</div>
</Card>
<form className={classes.formPayment} action='POST'>
<FormControl fullWidth>
<FormattedMessage id='task.status.deadline.day.label' defaultMessage='Day'>
{(msg) => (
<InputLabel htmlFor='adornment-date' className={classes.dayLabel}>{msg}</InputLabel>
)}
</FormattedMessage>
<FormattedMessage id='task.status.deadline.day.insert.label' defaultMessage='Choose a date'>
{(msg) => (
<Input
id='adornment-date'
startAdornment={<InputAdornment position='start'><DateIcon /></InputAdornment>}
placeholder={msg}
type='date'
value={`${MomentComponent(this.state.deadline).format('YYYY-MM-DD')}` || `${MomentComponent().format('YYYY-MM-DD')}`}
onChange={this.handleInputChangeCalendar}
/>
)}
</FormattedMessage>
</FormControl>
<Button disabled={!this.state.deadline} onClick={this.handleDeadline} variant='contained' color='primary' className={classes.btnPayment}>
{this.state.deadline && this.state.dateChanged
? <FormattedMessage id='task.status.deadline.set.target' defaultMessage='set to target date to {date}' values={{
date: MomentComponent(this.state.deadline).format('MM/DD/YYYY')
}} />
: <FormattedMessage id='task.deadline.button.save' defaultMessage='Save date' />
}
</Button>
{this.state.deadline && (
<Button
onClick={this.handleClearDeadline}
variant='outlined'
color='secondary'
className={classes.btnClearDeadline}
>
<FormattedMessage id='task.deadline.button.clear' defaultMessage='Clear deadline' />
</Button>
)}
</form>
<div className={classes.controls} />
</div>
</Collapse>
</div>
)
Expand All @@ -164,7 +203,8 @@ TaskDeadlineForm.propTypes = {
updateTask: PropTypes.func.isRequired,
match: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired,
open: PropTypes.bool
open: PropTypes.bool,
task: PropTypes.object
}

export default injectIntl(TaskDeadlineForm)
export default injectIntl(withStyles(styles)(TaskDeadlineForm))
40 changes: 27 additions & 13 deletions frontend/src/components/task/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ import TaskStatusIcons from './task-status-icons'

import Constants from '../../consts'

import TaskDeadlineDrawer from '../design-library/templates/task-deadline-drawer/task-deadline-drawer'

const taskCover = require('../../images/task-cover.png')
const inviteCover = require('../../images/funds.png')

Expand Down Expand Up @@ -1047,12 +1049,19 @@ class Task extends Component {
</Typography>
<div>
<Typography variant='h6' className={classes.taskInfoContent}>
<div>
<div>{deliveryDate}</div>
{deadline && parseInt(deadline) > 0 ? <small>in {deadline} days</small>
: <Chip size='small' label={<FormattedMessage id='task.dealine.past' defaultMessage='Overdue' />} />
}
</div>
<Button onClick={() => this.setState({ deadlineForm: true })}>
{task.data.deadline ? (
<div>
<div>{deliveryDate}</div>
{deadline && parseInt(deadline) > 0 ?
<small>in {deadline} days</small>
: <Chip size='small' label={<FormattedMessage id='task.dealine.past' defaultMessage='Overdue' />} />
}
</div>
) : (
<FormattedMessage id='task.deadline.call' defaultMessage='Set deadline' />
)}
</Button>
</Typography>
</div>
</div>
Expand All @@ -1076,30 +1085,35 @@ class Task extends Component {
</Typography>
<div>
<Typography variant='h6' className={classes.taskInfoContent}>
<Button onClick={() => this.setState({ deadlineForm: !this.state.deadlineForm })}>
<Button onClick={() => this.setState({ deadlineForm: true })}>
{task.data.deadline ? (
<div>
<div>{deliveryDate}</div>
{deadline && parseInt(deadline) > 0 ? <small>in {deadline} days</small>
{deadline && parseInt(deadline) > 0 ?
<small>in {deadline} days</small>
: <Chip size='small' label={<FormattedMessage id='task.dealine.past' defaultMessage='Overdue' />} />
}
</div>
) : (
<FormattedMessage id='task.deadline.call' defaultMessage='Set deadline' />
)}

</Button>
</Typography>
</div>
</div>
}
</div>
<div>
<TaskDeadlineForm match={{ params: { id: task.data.id } }} classes={classes} open={this.state.deadlineForm} updateTask={(task) => {
<TaskDeadlineDrawer
open={this.state.deadlineForm}
onClose={() => this.setState({ deadlineForm: false })}
taskId={task.data.id}
task={task.data}
onUpdate={(task) => {
this.props.updateTask(task)
this.setState({ deadlineForm: false })
}} />
</div>
}}
classes={classes}
/>
{task?.data && (task?.data?.orders?.length || task?.data?.Orders?.length) ?
<div>
<TaskPayments orders={(task?.data?.orders || task?.data?.Orders)?.filter(o => o.paid && o.status === 'succeeded')} />
Expand Down
1 change: 1 addition & 0 deletions frontend/src/translations/br.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
"task.status.deadline.day.insert.label": "Escolha uma data",
"task.status.deadline.set.target": "Escolher data para {date}",
"task.deadline.button.save": "Salvar data",
"task.deadline.button.clear": "Limpar data",
"task.invite.title": "Convide alguém para trabalhar nesta tarefa",
"task.funding.title": "Convide alguém para adicionar uma recompensa para essa tarefa",
"task.invite.text.label": "Escreva um texto para ser enviado junto ao convite",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"task.status.deadline.day.insert.label": "Choose a date",
"task.status.deadline.set.target": "Set deadline to {date}",
"task.deadline.button.save": "Save date",
"task.deadline.button.clear": "Clear deadline",
"task.invite.title": "Invite someone to work on this task",
"task.funding.title": "Invite someone to add bounties to this issue",
"task.invite.text.label": "Write an invitation text",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/translations/generated/br.json
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@
"task.claim.form.send": "",
"task.claim.subtitle": "",
"task.claim.title": "",
"task.deadline.button.clear": "",
"task.deadline.button.save": "",
"task.deadline.call": "",
"task.deadline.label": "",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/translations/generated/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@
"task.claim.form.send": "Claim",
"task.claim.subtitle": "If you're the original author of this issue, you can claim this issue so you will be admin and transfer the property to manage the issue on Gitpay.",
"task.claim.title": "Claim issue",
"task.deadline.button.clear": "Clear deadline",
"task.deadline.button.save": "Save date",
"task.deadline.call": "Set deadline",
"task.deadline.label": "Deadline",
Expand Down