From cd0bb308c7fbd91841499c5e25a50870d4911ad2 Mon Sep 17 00:00:00 2001 From: Alexandre Magno Date: Fri, 29 Nov 2024 15:03:21 +0100 Subject: [PATCH] Feat/issue actions revisited (#1160) * replacing issue actions with more simple ones and based on role * fixing no defined task variable * simplifying main bounty actions on the issue page for admin and users --- .../action-buttons/action-buttons.stories.tsx | 87 +++++++++++ .../action-buttons/action-buttons.tsx | 66 ++++++++ .../molecules/drawer/drawer.tsx | 51 ++++--- .../invoice-payment/invoice-payment.tsx | 12 +- .../issue-actions/issue-actions.stories.tsx | 61 ++++++++ .../organisms/issue-actions/issue-actions.tsx | 22 +++ .../task/components/issue-actions-by-role.tsx | 63 ++++++++ .../task/components/send-solution-drawer.tsx | 144 ++++++++++++++++++ .../invoice/payment-method-invoice-tab.tsx | 2 +- .../wallet/payment-method-wallet-tab.tsx | 14 +- .../components/task/send-solution-dialog.js | 2 +- .../src/components/task/task-invite-card.js | 3 +- .../src/components/task/task-payment-form.js | 3 +- frontend/src/components/task/task-payment.js | 31 ++-- frontend/src/components/task/task-solve.js | 85 ++++++++--- frontend/src/components/task/task.js | 87 +---------- .../src/containers/send-solution-drawer.js | 31 ++++ frontend/src/containers/task-payment-form.js | 34 +++++ frontend/src/containers/task-payment.js | 32 ++++ frontend/src/containers/task-solve.js | 18 +++ modules/mail/transfer.js | 12 +- 21 files changed, 700 insertions(+), 160 deletions(-) create mode 100644 frontend/src/components/design-library/molecules/action-buttons/action-buttons.stories.tsx create mode 100644 frontend/src/components/design-library/molecules/action-buttons/action-buttons.tsx create mode 100644 frontend/src/components/design-library/organisms/issue-actions/issue-actions.stories.tsx create mode 100644 frontend/src/components/design-library/organisms/issue-actions/issue-actions.tsx create mode 100644 frontend/src/components/task/components/issue-actions-by-role.tsx create mode 100644 frontend/src/components/task/components/send-solution-drawer.tsx create mode 100644 frontend/src/containers/send-solution-drawer.js create mode 100644 frontend/src/containers/task-payment-form.js create mode 100644 frontend/src/containers/task-payment.js create mode 100644 frontend/src/containers/task-solve.js diff --git a/frontend/src/components/design-library/molecules/action-buttons/action-buttons.stories.tsx b/frontend/src/components/design-library/molecules/action-buttons/action-buttons.stories.tsx new file mode 100644 index 000000000..176da6f35 --- /dev/null +++ b/frontend/src/components/design-library/molecules/action-buttons/action-buttons.stories.tsx @@ -0,0 +1,87 @@ +import React, { Component } from 'react'; +import AddIcon from '@material-ui/icons/Add'; +import EditIcon from '@material-ui/icons/Edit'; +import DeleteIcon from '@material-ui/icons/Delete'; +import CancelIcon from '@material-ui/icons/Cancel'; +import SaveIcon from '@material-ui/icons/Save'; +import ActionButtons from './action-buttons'; +import { Dialog } from '@material-ui/core'; + +export default { + title: 'Design Library/Molecules/ActionButtons', + component: ActionButtons, +}; + +const Template = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + primary: [ + { + key: 'create', + onClick: () => {}, + label: 'Create', + disabled: false, + icon: , + component: create, + }, + ], + secondary: [ + { + key: 'cancel', + onClick: () => {}, + label: 'Cancel', + disabled: false, + icon: , + component: cancel, + }, + ], +}; + +export const WithManyActions = Template.bind({}); +WithManyActions.args = { + primary: [ + { + key: 'create 2', + onClick: () => {}, + label: 'Create', + disabled: false, + icon: , + component: create 2, + }, + { + key: 'edit', + onClick: () => {}, + label: 'Edit', + disabled: false, + icon: , + component: edit, + }, + { + key: 'delete', + onClick: () => {}, + label: 'Delete', + disabled: false, + icon: , + component: delete, + }, + ], + secondary: [ + { + key: 'cancel secondary', + onClick: () => {}, + label: 'Cancel', + disabled: false, + icon: , + component: cancel secondary, + }, + { + key: 'save secondary', + onClick: () => {}, + label: 'Save', + disabled: false, + icon: , + component: save secondary, + }, + ], +}; \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/action-buttons/action-buttons.tsx b/frontend/src/components/design-library/molecules/action-buttons/action-buttons.tsx new file mode 100644 index 000000000..e1a6d79eb --- /dev/null +++ b/frontend/src/components/design-library/molecules/action-buttons/action-buttons.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { Button } from '@material-ui/core'; + + +interface ActionButtonsProps { + primary: any[]; + secondary: any[]; +} + +const ActionButtons: React.FC = ({ primary, secondary }) => { + const [ currentKey, setCurrentKey ] = React.useState(''); + + const actionClick = (key:string, onClick: any) => { + setCurrentKey(key); + onClick?.(); + } + + return ( +
+ {primary?.map((action, index) => ( + <> +
+ +
+ {action.component && React.cloneElement(action.component, { open: action.key === currentKey, onClose: () => setCurrentKey('') })} + + ))} +
+ {secondary?.map((action, index) => ( + <> + + {action.component && React.cloneElement(action.component, { open: action.key === currentKey, onClose: () => setCurrentKey('') })} + + ))} +
+
+ ); +}; + +export default ActionButtons; \ No newline at end of file diff --git a/frontend/src/components/design-library/molecules/drawer/drawer.tsx b/frontend/src/components/design-library/molecules/drawer/drawer.tsx index b4cc99213..481394f6c 100644 --- a/frontend/src/components/design-library/molecules/drawer/drawer.tsx +++ b/frontend/src/components/design-library/molecules/drawer/drawer.tsx @@ -5,6 +5,7 @@ import { Container, Typography, Fab, + Box, } from '@material-ui/core' import CloseIcon from '@material-ui/icons/Close' @@ -22,7 +23,7 @@ const useStyles = makeStyles((theme: Theme) => ({ boxShadow: 'none' }, })); - + type DrawerProps = { open: boolean; onClose: any; @@ -42,13 +43,13 @@ const Drawer = ({ const classes = useStyles(); const closeDialogButton = () => { - - return ( - - - - ) - + + return ( + + + + ) + } return ( @@ -57,18 +58,30 @@ const Drawer = ({ aria-labelledby='form-dialog-title' anchor='right' > - -
- - {title} - - {closeDialogButton()} - {children} - { actions.length > 0 && - + + + +
+ + {title} + + {closeDialogButton()} + {children} +
+
+ + {actions.length > 0 && + + + } -
-
+ + ) } diff --git a/frontend/src/components/design-library/organisms/invoice-payment/invoice-payment.tsx b/frontend/src/components/design-library/organisms/invoice-payment/invoice-payment.tsx index 1220c670c..a9ce34d50 100644 --- a/frontend/src/components/design-library/organisms/invoice-payment/invoice-payment.tsx +++ b/frontend/src/components/design-library/organisms/invoice-payment/invoice-payment.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Button } from '@material-ui/core'; -import { Theme, createStyles, withStyles } from '@material-ui/core/styles'; +import { Theme, createStyles, makeStyles, withStyles } from '@material-ui/core/styles'; import { FormattedMessage } from 'react-intl'; import { BillingInfoCard } from '../../molecules/billing-info-card/billing-info-card' import ReactPlaceholder from 'react-placeholder'; @@ -10,22 +10,24 @@ import { Alert, AlertTitle } from '@material-ui/lab' -const styles = (theme: Theme) => +const useStyles = makeStyles((theme: Theme) => createStyles({ btnPayment: { float: 'right', marginTop: 10 }, }) +) const InvoicePayment = ({ price, customer, onInvoicePayment, - onInfoClick, - classes + onInfoClick }) => { + const classes = useStyles() + const { name, address } = customer.data return ( @@ -81,4 +83,4 @@ const InvoicePayment = ({ ) } -export default withStyles(styles)(InvoicePayment) \ No newline at end of file +export default InvoicePayment \ No newline at end of file diff --git a/frontend/src/components/design-library/organisms/issue-actions/issue-actions.stories.tsx b/frontend/src/components/design-library/organisms/issue-actions/issue-actions.stories.tsx new file mode 100644 index 000000000..f31b5b9fd --- /dev/null +++ b/frontend/src/components/design-library/organisms/issue-actions/issue-actions.stories.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import IssueActions from './issue-actions'; + +const roles = { + admin: { + primary: [ + { + onClick: () => {}, + label: 'Pay contributor', + disabled: false, + icon: null, + }, + ], + secondary: [ + { + onClick: () => {}, + label: 'Make a payment', + disabled: false, + icon: null, + }, + ], + }, + user: { + primary: [ + { + onClick: () => {}, + label: 'Create', + disabled: false, + icon: null, + }, + ], + secondary: [ + { + onClick: () => {}, + label: 'Cancel', + disabled: false, + icon: null, + }, + ], + }, +} + +export default { + title: 'Design Library/Organisms/IssueActions', + component: IssueActions, +}; + +const Template = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + role: 'admin', + roles, + +}; + +export const User = Template.bind({}); +User.args = { + role: 'user', + roles, +} \ No newline at end of file diff --git a/frontend/src/components/design-library/organisms/issue-actions/issue-actions.tsx b/frontend/src/components/design-library/organisms/issue-actions/issue-actions.tsx new file mode 100644 index 000000000..396952af0 --- /dev/null +++ b/frontend/src/components/design-library/organisms/issue-actions/issue-actions.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import ActionButtons from '../../molecules/action-buttons/action-buttons'; + +interface IssueActionsProps { + role: string; + roles: any; + children?: React.ReactNode; +} + +const IssueActions = ({ + role, + roles, +}:IssueActionsProps) => { + return ( + + ); +}; + +export default IssueActions; \ No newline at end of file diff --git a/frontend/src/components/task/components/issue-actions-by-role.tsx b/frontend/src/components/task/components/issue-actions-by-role.tsx new file mode 100644 index 000000000..4f3e21ac2 --- /dev/null +++ b/frontend/src/components/task/components/issue-actions-by-role.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { + Redeem as RedeemIcon, + CreditCard as BountyIcon, + HowToReg as HowToRegIcon, +} from '@material-ui/icons'; +import IssueActions from '../../design-library/organisms/issue-actions/issue-actions'; +import TaskPaymentContainer from '../../../containers/task-payment'; +import TaskPaymentFormContainer from '../../../containers/task-payment-form'; +import TaskSolutionDrawer from '../../../containers/send-solution-drawer'; + +interface IssueActionsProps { + issue: any; + currentRole: string; + children?: React.ReactNode; +} + +const IssueActionsByRole = ({ + issue, + currentRole, +}:IssueActionsProps) => { + const { data } = issue; + const shouldDisable = data?.paid || data?.transfer_id || data?.Transfer?.id; + const commonAction = { + key: 'add-bounty', + onClick: () => {}, + label: 'Add bounty', + disabled: shouldDisable, + icon: , + component: , + } + const roles = { + admin: { + primary: [ + commonAction + ] + }, + user: { + primary: [ + { + key: 'send-solution', + onClick: () => {}, + label: 'Send solution', + disabled: shouldDisable, + icon: , + component: , + }, + ], + secondary: [ + commonAction + ], + }, + } + return ( + + ); +}; + +export default IssueActionsByRole; \ No newline at end of file diff --git a/frontend/src/components/task/components/send-solution-drawer.tsx b/frontend/src/components/task/components/send-solution-drawer.tsx new file mode 100644 index 000000000..9683f01f8 --- /dev/null +++ b/frontend/src/components/task/components/send-solution-drawer.tsx @@ -0,0 +1,144 @@ +import React, { useState, useEffect } from 'react' +import { Redirect, withRouter } from 'react-router-dom' +import SendSolutionForm from '../send-solution-form' +import { + DialogTitle, + DialogActions, + DialogContent, + Button, + Typography +} from '@material-ui/core' +import { FormattedMessage } from 'react-intl' +import { validAccount } from '../../../utils/valid-account' +import AccountRequirements from '../../../components/design-library/molecules/account-requirements/account-requirements' +import SendSolutionRequirements from '../send-solution-requirements' +import TaskSolution from '../task-solution' +import Drawer from '../../design-library/molecules/drawer/drawer' + +const SendSolutionDialog = props => { + const [pullRequestURL, setPullRequestURL] = useState('') + const [editMode, setEditMode] = useState(false) + const [timer, setTimer] = useState(null) + + const { taskSolution, pullRequestData, task, user, fetchAccount, account, history } = props + + useEffect(() => { + user.id && task.data.id && props.getTaskSolution(user.id, task.data.id) + }, [user, task]) + + useEffect(() => { + props.cleanPullRequestDataState() + }, [props.assignDialog]) + + useEffect(() => { + if (pullRequestURL.length >= 20) { + clearTimeout(timer) + setTimer(setTimeout(() => { + const urlSplitted = pullRequestURL.split('/') + props.fetchPullRequestData(urlSplitted[3], urlSplitted[4], urlSplitted[6], props.user.id, task.data.id) + }, 2500)) + } + }, [pullRequestURL]) + + useEffect(() => { + fetchAccount() + }, []) + + const handlePullRequestURLChange = (event) => { + setPullRequestURL(event.target.value) + } + + const handleTaskSolutionUpdate = () => { + console.log('handleTaskSolutionUpdate') + setEditMode(true) + } + + const submitTaskSolution = () => { + console.log('submitTaskSolution', editMode) + if (editMode) { + const payload = { pullRequestURL: pullRequestURL, taskId: task.data.id, userId: props.user.id, taskSolutionId: taskSolution.id } + props.updateTaskSolution(payload).then((solution) => { + + }) + setEditMode(false) + // eslint-disable-next-line no-useless-return + return + } + + const payload = { ...pullRequestData, pullRequestURL: pullRequestURL, taskId: task.data.id, userId: props.user.id } + + props.createTaskSolution(payload).then((solution) => { + + }) + } + + /* + if(!user.id) { + return + } + */ + + return ( + + } + actions={[ + { + label: , + onClick: props.onClose, + variant: 'text', + color: 'default', + disabled: false, + }, + Object.keys(props.taskSolution).length !== 0 && !editMode + ? + { + onClick: handleTaskSolutionUpdate, + label: , + variant: 'contained', + color: 'primary', + disabled: task.data.paid || task.data.Transfer || task.data.transfer_id, + } + : + { + onClick: submitTaskSolution, + label: , + variant: 'contained', + color: 'primary', + disabled: !pullRequestURL || + !pullRequestData.isConnectedToGitHub || + !pullRequestData.isAuthorOfPR || + !pullRequestData.isPRMerged || + !pullRequestData.isIssueClosed || + !pullRequestData.hasIssueReference || + task.data.paid || + task.data.transfer_id || + task.data.Transfer || + !validAccount(user, account), + } + ]} + + > + + + + history.push('/profile/user-account/payouts')} + /> + { Object.keys(props.taskSolution).length === 0 || editMode + ? + + + + : + } + + + ) +} + +export default withRouter(SendSolutionDialog) diff --git a/frontend/src/components/task/payment/methods/invoice/payment-method-invoice-tab.tsx b/frontend/src/components/task/payment/methods/invoice/payment-method-invoice-tab.tsx index ef77b2e89..3da46270f 100644 --- a/frontend/src/components/task/payment/methods/invoice/payment-method-invoice-tab.tsx +++ b/frontend/src/components/task/payment/methods/invoice/payment-method-invoice-tab.tsx @@ -56,7 +56,7 @@ const PaymentMethodInvoiceTab: React.FC { user.id && fetchCustomer(user.id); - }, [fetchCustomer, user]); + }, []); if (!user.id) return
; diff --git a/frontend/src/components/task/payment/methods/wallet/payment-method-wallet-tab.tsx b/frontend/src/components/task/payment/methods/wallet/payment-method-wallet-tab.tsx index 27b6dd4f8..eacc6d773 100644 --- a/frontend/src/components/task/payment/methods/wallet/payment-method-wallet-tab.tsx +++ b/frontend/src/components/task/payment/methods/wallet/payment-method-wallet-tab.tsx @@ -1,12 +1,20 @@ import React, { useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; import ReactPlaceholder from 'react-placeholder' -import { Button } from '@material-ui/core' +import { Button, createStyles, makeStyles, Theme } from '@material-ui/core' import { formatCurrency } from '../../../../../utils/format-currency' import BalanceCard from '../../../../design-library/molecules/balance-card/balance-card' +const useStyles = makeStyles((theme: Theme) => + createStyles({ + btnPayment: { + float: 'right', + marginTop: 10 + }, + }) +) + const PaymentMethodWalletTab = ({ - classes, user, task, price, @@ -22,6 +30,8 @@ const PaymentMethodWalletTab = ({ syncTask }) => { + const classes = useStyles() + const onWalletPayment = async () => { await createOrder({ provider: 'wallet', diff --git a/frontend/src/components/task/send-solution-dialog.js b/frontend/src/components/task/send-solution-dialog.js index c496fa3f0..88c9384bd 100644 --- a/frontend/src/components/task/send-solution-dialog.js +++ b/frontend/src/components/task/send-solution-dialog.js @@ -27,7 +27,7 @@ const SendSolutionDialog = props => { useEffect(() => { props.cleanPullRequestDataState() - }, [props.assignDialog]) + }, [props.open]) useEffect(() => { if (pullRequestURL.length >= 20) { diff --git a/frontend/src/components/task/task-invite-card.js b/frontend/src/components/task/task-invite-card.js index 1c33e985e..5852b2553 100644 --- a/frontend/src/components/task/task-invite-card.js +++ b/frontend/src/components/task/task-invite-card.js @@ -11,7 +11,8 @@ import TaskInvite from './task-invite' const useStyles = makeStyles({ root: { - + marginBottom: 20, + marginTop: 20, }, }) diff --git a/frontend/src/components/task/task-payment-form.js b/frontend/src/components/task/task-payment-form.js index 2f8fb1294..7fbba4cba 100644 --- a/frontend/src/components/task/task-payment-form.js +++ b/frontend/src/components/task/task-payment-form.js @@ -1,4 +1,5 @@ import React, { Component } from 'react' +import { withRouter } from 'react-router-dom' import PropTypes from 'prop-types' import { FormattedMessage, injectIntl, defineMessages } from 'react-intl' @@ -231,4 +232,4 @@ TaskPaymentForm.propTypes = { createOrder: PropTypes.func.isRequired } -export default injectIntl(TaskPaymentForm) +export default withRouter(injectIntl(TaskPaymentForm)) diff --git a/frontend/src/components/task/task-payment.js b/frontend/src/components/task/task-payment.js index 15fdb6734..60a8df124 100644 --- a/frontend/src/components/task/task-payment.js +++ b/frontend/src/components/task/task-payment.js @@ -137,8 +137,9 @@ class TaskPayment extends Component { } payTask = e => { + const { task } = this.props //this.props.onPayTask(this.props.id, this.props.values.card) - this.props.onTransferTask(this.props.id) + this.props.onTransferTask(task.data.id) this.props.onClose() } @@ -168,7 +169,7 @@ class TaskPayment extends Component { } render() { - const { classes, orders, offers, ...other } = this.props + const { classes, task, orders, offers, ...other } = this.props const TabContainer = props => { return ( @@ -192,7 +193,7 @@ class TaskPayment extends Component { const assign = this.props.assigns.filter(item => item.userId === offer.userId)[0] - await task.id && loggedUser.logged && await createOrder({ + await task.data.id && loggedUser.logged && await createOrder({ provider: 'stripe', amount: offer.value, userId: loggedUser?.user?.id, @@ -206,14 +207,14 @@ class TaskPayment extends Component { offer_id: offer.id, } }) - await assignTask(task.id, assign.id) - await this.props.offerUpdate(task.id, offer.id, { status: 'accepted' }) + await assignTask(task.data.id, assign.id) + await this.props.offerUpdate(task.data.id, offer.id, { status: 'accepted' }) this.setState({ confirmOrderDialog: false, currentOffer: null }) } const onReject = async (event, offer) => { event.preventDefault() - this.props.offerUpdate(this.props.task.id, offer.id, { status: 'rejected' }) + this.props.offerUpdate(task.data.id, offer.id, { status: 'rejected' }) } const sendTo = id => { @@ -376,11 +377,11 @@ class TaskPayment extends Component {
- {(!this.props.paid || this.props.task?.Transfer?.id) ? ( + {(!this.props.task?.data?.paid || this.props.task?.Transfer?.id) ? (
{this.props.assigned ? {hasOrders() ? (
- {!this.props.paid && ( + {!this.props.task.data.paid && ( )} @@ -462,21 +463,13 @@ class TaskPayment extends Component { } TaskPayment.propTypes = { - classes: PropTypes.object.isRequired, - onClose: PropTypes.func, onPay: PropTypes.func, onPayOrder: PropTypes.func, onPayTask: PropTypes.func, onTransfer: PropTypes.func, - selectedValue: PropTypes.string, - id: PropTypes.number, orders: PropTypes.array, - assigned: PropTypes.number, - paid: PropTypes.bool, assigns: PropTypes.array, - transferId: PropTypes.string, - filterTaskOrders: PropTypes.func, - values: PropTypes.object + filterTaskOrders: PropTypes.func } export default injectIntl(withStyles(styles)(TaskPayment)) diff --git a/frontend/src/components/task/task-solve.js b/frontend/src/components/task/task-solve.js index c32361112..a08b23332 100644 --- a/frontend/src/components/task/task-solve.js +++ b/frontend/src/components/task/task-solve.js @@ -1,13 +1,12 @@ import React, { useState } from 'react' -import { injectIntl, FormattedMessage, FormattedHTMLMessage } from 'react-intl' +import { injectIntl, FormattedMessage } from 'react-intl' import MomentComponent from 'moment' +import { withRouter } from 'react-router-dom' import classNames from 'classnames' import { Dialog, - DialogActions, DialogContent, DialogTitle, - Grid, Avatar, Card, CardHeader, @@ -15,26 +14,12 @@ import { Button, Fab, Tooltip, - Chip, - Paper, - FormControl, - Input, - InputAdornment, - InputLabel, - Checkbox, Link, - FormControlLabel, - DialogContentText, - AppBar, Tabs, Tab, - TextareaAutosize, + withStyles } from '@material-ui/core' import { - DateRange as DateIcon, - CalendarToday as CalendarIcon, - Warning as WarningIcon, - Info as InfoIcon, Close as CloseIcon } from '@material-ui/icons' import LoginButton from '../session/login-button' @@ -43,7 +28,35 @@ import TaskSolveInstructions from './task-solve-instructions' import logoGithub from '../../images/github-logo-black.png' import logoBitbucket from '../../images/bitbucket-logo-blue.png' -import Task from './task' + +const styles = theme => ({ + closeButton: { + position: 'absolute', + right: theme.spacing(1), + top: theme.spacing(1), + backgroundColor: 'darkgray', + color: 'white', + boxShadow: 'none' + }, + mainBlock: { + textAlign: 'center', + padding: 20 + }, + cardHeader: { + paddingBottom: 0 + }, + cardAvatar: { + display: 'inline-block' + }, + avatar: { + width: 40, + height: 40 + }, + taskTitle: { + color: 'black', + textDecoration: 'none' + }, +}) const TaskSolve = ({ classes, @@ -51,9 +64,7 @@ const TaskSolve = ({ open, onClose, location, - logged, - renderIssueAuthorLink, - timePlaceholder + logged }) => { const [currentTab, setCurrentTab] = useState(0) @@ -61,6 +72,34 @@ const TaskSolve = ({ setCurrentTab(value) } + const updatedAtTimeString = task.data.metadata ? MomentComponent(task.data.metadata.issue.updated_at).utc().fromNow() : 'not available' + const timePlaceholder = ( + + {updatedAtTimeString} + + ) + + const renderIssueAuthorLink = () => { + if (task.data.metadata && task.data.metadata.issue.user.html_url) { + return ( + + + + ) + } + else { + return ( + + ) + } + } + const closeDialogButton = () => { return ( @@ -234,4 +273,4 @@ const TaskSolve = ({ ) } -export default injectIntl(TaskSolve) +export default withRouter(injectIntl(withStyles(styles)(TaskSolve))) diff --git a/frontend/src/components/task/task.js b/frontend/src/components/task/task.js index 33cca82d3..50ee5dd80 100644 --- a/frontend/src/components/task/task.js +++ b/frontend/src/components/task/task.js @@ -46,6 +46,7 @@ import { import OfferDrawer from '../design-library/templates/offer-drawer/offer-drawer' +import IssueActionsByRole from './components/issue-actions-by-role' import TopBarContainer from '../../containers/topbar' import Bottom from '../bottom/bottom' import TaskPayment from './task-payment' @@ -1106,75 +1107,10 @@ class Task extends Component { o.paid && o.status === 'succeeded')} />
: null } - { this.taskOwner() && - -
- - -
-
- } -
- -
- -
- {this.rendereAmountStatsCardContent(this.taskOwner())} -
+ - {noBottomBar ? null : ( diff --git a/frontend/src/containers/send-solution-drawer.js b/frontend/src/containers/send-solution-drawer.js new file mode 100644 index 000000000..8376e832b --- /dev/null +++ b/frontend/src/containers/send-solution-drawer.js @@ -0,0 +1,31 @@ +import { connect } from 'react-redux' +import { fetchAccount } from '../actions/userActions' +import { getTaskSolution, createTaskSolution, updateTaskSolution, fetchPullRequestData, cleanPullRequestDataState } from '../actions/taskSolutionActions' +import { getUser } from '../common/selectors/user/getUser' +import SendSolutionDrawer from '../components/task/components/send-solution-drawer' + +const mapStateToProps = state => { + return { + user: getUser(state), + account: state.account, + taskSolution: state.taskSolutionReducer.taskSolution, + pullRequestData: state.taskSolutionReducer.pullRequestData, + task: state.task, + completed: state.taskSolutionReducer.completed + } +} + +const mapDispatchToProps = dispatch => { + return { + getTaskSolution: (userId, taskId) => dispatch(getTaskSolution(userId, taskId)), + createTaskSolution: (taskSolution) => dispatch(createTaskSolution(taskSolution)), + updateTaskSolution: (payload) => dispatch(updateTaskSolution(payload)), + fetchPullRequestData: (owner, repositoryName, pullRequestId, userId, taskId) => dispatch( + fetchPullRequestData(owner, repositoryName, pullRequestId, userId, taskId) + ), + cleanPullRequestDataState: () => dispatch(cleanPullRequestDataState()), + fetchAccount: () => dispatch(fetchAccount()) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(SendSolutionDrawer) diff --git a/frontend/src/containers/task-payment-form.js b/frontend/src/containers/task-payment-form.js new file mode 100644 index 000000000..8ebd951e7 --- /dev/null +++ b/frontend/src/containers/task-payment-form.js @@ -0,0 +1,34 @@ +import { connect } from 'react-redux' +import { fetchCustomer } from '../actions/userActions' +import { updateTask, fetchTask, syncTask } from '../actions/taskActions' +import { createOrder } from '../actions/orderActions' +import { fetchWallet, listWallets } from '../actions/walletActions' +import { getTaskOrdersByFilter } from '../selectors/task' +import { getUser } from '../common/selectors/user/getUser' +import TaskPaymentForm from '../components/task/task-payment-form' + +const mapStateToProps = (state, ownProps) => { + return { + logged: state.loggedIn, + user: getUser(state), + task: getTaskOrdersByFilter(state), + order: state.order, + customer: state.customer, + wallets: state.wallets, + wallet: state.wallet, + } +} + +const mapDispatchToProps = (dispatch, ownProps) => { + return { + updateTask: (task) => dispatch(updateTask(task)), + fetchTask: (taskId) => dispatch(fetchTask(taskId)), + syncTask: (taskId) => dispatch(syncTask(taskId)), + createOrder: (order) => dispatch(createOrder(order)), + fetchCustomer: (id) => dispatch(fetchCustomer(id)), + fetchWallet: (id) => dispatch(fetchWallet(id)), + listWallets: () => dispatch(listWallets()), + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(TaskPaymentForm) diff --git a/frontend/src/containers/task-payment.js b/frontend/src/containers/task-payment.js new file mode 100644 index 000000000..3bd7301ff --- /dev/null +++ b/frontend/src/containers/task-payment.js @@ -0,0 +1,32 @@ +import { connect } from 'react-redux' +import { assignTask, removeAssignment, actionAssign } from '../actions/assignActions' +import { updateTask, paymentTask, filterTaskOrders, transferTask } from '../actions/taskActions' +import { createOrder, payOrder } from '../actions/orderActions' +import { getTaskOrdersByFilter } from '../selectors/task' +import { getUser } from '../common/selectors/user/getUser' +import TaskPayment from '../components/task/task-payment' + +const mapStateToProps = (state, ownProps) => { + return { + logged: state.loggedIn, + user: getUser(state), + task: getTaskOrdersByFilter(state), + orders: state.orders + } +} + +const mapDispatchToProps = (dispatch, ownProps) => { + return { + updateTask: (task) => dispatch(updateTask(task)), + assignTask: (taskId, assignId) => dispatch(assignTask(taskId, assignId)), + actionAssign: (taskId, assignId, action, message) => dispatch(actionAssign(taskId, assignId, action, message)), + removeAssignment: (id, message) => dispatch(removeAssignment(id, message)), + paymentTask: (taskId, value) => dispatch(paymentTask(taskId, value)), + transferTask: (taskId) => dispatch(transferTask(taskId)), + paymentOrder: (order) => dispatch(payOrder(order)), + createOrder: (order) => dispatch(createOrder(order)), + filterTaskOrders: (filter) => dispatch(filterTaskOrders(filter)), + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(TaskPayment) diff --git a/frontend/src/containers/task-solve.js b/frontend/src/containers/task-solve.js new file mode 100644 index 000000000..be7a35fac --- /dev/null +++ b/frontend/src/containers/task-solve.js @@ -0,0 +1,18 @@ +import { connect } from 'react-redux' +import { getTaskOrdersByFilter } from '../selectors/task' +import TaskSolve from '../components/task/task-solve' + +const mapStateToProps = (state, ownProps) => { + return { + logged: state.loggedIn, + task: getTaskOrdersByFilter(state), + } +} + +const mapDispatchToProps = (dispatch, ownProps) => { + return { + + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(TaskSolve) diff --git a/modules/mail/transfer.js b/modules/mail/transfer.js index 5b9e5beee..59abdc311 100644 --- a/modules/mail/transfer.js +++ b/modules/mail/transfer.js @@ -24,7 +24,7 @@ if (constants.canSendEmail) { [ { type: 'text/html', - value: emailTemplate.mainContentEmailTemplate(` + value: emailTemplate.baseContentEmailTemplate(`

${i18n.__('mail.transfer.new.message.success', { value: value, title: task.title, url: `${process.env.FRONTEND_HOST}/#/task/${task.id}` })}

` ) }, @@ -42,7 +42,7 @@ if (constants.canSendEmail) { [ { type: 'text/html', - value: emailTemplate.mainContentEmailTemplate( + value: emailTemplate.baseContentEmailTemplate( `

${i18n.__('mail.transfer.notify.message.success', { value: value, title: task.title, url: `${process.env.FRONTEND_HOST}/#/task/${task.id}` })}

`) }, ] @@ -59,7 +59,7 @@ if (constants.canSendEmail) { [ { type: 'text/html', - value: emailTemplate.mainContentEmailTemplate(` + value: emailTemplate.baseContentEmailTemplate(`

${i18n.__('mail.transfer.error.message', { value: value, title: task.title, url: `${process.env.FRONTEND_HOST}/#/task/${task.id}` })}

`) }, ] @@ -76,7 +76,7 @@ if (constants.canSendEmail) { [ { type: 'text/html', - value: emailTemplate.mainContentEmailTemplate(`

${i18n.__('mail.transfer.missing.message')}

`) + value: emailTemplate.baseContentEmailTemplate(`

${i18n.__('mail.transfer.missing.message')}

`) }, ] ) @@ -92,7 +92,7 @@ if (constants.canSendEmail) { [ { type: 'text/html', - value: emailTemplate.mainContentEmailTemplate( + value: emailTemplate.baseContentEmailTemplate( `

${i18n.__('mail.transfer.invalid.message')}

` ) }, @@ -110,7 +110,7 @@ if (constants.canSendEmail) { [ { type: 'text/html', - value: emailTemplate.mainContentEmailTemplate(` + value: emailTemplate.baseContentEmailTemplate(`

${i18n.__('mail.transfer.bounty.message', { taskFromTitle: taskFrom.title, taskFromUrl: taskFrom.url,