You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
527 lines
17 KiB
527 lines
17 KiB
import { utc, ISO_8601, duration } from "moment"; |
|
import React, { Component, Fragment } from "react"; |
|
import { connect } from "react-redux"; |
|
import { Redirect, Link } from "react-router-dom"; |
|
import { |
|
Button, |
|
Container, |
|
Dropdown, |
|
Header, |
|
Item, |
|
Segment, |
|
Pagination, |
|
Popup, |
|
Loader, |
|
Label, |
|
List, |
|
Message |
|
} from "semantic-ui-react"; |
|
import { |
|
setCShiftPage, |
|
setCShiftApprovalFilter, |
|
setCShiftCompletedFilter, |
|
setCShiftProviderFilter, |
|
setCShiftWorkTypeFilter |
|
} from "../../../actions/cShift/reducer.actions"; |
|
import { |
|
getCShiftsRequest, |
|
deleteCShiftRequest, |
|
editCShiftRequest |
|
} from "../../../actions/cShift/saga.actions"; |
|
import { getEmployeeFromPrice, getPriceFromPrice } from "./ClientShiftShared"; |
|
import ShiftLabel from "../../Shared/ShiftLabel"; |
|
|
|
class ClientShifts extends Component { |
|
constructor(props) { |
|
super(props); |
|
this.state = { |
|
pageSize: 10 // client can't control this, but set here just in case |
|
}; |
|
} |
|
|
|
componentWillMount = () => { |
|
this.props.dispatch( |
|
getCShiftsRequest({ |
|
page: this.props.page, |
|
provider_approved: this.props.approvalFilter, |
|
completed: this.props.completedFilter, |
|
manage: this.props.providerFilter, |
|
work_type: this.props.workTypeFilter, |
|
page_size: this.state.pageSize |
|
}) |
|
); |
|
}; |
|
|
|
handlePaginationChange = (event, { activePage }) => { |
|
this.props.dispatch( |
|
getCShiftsRequest({ |
|
page: activePage, |
|
provider_approved: this.props.approvalFilter, |
|
manage: this.props.providerFilter, |
|
work_type: this.props.workTypeFilter, |
|
page_size: this.state.pageSize |
|
}) |
|
); |
|
this.props.dispatch(setCShiftPage(activePage)); |
|
}; |
|
|
|
handleChangeApprovalFilter = (event, { value }) => { |
|
this.props.dispatch( |
|
getCShiftsRequest({ |
|
page: 1, |
|
provider_approved: value, |
|
completed: this.props.completedFilter, |
|
manage: this.props.providerFilter, |
|
work_type: this.props.workTypeFilter, |
|
page_size: this.state.pageSize |
|
}) |
|
); |
|
this.props.dispatch(setCShiftApprovalFilter(value)); |
|
this.props.dispatch(setCShiftPage(1)); |
|
}; |
|
|
|
handleChangeCompletedFilter = (event, { value }) => { |
|
this.props.dispatch( |
|
getCShiftsRequest({ |
|
page: 1, |
|
provider_approved: this.props.approvalFilter, |
|
completed: value, |
|
manage: this.props.providerFilter, |
|
work_type: this.props.workTypeFilter, |
|
page_size: this.state.pageSize |
|
}) |
|
); |
|
this.props.dispatch(setCShiftCompletedFilter(value)); |
|
this.props.dispatch(setCShiftPage(1)); |
|
}; |
|
|
|
handleChangeProviderFilter = (event, { value }) => { |
|
this.props.dispatch( |
|
getCShiftsRequest({ |
|
page: 1, |
|
provider_approved: this.props.approvalFilter, |
|
completed: this.props.completedFilter, |
|
manage: value, |
|
work_type: this.props.workTypeFilter, |
|
page_size: this.state.pageSize |
|
}) |
|
); |
|
this.props.dispatch(setCShiftProviderFilter(value)); |
|
this.props.dispatch(setCShiftPage(1)); |
|
}; |
|
|
|
handleChangeWorkTypeFilter = (event, { value }) => { |
|
this.props.dispatch( |
|
getCShiftsRequest({ |
|
page: 1, |
|
provider_approved: this.props.approvalFilter, |
|
completed: this.props.completedFilter, |
|
manage: this.props.manageFilter, |
|
work_type: value, |
|
page_size: this.state.pageSize |
|
}) |
|
); |
|
this.props.dispatch(setCShiftWorkTypeFilter(value)); |
|
this.props.dispatch(setCShiftPage(1)); |
|
}; |
|
|
|
deleteCShift = uuid => { |
|
this.props.dispatch(deleteCShiftRequest(uuid)); |
|
}; |
|
|
|
approveHoursCShift = async cshift => { |
|
const { |
|
set_start: approved_start, |
|
set_end: approved_end, |
|
price: get_price_uuid |
|
} = cshift; |
|
this.props.dispatch( |
|
editCShiftRequest({ |
|
uuid: cshift.uuid, |
|
get_price_uuid, |
|
approved_start, |
|
approved_end, |
|
set_start: null, |
|
set_end: null |
|
}) |
|
); |
|
await new Promise(resolve => setTimeout(resolve, 500)); |
|
this.props.dispatch( |
|
getCShiftsRequest({ |
|
page: this.props.page, |
|
provider_approved: this.props.approvalFilter, |
|
completed: this.props.completedFilter, |
|
manage: this.props.providerFilter, |
|
work_type: this.props.workTypeFilter, |
|
page_size: this.state.pageSize |
|
}) |
|
); |
|
}; |
|
|
|
render() { |
|
const { |
|
isSendingCShiftRequest, |
|
cShiftRequestSuccess, |
|
selfUser, |
|
page, |
|
approvalFilter, |
|
completedFilter, |
|
providerFilter, |
|
workTypeFilter |
|
} = this.props; |
|
const { pageSize } = this.state; |
|
if (selfUser.client) { |
|
return ( |
|
<ClientShiftsView |
|
isSendingCShiftRequest={isSendingCShiftRequest} |
|
cShiftRequestSuccess={cShiftRequestSuccess} |
|
page={page} |
|
approvalFilter={approvalFilter} |
|
completedFilter={completedFilter} |
|
providerFilter={providerFilter} |
|
workTypeFilter={workTypeFilter} |
|
pageSize={pageSize} |
|
user={selfUser} |
|
handlePaginationChange={this.handlePaginationChange} |
|
handleChangeApprovalFilter={this.handleChangeApprovalFilter} |
|
handleChangeCompletedFilter={this.handleChangeCompletedFilter} |
|
handleChangeProviderFilter={this.handleChangeProviderFilter} |
|
handleChangeWorkTypeFilter={this.handleChangeWorkTypeFilter} |
|
deleteCShift={this.deleteCShift} |
|
approveHoursCShift={this.approveHoursCShift} |
|
/> |
|
); |
|
} else { |
|
return <Redirect to="/" />; |
|
} |
|
} |
|
} |
|
|
|
function mapStateToProps(state) { |
|
return { ...state.cShift, selfUser: state.user.selfUser }; |
|
} |
|
|
|
const ClientShiftsView = ({ |
|
isSendingCShiftRequest, |
|
cShiftRequestSuccess, |
|
user, |
|
page, |
|
approvalFilter, |
|
completedFilter, |
|
providerFilter, |
|
workTypeFilter, |
|
pageSize, |
|
handlePaginationChange, |
|
handleChangeApprovalFilter, |
|
handleChangeCompletedFilter, |
|
handleChangeProviderFilter, |
|
handleChangeWorkTypeFilter, |
|
deleteCShift, |
|
approveHoursCShift |
|
}) => { |
|
const { count = 0, results = [] } = cShiftRequestSuccess; |
|
const approvedOptions = [ |
|
{ text: "Approved", value: true }, |
|
{ text: "Rejected", value: false }, |
|
{ text: "Pending", value: "" }, |
|
{ text: "All", value: null } |
|
]; |
|
const completedOptions = [ |
|
{ text: "Completed", value: true }, |
|
{ text: "Incomplete", value: false }, |
|
{ text: "All", value: null } |
|
]; |
|
const manageOptions = (user.client.employees || []).map(employee => { |
|
const name = |
|
`${employee.provider.first_name} ${employee.provider.last_name}`.trim() || |
|
"No Name"; |
|
return { |
|
text: `${name} <${employee.provider.email}>`, |
|
value: employee.uuid |
|
}; |
|
}); |
|
manageOptions.push({ text: `All`, value: null }); |
|
const workTypeOptions = (user.client.work_types || []).map(workType => { |
|
return { |
|
text: `${workType.label}`, |
|
value: workType.uuid, |
|
label: ( |
|
<Label |
|
circular |
|
empty |
|
style={{ |
|
backgroundColor: workType.color, |
|
borderColor: workType.color |
|
}} |
|
/> |
|
) |
|
}; |
|
}); |
|
workTypeOptions.push({ text: `All`, value: null }); |
|
const OLD_PEOPLE_TIME_FORMAT = "dddd, MMMM Do YYYY, h:mm a "; |
|
return ( |
|
<Container> |
|
<Header>Shifts</Header> |
|
<Segment.Group horizontal> |
|
<Button |
|
attached="left" |
|
color="green" |
|
size="big" |
|
as={Link} |
|
to="/user/profile/client/add-shift" |
|
> |
|
Schedule Shift |
|
</Button> |
|
<Segment> |
|
{"Filter by Provider Approval "} |
|
<Dropdown |
|
inline |
|
placeholder={approvalFilter === null ? `All` : `Pending`} |
|
options={approvedOptions} |
|
onChange={handleChangeApprovalFilter} |
|
value={approvalFilter} |
|
/> |
|
{" and Completed "} |
|
<Dropdown |
|
inline |
|
placeholder="All" |
|
options={completedOptions} |
|
onChange={handleChangeCompletedFilter} |
|
value={completedFilter} |
|
/> |
|
{" and Provider "} |
|
<Dropdown |
|
inline |
|
placeholder="All" |
|
options={manageOptions} |
|
onChange={handleChangeProviderFilter} |
|
value={providerFilter} |
|
/> |
|
{" and Work Type "} |
|
<Dropdown |
|
inline |
|
placeholder="All" |
|
options={workTypeOptions} |
|
onChange={handleChangeWorkTypeFilter} |
|
value={workTypeFilter} |
|
/> |
|
</Segment> |
|
</Segment.Group> |
|
{!!isSendingCShiftRequest && <Loader content="Loading" active />} |
|
{!isSendingCShiftRequest && |
|
results.length > 0 && ( |
|
<Item.Group divided> |
|
{results.map(result => { |
|
const employee = getEmployeeFromPrice(result.price, user) || {}; |
|
const provider = employee.provider || {}; |
|
const price = getPriceFromPrice(result.price, user) || {}; |
|
const workType = price.work_type; |
|
const checkedIn = |
|
!!result.actual_start && utc(result.actual_start, ISO_8601); |
|
const checkedOut = |
|
!!result.actual_end && utc(result.actual_end, ISO_8601); |
|
const min = duration( |
|
utc(result.set_end, ISO_8601) - utc(result.set_start, ISO_8601), |
|
"milliseconds" |
|
).as("minutes"); |
|
let displayDuration = duration(min, "minutes").humanize(); |
|
if (min % 60) { |
|
displayDuration = duration( |
|
Math.floor(min / 60), |
|
"hours" |
|
).humanize(); |
|
displayDuration += ` and ${duration( |
|
min % 60, |
|
"minutes" |
|
).humanize()}`; |
|
} |
|
|
|
return ( |
|
<Item key={result.uuid} style={{ justifyContent: "space-between" }}> |
|
<Item.Content style={{ minWidth: "14em", paddingRight: "1em" }}> |
|
<Item.Header> |
|
<Label |
|
circular |
|
empty |
|
style={{ |
|
backgroundColor: workType.color, |
|
borderColor: workType.color |
|
}} |
|
/> |
|
{workType.label} |
|
</Item.Header> |
|
<Item.Extra> |
|
<ShiftLabel |
|
provider_approved={result.provider_approved} |
|
client_approved_start={result.approved_start} |
|
client_approved_end={result.approved_end} |
|
/> |
|
</Item.Extra> |
|
<Item.Meta> |
|
{"Scheduled for " + |
|
utc(result.set_start, ISO_8601) |
|
.local(false) |
|
.format(OLD_PEOPLE_TIME_FORMAT)} |
|
</Item.Meta> |
|
<Item.Meta>{displayDuration}</Item.Meta> |
|
<Item.Meta>{`Rate $${price.amount}/hour`}</Item.Meta> |
|
{/* <Item.Description>{result.description}</Item.Description> */} |
|
{/* <code>{JSON.stringify(result, null, 2)}</code> */} |
|
<Item.Description> |
|
{`${provider.first_name} ${provider.last_name}`.trim() || |
|
"No Name!"}{" "} |
|
<a href={"mailto:" + provider.email}>{provider.email}</a> |
|
</Item.Description> |
|
</Item.Content> |
|
{!checkedIn && ( |
|
<Item.Extra> |
|
<Popup |
|
content={ |
|
<div> |
|
Are you sure you want to delete this shift?<br /> |
|
<Button |
|
basic |
|
color="red" |
|
size="small" |
|
onClick={() => deleteCShift(result.uuid)} |
|
> |
|
Confirm Deletion |
|
</Button> |
|
</div> |
|
} |
|
trigger={ |
|
<Button color="red" floated="right"> |
|
Delete |
|
</Button> |
|
} |
|
on="click" |
|
position="top right" |
|
/> |
|
<Button |
|
primary |
|
floated="right" |
|
as={Link} |
|
to={`/user/profile/client/edit-shift/${result.uuid}`} |
|
> |
|
Edit |
|
</Button> |
|
</Item.Extra> |
|
)} |
|
{checkedIn && ( |
|
<Fragment> |
|
<Item.Content style={{ flex: "2 0 auto" }}> |
|
<Item.Description> |
|
{result.description && ( |
|
<Message>{result.description}</Message> |
|
)} |
|
{result.approved_start && ( |
|
<Fragment> |
|
<strong>Client</strong> |
|
<List bulleted> |
|
<List.Item> |
|
{`Approved time start ${utc( |
|
result.approved_start |
|
) |
|
.local(false) |
|
.format(OLD_PEOPLE_TIME_FORMAT)}`} |
|
</List.Item> |
|
<List.Item>{`Approved time end ${utc( |
|
result.approved_end |
|
) |
|
.local(false) |
|
.format(OLD_PEOPLE_TIME_FORMAT)}`}</List.Item> |
|
</List> |
|
</Fragment> |
|
)} |
|
<strong>Provider</strong> |
|
<List bulleted> |
|
<List.Item> |
|
{"Checked in at " + |
|
checkedIn |
|
.local(false) |
|
.format(OLD_PEOPLE_TIME_FORMAT)} |
|
</List.Item> |
|
{checkedOut && ( |
|
<List.Item> |
|
{"Checked out at " + |
|
checkedOut |
|
.local(false) |
|
.format(OLD_PEOPLE_TIME_FORMAT)} |
|
</List.Item> |
|
)} |
|
</List> |
|
{result.chart && ( |
|
<Message> |
|
<Message.Header>Chart</Message.Header> |
|
{result.chart} |
|
</Message> |
|
)} |
|
</Item.Description> |
|
</Item.Content> |
|
<Item.Extra style={{ flex: "1 1 auto" }}> |
|
<Popup |
|
content={ |
|
<div> |
|
Are you sure you want to delete this shift?<br /> |
|
<Button |
|
basic |
|
color="red" |
|
size="small" |
|
onClick={() => deleteCShift(result.uuid)} |
|
> |
|
Confirm Deletion |
|
</Button> |
|
</div> |
|
} |
|
trigger={ |
|
<Button color="red" floated="right"> |
|
Delete |
|
</Button> |
|
} |
|
on="click" |
|
position="top right" |
|
/> |
|
<Button |
|
primary |
|
floated="right" |
|
as={Link} |
|
to={`/user/profile/client/edit-shift/${result.uuid}`} |
|
> |
|
Adjust Hours |
|
</Button> |
|
{!result.approved_start && ( |
|
<Button |
|
color="green" |
|
floated="right" |
|
onClick={() => approveHoursCShift(result)} |
|
> |
|
Approve Hours |
|
</Button> |
|
)} |
|
</Item.Extra> |
|
</Fragment> |
|
)} |
|
</Item> |
|
); |
|
})} |
|
</Item.Group> |
|
)} |
|
<div style={{ textAlign: "center" }}> |
|
<Pagination |
|
activePage={page} |
|
onPageChange={handlePaginationChange} |
|
totalPages={Math.ceil(count / pageSize)} |
|
boundaryRange={1} |
|
siblingRange={1} |
|
size="mini" |
|
firstItem={undefined} |
|
lastItem={undefined} |
|
prevItem={null} |
|
nextItem={null} |
|
/> |
|
</div> |
|
</Container> |
|
); |
|
}; |
|
|
|
export default connect(mapStateToProps)(ClientShifts);
|
|
|