Created
July 31, 2019 07:28
-
-
Save NSLog0/07ef170a0815eb6624ddbbc8115b9e20 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
import PropTypes from 'prop-types' | |
import { get, mapKeys, snakeCase, omitBy } from 'lodash' | |
import className from 'classnames' | |
import { connect } from 'react-redux' | |
import moment from 'moment' | |
import { | |
getProject, | |
loadProject, | |
clearProject, | |
updateProjectAutoRefillCredit, | |
addNotification, | |
getBudget, | |
loadBudget, | |
updateBudget, | |
getBudgetHistories, | |
loadBudgetHistories, | |
clearBudgetHistories, | |
getDatePicker, | |
updateBudgetProject, | |
loadBudgetReport, | |
} from '@redux-action' | |
import { FormatDateWithTime } from '@helpers' | |
import DateSelector from '@shared/forms/DateSelector' | |
import DateSelectorRange from '@shared/forms/DateSelectorRange' | |
import CentralHeader from '@layouts/CentralHeader' | |
import ProjectMenu from '@shared/ProjectMenu' | |
import CheckBox from '@shared/forms/CheckBox' | |
import Switch from '@shared/forms/Switch' | |
import ButtonSubmit from '@shared/forms/ButtonSubmit' | |
import Notification from '@shared/Notification' | |
import SwitchGroups from '@shared/SwitchGroups' | |
import SectionLoader from '@shared/SectionLoader' | |
import Modal from '@shared/Modal' | |
class ProjectBudget extends React.Component { | |
state = { | |
creditRefill: this.props.creditRefill, | |
topupModal: false, | |
downloadModal: false, | |
autoRefillForm: { | |
autoRefill: false, | |
autoRefillCredit: 0, | |
creditRefill: 0, | |
autoRefillNotify: false, | |
autoRefillEndDate: moment(), | |
}, | |
budgetForm: { | |
status: 'deposit', | |
credit: 0, | |
}, | |
reportForm: { | |
type: 'csv', | |
}, | |
itemTabs: ['Day', 'Week', 'Month', 'Year'], | |
donwloadTabs: ['CSV', 'XLSX', 'JSON'], | |
filter: 'Day', | |
} | |
_getMeta = (startDate, endDate) => { | |
let page = get(this.props, 'budgethistories.meta.page') | |
if (!page || page < 0) { | |
page = null | |
} | |
const params = omitBy( | |
{ | |
project_id: get(this.props, 'match.params.id'), | |
page, | |
start_date: startDate || get(this.props, 'datePicker.startDate'), | |
end_date: endDate || get(this.props, 'datePicker.endDate'), | |
}, | |
(x) => !x | |
) | |
return params | |
} | |
_getFile = (res, type) => { | |
const url = window.URL.createObjectURL(new Blob([res.payload.data])) | |
const link = document.createElement('a') | |
link.href = url | |
link.setAttribute('download', `file.${type}`) | |
document.body.appendChild(link) | |
link.click() | |
link.remove() | |
} | |
componentDidMount = () => { | |
this.props.clearProject() | |
this.props | |
.loadProject({ | |
id: get(this.props, 'match.params.id'), | |
}) | |
.then(() => { | |
this.props | |
.loadBudget({ project_id: get(this.props, 'match.params.id') }) | |
.then((res) => { | |
const budget = get(this.props, 'budget.data', {}) | |
if (get(res, 'type', '').endsWith('_SUCCESS')) { | |
if (!budget.auto_refill_end_date) { | |
budget.auto_refill_end_date = moment() | |
} else { | |
budget.auto_refill_end_date = moment( | |
budget.auto_refill_end_date | |
) | |
} | |
this.setState((prevState) => ({ | |
autoRefillForm: { | |
...prevState.autoRefillForm, | |
autoRefill: budget.auto_refill, | |
autoRefillCredit: budget.auto_refill_credit, | |
autoRefillEndDate: budget.auto_refill_end_date, | |
autoRefillNotify: budget.auto_refill_notify, | |
creditRefill: budget.credit_refill, | |
}, | |
})) | |
} | |
}) | |
}) | |
if (!get(this.props, 'budgetHistories.isLoaded')) { | |
this.props.loadBudgetHistories(this._getMeta()) | |
} | |
} | |
componentDidUpdate = (prevProps, prevState) => { | |
if ( | |
get(prevProps, 'datePicker.startDate') !== | |
get(this.props, 'datePicker.startDate') || | |
get(prevProps, 'datePicker.end') !== get(this.props, 'datePicker.end') | |
) { | |
this.props.clearBudgetHistories() | |
this.props.loadBudgetHistories(this._getMeta()) | |
} | |
if (get(prevState, 'filter') !== get(this.state, 'filter')) { | |
this.onFilterByTime() | |
} | |
} | |
onDownloadReport = (e) => { | |
e.preventDefault() | |
const data = { | |
project_id: get(this.props, 'match.params.id'), | |
start_date: get(this.props, 'datePicker.startDate'), | |
end_date: get(this.props, 'datePicker.endDate'), | |
type: get(this.state, 'reportForm.type', 'csv'), | |
} | |
this.props.loadBudgetReport(data).then((res) => { | |
this._getFile(res, get(this.state, 'reportForm.type', 'csv')) | |
}) | |
} | |
onLoadMore = () => { | |
this.props.loadBudgetHistories({ | |
...this._getMeta(), | |
page: (get(this.props, 'budgetHistories.meta.page') || 1) + 1, | |
}) | |
} | |
onFilterByTime = () => { | |
let startDate = moment() | |
.subtract(1, this.state.filter) | |
.format('YYYY-MM-DD') | |
let endDate = moment().format('YYYY-MM-DD') | |
this.props.clearBudgetHistories() | |
this.props.loadBudgetHistories( | |
omitBy( | |
{ | |
...this._getMeta(startDate, endDate), | |
page: 1, | |
}, | |
(x) => !x | |
) | |
) | |
} | |
onTopupSubmit = (e) => { | |
e.preventDefault() | |
let data = Object.assign({}, this.state.budgetForm) | |
data = { ...data, ...{ project_id: get(this.props, 'match.params.id') } } | |
this.props.updateBudgetProject(data).then((res) => { | |
if (get(res, 'type', '').endsWith('_SUCCESS')) { | |
this.onToggleModal('topupModal') | |
this.props.addNotification({ | |
message: `${this.state.budgetForm.status} success`, | |
type: Notification.NOTIFICATION_TYPES.success, | |
}) | |
this.props.loadProject({ id: get(this.props, 'match.params.id') }) | |
} | |
}) | |
} | |
onToggleModal = (name) => (e) => { | |
this.setState((prevState) => ({ | |
[name]: !prevState[name], | |
})) | |
} | |
onFilterTabChange = (val) => { | |
this.setState({ filter: val }) | |
} | |
onTabReportChange = (val) => { | |
this.setState((prevState) => ({ | |
reportForm: { | |
...prevState.reportForm, | |
type: val.toLowerCase(), | |
}, | |
})) | |
} | |
onToggleAutoRefill = (e) => { | |
this.setState((prevState) => ({ | |
autoRefillForm: { | |
...prevState.autoRefillForm, | |
autoRefill: !prevState.autoRefillForm.autoRefill, | |
}, | |
})) | |
} | |
onAutoRefillChange = (e) => { | |
const type = e.target.type | |
const name = e.target.name | |
const value = type === 'checkbox' ? e.target.checked : e.target.value | |
this.setState((prevState) => ({ | |
autoRefillForm: { | |
...prevState.autoRefillForm, | |
[name]: value, | |
}, | |
})) | |
} | |
onCreditChange = (e) => { | |
const name = e.target.name | |
const value = e.target.value | |
this.setState((prevState) => ({ | |
budgetForm: { | |
...prevState.budgetForm, | |
[name]: value, | |
}, | |
})) | |
} | |
onSubmitRefill = (e) => { | |
e.preventDefault() | |
const { | |
autoRefill, | |
autoRefillCredit, | |
creditRefill, | |
autoRefillNotify, | |
autoRefillEndDate, | |
} = this.state.autoRefillForm | |
let data = { project_id: get(this.props, 'match.params.id') } | |
if (autoRefill) { | |
data = { | |
...data, | |
...{ | |
autoRefill, | |
autoRefillCredit, | |
creditRefill, | |
autoRefillNotify, | |
autoRefillEndDate, | |
}, | |
} | |
} else { | |
data = { ...data, ...{ autoRefill } } | |
} | |
data = mapKeys(data, (value, key) => snakeCase(key)) | |
this.props.updateBudget(data).then((res) => { | |
if (get(res, 'type', '').endsWith('_SUCCESS')) { | |
this.props.addNotification({ | |
message: `Auto refill credit has been ${ | |
autoRefill ? 'enabled' : 'disabled' | |
}.`, | |
type: Notification.NOTIFICATION_TYPES.success, | |
}) | |
} | |
}) | |
} | |
onUpdateAutoRefillCredit = (e) => { | |
e.preventDefault() | |
this.props | |
.updateProjectAutoRefillCredit({ | |
project_id: get(this.props, 'project.data.id'), | |
auto_refill: true, | |
auto_refill_credit: get(this.state, 'form.autoRefillCredit'), | |
credit_refill: get(this.state, 'form.creditRefill'), | |
}) | |
.then(this._autoRefillCallback) | |
} | |
onClickDate = (autoRefillEndDate) => () => { | |
this.setState((prevState) => ({ | |
autoRefillForm: { | |
...prevState.autoRefillForm, | |
autoRefillEndDate, | |
}, | |
})) | |
} | |
render = () => { | |
return ( | |
<React.Fragment> | |
<CentralHeader | |
header={get(this.props, 'project.data.name')} | |
breadcrumbs={[ | |
{ label: 'Projects', to: '/app/projects' }, | |
{ label: get(this.props, 'project.data.name') }, | |
{ label: 'Input' }, | |
]} | |
/> | |
<div className="Project__menu-wrapper"> | |
<ProjectMenu | |
url={window.location.pathname} | |
id={get(this.props, 'project.data.id')} | |
/> | |
<button className="Button Button--green">launch project</button> | |
</div> | |
<div className="Central__content"> | |
<div className="Central__content__header"> | |
<h3 className="Central__content__h3">Project Budget</h3> | |
</div> | |
<div className="Project__detail Project__detail--budget-layout"> | |
<div className="Item"> | |
<div className="Item__wrapper"> | |
<label className="Item__title">Remaining Credit</label> | |
<p className="Item__content"> | |
<strong className="Item__content--bold"> | |
{( | |
get(this.props, 'project.data.credit') || 0 | |
).toLocaleString()} | |
</strong>{' '} | |
credits | |
</p> | |
</div> | |
</div> | |
<div className="Item"> | |
<div className="Item__wrapper"> | |
<label className="Item__title">Remaining Tasks</label> | |
<p className="Item__content"> | |
<strong className="Item__content--bold"> | |
{( | |
(get(this.props, 'project.data.tasks.unprocessed') || 0) + | |
(get(this.props, 'project.data.tasks.processing') || 0) | |
).toLocaleString()} | |
</strong>{' '} | |
tasks | |
</p> | |
</div> | |
</div> | |
<div className="Item"> | |
<div className="Item__wrapper"> | |
<label className="Item__title">Credit Per Task</label> | |
<p className="Item__content"> | |
<strong className="Item__content--bold"> | |
{( | |
get(this.props, 'project.data.credit_rate') || 0 | |
).toLocaleString()} | |
</strong>{' '} | |
credits | |
</p> | |
</div> | |
</div> | |
<div className="Item"> | |
<div className="Item__wrapper"> | |
<label className="Item__title">Auto Refill</label> | |
{get(this.props, 'project.data.auto_refill', false) | |
? `Enabled Enabled Auto Refill ${get( | |
this.props, | |
'project.data.auto_refill_credit', | |
0 | |
)} Credits` | |
: 'Diabled'} | |
</div> | |
</div> | |
</div> | |
<div className="ProjectBudget"> | |
<div className="ProjectBudget__left"> | |
<div className="Central__content__header ProjectBudget__left__header"> | |
<h3 className="Central__content__h3 Project__content-title"> | |
Credits Transaction | |
</h3> | |
<button | |
className="Button Button--gray" | |
onClick={this.onToggleModal('downloadModal')} | |
> | |
Download Report | |
</button> | |
</div> | |
<div className="Central__content__section"> | |
<div className="ProjectBudget__left__filter"> | |
<SwitchGroups | |
items={this.state.itemTabs} | |
onTabChange={this.onFilterTabChange} | |
/> | |
<DateSelectorRange | |
right | |
withIcon | |
/> | |
</div> | |
{(() => { | |
const data = get(this.props, 'budgetHistories.data', []) | |
return ( | |
<SectionLoader | |
isLoading={get(this.props, 'budgetHistories.isLoading')} | |
isLoaded={get(this.props, 'budgetHistories.isLoaded')} | |
isError={get(this.props, 'budgetHistories.isError')} | |
isEmpty={data.length === 0} | |
alwaysDisplayChildren | |
> | |
<div className="Table"> | |
{(() => { | |
const headers = [ | |
'Transaction Date', | |
'Credits', | |
'Amount', | |
'Created by', | |
] | |
return ( | |
<React.Fragment> | |
<div className="Table__tr"> | |
{headers.map((x, i) => ( | |
<div | |
key={i} | |
className="Table__td Table__td--th" | |
> | |
{(() => { | |
const sortBy = get( | |
this.props, | |
'budgetHistories.meta.sort_by' | |
) | |
const sortDirection = get( | |
this.props, | |
'budgetHistorie.meta.sort_direction' | |
) | |
if (x === 'Transaction Date') { | |
return [ | |
<a | |
className={className({ | |
Table__td__caret: | |
sortBy === 'created_at', | |
'Table__td__caret--up': | |
sortBy === 'created_at' && | |
sortDirection === 'desc', | |
})} | |
onClick={() => { | |
this.onToggleSorting(x) | |
}} | |
> | |
{x} | |
</a>, | |
] | |
} else { | |
return x | |
} | |
})()} | |
</div> | |
))} | |
</div> | |
</React.Fragment> | |
) | |
})()} | |
<React.Fragment> | |
{data.map((x, i) => ( | |
<div | |
className="Table__tr" | |
key={i} | |
data-x={x} | |
> | |
<div className="Table__td"> | |
{FormatDateWithTime(get(x, 'created_at'))} | |
</div> | |
<div className="Table__td"> | |
{get(x, 'credit')} | |
</div> | |
<div className="Table__td"> | |
{get(x, 'total_credit')} | |
</div> | |
<div className="Table__td"> | |
{get(x, 'created_by')} | |
</div> | |
</div> | |
))} | |
</React.Fragment> | |
</div> | |
{!get(this.props, 'budgetHistories.isLoading') && | |
get(this.props, 'budgetHistories.isLoaded') && | |
get(this.props, 'budgetHistories.data', []).length < | |
get(this.props, 'budgetHistories.meta.total', 0) && ( | |
<a | |
className="LoadMore" | |
onClick={this.onLoadMore} | |
> | |
Load more | |
</a> | |
)} | |
</SectionLoader> | |
) | |
})()} | |
</div> | |
</div> | |
<div className="ProjectBudget__right"> | |
<div className="Central__content__header"> | |
<h3 className="Central__content__h3 Project__content-title"> | |
Budget Setting | |
</h3> | |
</div> | |
<div className="Central__content__section"> | |
<form className="ProjectBudget__form"> | |
<section> | |
<div> | |
<label className="ProjectBudget__form__header"> | |
Budget | |
</label> | |
</div> | |
<p> | |
I want to credits | |
<select | |
id="status" | |
name="status" | |
onChange={this.onCreditChange} | |
value={this.state.budgetForm.status} | |
> | |
<option value="deposit">Add</option> | |
<option value="withdraw">Withdraw</option> | |
</select> | |
to this project with | |
</p> | |
<input | |
type="text" | |
className="Input" | |
onChange={this.onCreditChange} | |
value={this.state.budgetForm.credit} | |
name="credit" | |
/> | |
</section> | |
<div className="ProjectBudget__form__button"> | |
<button | |
className="Button Button--green" | |
type="button" | |
onClick={this.onToggleModal('topupModal')} | |
> | |
Submit | |
</button> | |
</div> | |
</form> | |
</div> | |
<div className="Central__content__section"> | |
<form | |
className="ProjectBudget__form" | |
onSubmit={this.onSubmitRefill} | |
> | |
<section> | |
<div className="ProjectBudget__form__switch"> | |
<label className="ProjectBudget__form__header"> | |
Auto Refill Credits | |
</label> | |
<Switch | |
meduim | |
checked={this.state.autoRefillForm.autoRefill} | |
onInputChange={this.onToggleAutoRefill} | |
mode={'inline'} | |
/> | |
</div> | |
<p>When credits are less than…</p> | |
<input | |
type="text" | |
className="Input" | |
value={this.state.autoRefillForm.autoRefillCredit} | |
onChange={this.onAutoRefillChange} | |
name="autoRefillCredit" | |
disabled={!this.state.autoRefillForm.autoRefill} | |
/> | |
<p>Then I want to refill…</p> | |
<input | |
type="text" | |
className="Input" | |
value={this.state.autoRefillForm.creditRefill} | |
onChange={this.onAutoRefillChange} | |
name="creditRefill" | |
disabled={!this.state.autoRefillForm.autoRefill} | |
/> | |
<p>End Date:</p> | |
<DateSelector | |
left | |
onClickDate={this.onClickDate} | |
selectedDate={this.state.autoRefillForm.autoRefillEndDate} | |
/> | |
<label className="ProjectBudget__form__check-notify"> | |
<input | |
disabled={!this.state.autoRefillForm.autoRefill} | |
type="checkbox" | |
defaultChecked={ | |
this.state.autoRefillForm.autoRefillNotify | |
} | |
onChange={this.onAutoRefillChange} | |
name="autoRefillNotify" | |
/> | |
<CheckBox /> | |
Notify me when refilled credits | |
</label> | |
</section> | |
<div className="ProjectBudget__form__button"> | |
<ButtonSubmit | |
label="Submit" | |
isLoading={get(this.props, 'project.isLoading')} | |
/> | |
</div> | |
</form> | |
</div> | |
</div> | |
</div> | |
</div> | |
<Modal | |
isActive={this.state.topupModal} | |
toggleFunction={this.onToggleModal('topupModal')} | |
> | |
<form onSubmit={this.onTopupSubmit}> | |
<h3 className="Modal__h3 ProjectBudget__modal__header">{`${this.state.budgetForm.status} Budget`}</h3> | |
<section> | |
<p> | |
Would you like to{' '} | |
{`${this.state.budgetForm.status} | |
${this.state.budgetForm.credit}`}{' '} | |
credits to this project | |
</p> | |
</section> | |
<div className="Modal__button ProjectBudget__modal__button"> | |
<button | |
className="Button Button--gray" | |
onClick={this.onToggleModal('topupModal')} | |
type="button" | |
> | |
Discard | |
</button> | |
<ButtonSubmit | |
label="Confirm" | |
isLoading={get(this.props, 'project.isLoading')} | |
/> | |
</div> | |
</form> | |
</Modal> | |
<Modal | |
isActive={this.state.downloadModal} | |
toggleFunction={this.onToggleModal('downloadModal')} | |
> | |
<form onSubmit={this.onDownloadReport}> | |
<h3 className="Modal__h3 ProjectBudget__modal__header"> | |
Download Report | |
</h3> | |
<section className="ProjectBudget__modal__content-center"> | |
<SwitchGroups | |
center | |
full | |
items={this.state.donwloadTabs} | |
onTabChange={this.onTabReportChange} | |
/> | |
<p className="ProjectBudget__modal__p"> | |
<span>Select Date</span> | |
</p> | |
<DateSelectorRange withIcon /> | |
</section> | |
<div className="Modal__button ProjectBudget__modal__button"> | |
<ButtonSubmit | |
label="Download" | |
isLoading={get(this.props, 'project.isLoading')} | |
/> | |
</div> | |
</form> | |
</Modal> | |
</React.Fragment> | |
) | |
} | |
} | |
const mapStateToProps = (state) => ({ | |
project: getProject(state), | |
budget: getBudget(state), | |
budgetHistories: getBudgetHistories(state), | |
datePicker: getDatePicker(state), | |
}) | |
const mapDispatchToProps = (dispatch) => ({ | |
loadProject: (params) => dispatch(loadProject(params)), | |
clearProject: (params) => dispatch(clearProject(params)), | |
updateProjectAutoRefillCredit: (params) => | |
dispatch(updateProjectAutoRefillCredit(params)), | |
addNotification: (params) => dispatch(addNotification(params)), | |
loadBudget: (params) => dispatch(loadBudget(params)), | |
updateBudget: (params) => dispatch(updateBudget(params)), | |
loadBudgetHistories: (params) => dispatch(loadBudgetHistories(params)), | |
clearBudgetHistories: (params) => dispatch(clearBudgetHistories(params)), | |
updateBudgetProject: (params) => dispatch(updateBudgetProject(params)), | |
loadBudgetReport: (params) => dispatch(loadBudgetReport(params)), | |
}) | |
export default connect( | |
mapStateToProps, | |
mapDispatchToProps | |
)(ProjectBudget) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment