parent
0950f264e8
commit
4f548d529f
13 changed files with 543 additions and 160 deletions
@ -0,0 +1,237 @@ |
||||
import { utc, duration, ISO_8601 } from "moment"; |
||||
import React, { Component } from "react"; |
||||
import { connect } from "react-redux"; |
||||
import { Redirect } from "react-router-dom"; |
||||
import { Header, Label, Loader } from "semantic-ui-react"; |
||||
|
||||
import { |
||||
setFormEmployeeUUID, |
||||
setFormPriceUUID, |
||||
setFormShiftStartTime, |
||||
setFormShiftDuration, |
||||
setFormShiftNote, |
||||
setClearCShiftState, |
||||
setFormShiftDates, |
||||
setCShiftUUID |
||||
} from "../../../actions/cShift/reducer.actions"; |
||||
import { |
||||
getCShiftRequest, |
||||
editCShiftRequest |
||||
} from "../../../actions/cShift/saga.actions"; |
||||
import { ClientShiftFormView } from "./ClientShiftFormView"; |
||||
import { getEmployeeFromPrice } from "./ClientShiftShared"; |
||||
|
||||
class ClientEditShiftForm extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
|
||||
const { cShiftRequestSuccess, selfUser } = this.props; |
||||
if (selfUser.client && cShiftRequestSuccess.uuid) { |
||||
const employee = getEmployeeFromPrice( |
||||
cShiftRequestSuccess.price, |
||||
selfUser |
||||
); |
||||
const startTime = utc(cShiftRequestSuccess.set_start, ISO_8601).local(); |
||||
const endTime = utc(cShiftRequestSuccess.set_end, ISO_8601).local(); |
||||
this.props.dispatch(setCShiftUUID(cShiftRequestSuccess.uuid)); |
||||
this.props.dispatch(setFormShiftDates([startTime])); |
||||
this.props.dispatch(setFormShiftStartTime(startTime)); |
||||
this.props.dispatch( |
||||
setFormShiftDuration( |
||||
duration(endTime - startTime, "milliseconds").as("minutes") |
||||
) |
||||
); |
||||
this.props.dispatch(setFormEmployeeUUID(employee.uuid)); |
||||
this.props.dispatch(setFormShiftNote(cShiftRequestSuccess.description)); |
||||
this.props.dispatch(setFormPriceUUID(cShiftRequestSuccess.price)); |
||||
} |
||||
} |
||||
|
||||
changeSelectedEmployee = (e, { value }) => { |
||||
if (value !== this.props.employeeUUID) { |
||||
this.props.dispatch(setFormPriceUUID("")); |
||||
} |
||||
this.props.dispatch(setFormEmployeeUUID(value)); |
||||
}; |
||||
|
||||
changeSelectedPrice = (e, { value }) => { |
||||
this.props.dispatch(setFormPriceUUID(value)); |
||||
}; |
||||
|
||||
/** |
||||
* change handler for shift start time selector. |
||||
* @param momentTime - instance of moment (but we only care about the time) |
||||
*/ |
||||
changeShiftStartTime = momentTime => { |
||||
this.props.dispatch(setFormShiftStartTime(momentTime)); |
||||
}; |
||||
|
||||
changeShiftDuration = (e, { value }) => { |
||||
this.props.dispatch(setFormShiftDuration(value)); |
||||
}; |
||||
|
||||
changeShiftNote = event => { |
||||
this.props.dispatch(setFormShiftNote(event.target.value)); |
||||
}; |
||||
|
||||
/** |
||||
* change handler for shift date selector |
||||
* @param momentDate - instance of moment (but we only care about the day) |
||||
*/ |
||||
handleSelectDate = momentDate => { |
||||
this.props.dispatch(setFormShiftDates([momentDate])); |
||||
}; |
||||
|
||||
onSubmitShifts = event => { |
||||
event.preventDefault(); |
||||
// change this into interable cshift post request bodies |
||||
const { |
||||
cShiftUUID, |
||||
priceUUID, |
||||
startTime, |
||||
duration, |
||||
note, |
||||
shiftDates |
||||
} = this.props; |
||||
const postRequestBodies = []; |
||||
for (let shiftDateString in shiftDates) { |
||||
const dynamicStartTime = utc(startTime); |
||||
const startDate = shiftDates[shiftDateString]; |
||||
dynamicStartTime.set({ |
||||
year: startDate.get("year"), |
||||
month: startDate.get("month"), |
||||
date: startDate.get("date") |
||||
}); |
||||
const dynamicEndTime = utc(dynamicStartTime); |
||||
dynamicEndTime.add(duration, "minutes"); |
||||
postRequestBodies.push({ |
||||
get_price_uuid: priceUUID, |
||||
set_start: dynamicStartTime.format(), |
||||
set_end: dynamicEndTime.format(), |
||||
description: note ? note : undefined |
||||
}); |
||||
} |
||||
console.log({ ...postRequestBodies[0], uuid: cShiftUUID }) |
||||
this.props.dispatch( |
||||
editCShiftRequest({ ...postRequestBodies[0], uuid: cShiftUUID }) |
||||
); |
||||
}; |
||||
|
||||
render() { |
||||
const { |
||||
isSendingCShiftRequest, |
||||
cShiftRequestErrors, |
||||
cShiftRequestSuccess, |
||||
selfUser, |
||||
employeeUUID, |
||||
priceUUID, |
||||
startTime, |
||||
duration, |
||||
note, |
||||
shiftDates, |
||||
cShiftUUID |
||||
} = this.props; |
||||
|
||||
if (!selfUser.client) { |
||||
return <Redirect to="/" />; |
||||
} |
||||
if (cShiftUUID === true) { |
||||
return <Redirect to="/user/profile/client/shifts" />; |
||||
} |
||||
|
||||
const employeeChoices = selfUser.client.employees |
||||
// TODO: ugly edit of state changed employee |
||||
.filter(employee => !employee.deleted && !!employee.approved) |
||||
.map(({ uuid, provider }) => ({ |
||||
key: uuid, |
||||
value: uuid, |
||||
text: provider.email |
||||
})); |
||||
|
||||
const priceChoices = []; |
||||
if (employeeUUID) { |
||||
const employee = selfUser.client.employees.find(emp => { |
||||
return emp && emp.uuid === employeeUUID; |
||||
}); |
||||
priceChoices.push( |
||||
...employee.prices |
||||
.filter(price => !price.deleted) |
||||
.map(({ amount, uuid, work_type }) => ({ |
||||
key: uuid, |
||||
value: uuid, |
||||
text: `${work_type.label} ($${amount}/hr)`, |
||||
content: ( |
||||
<Header> |
||||
<Label |
||||
circular |
||||
empty |
||||
style={{ |
||||
backgroundColor: work_type.color, |
||||
borderColor: work_type.color |
||||
}} |
||||
/> |
||||
{work_type.label} |
||||
<Header.Subheader |
||||
style={{ paddingLeft: "2em" }} |
||||
content={`Hourly Rate: ${amount}`} |
||||
/> |
||||
</Header> |
||||
) |
||||
})) |
||||
); |
||||
} |
||||
|
||||
const selectedShiftDates = Object.keys(shiftDates).map( |
||||
key => shiftDates[key] |
||||
); |
||||
return ( |
||||
<ClientShiftFormView |
||||
isSendingCShiftRequest={isSendingCShiftRequest} |
||||
cShiftRequestErrors={cShiftRequestErrors} |
||||
cShiftRequestSuccess={!!cShiftRequestSuccess.length} |
||||
user={selfUser} |
||||
employeeChoices={employeeChoices} |
||||
priceChoices={priceChoices} |
||||
employeeUUID={employeeUUID} |
||||
priceUUID={priceUUID} |
||||
startTime={startTime} |
||||
duration={duration} |
||||
note={note} |
||||
selectedShiftDates={selectedShiftDates} |
||||
changeSelectedEmployee={this.changeSelectedEmployee} |
||||
changeSelectedPrice={this.changeSelectedPrice} |
||||
changeShiftStartTime={this.changeShiftStartTime} |
||||
changeShiftDuration={this.changeShiftDuration} |
||||
changeShiftNote={this.changeShiftNote} |
||||
handleSelectDate={this.handleSelectDate} |
||||
onSubmitShifts={this.onSubmitShifts} |
||||
isEditing={true} |
||||
/> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { ...state.cShift, selfUser: state.user.selfUser }; |
||||
} |
||||
|
||||
class EditClientShiftWrapper extends Component { |
||||
constructor(props) { |
||||
super(props); |
||||
this.props.dispatch(setClearCShiftState()); |
||||
this.props.dispatch( |
||||
getCShiftRequest({ uuid: this.props.match.params.shiftUUID }) |
||||
); |
||||
} |
||||
|
||||
render() { |
||||
const { cShiftRequestSuccess } = this.props; |
||||
if (cShiftRequestSuccess.uuid) { |
||||
return <ClientEditShiftForm {...this.props} />; |
||||
} else { |
||||
return <Loader />; |
||||
} |
||||
} |
||||
} |
||||
|
||||
export default connect(mapStateToProps)(EditClientShiftWrapper); |
@ -0,0 +1,159 @@ |
||||
import { utc, duration } from "moment"; |
||||
import React from "react"; |
||||
import DatePicker from "react-datepicker"; |
||||
import { Redirect } from "react-router-dom"; |
||||
import { |
||||
Container, |
||||
Dropdown, |
||||
Form, |
||||
Header, |
||||
Message, |
||||
TextArea |
||||
} from "semantic-ui-react"; |
||||
|
||||
import Error from "../../Shared/Error"; |
||||
import "react-datepicker/dist/react-datepicker.css"; |
||||
import "./shiftStartTimeOverrides.css"; |
||||
|
||||
const CShiftDurationOptions = []; |
||||
for (let min = 60; min <= 480; min += 30) { |
||||
let displayText = duration(min, "minutes").humanize(); |
||||
if (min % 60) { |
||||
displayText = duration(Math.floor(min / 60), "hours").humanize(); |
||||
displayText += ` and ${duration(min % 60, "minutes").humanize()}`; |
||||
} |
||||
CShiftDurationOptions.push({ |
||||
key: min, |
||||
value: min, |
||||
text: displayText |
||||
}); |
||||
} |
||||
|
||||
export const ClientShiftFormView = ({ |
||||
isSendingCShiftRequest, |
||||
cShiftRequestErrors, |
||||
cShiftRequestSuccess, |
||||
user, |
||||
employeeChoices, |
||||
priceChoices, |
||||
employeeUUID, |
||||
priceUUID, |
||||
startTime, |
||||
duration, |
||||
note, |
||||
selectedShiftDates, |
||||
changeSelectedEmployee, |
||||
changeSelectedPrice, |
||||
changeShiftStartTime, |
||||
changeShiftDuration, |
||||
changeShiftNote, |
||||
handleSelectDate, |
||||
onSubmitShifts, |
||||
isEditing = false |
||||
}) => ( |
||||
<Container> |
||||
<Header> |
||||
{!isEditing && "Schedule Shifts"} |
||||
{isEditing && "Edit Shift"} |
||||
</Header> |
||||
<Form |
||||
loading={isSendingCShiftRequest} |
||||
onSubmit={onSubmitShifts} |
||||
error={!!cShiftRequestErrors.length} |
||||
success={!!cShiftRequestSuccess} |
||||
> |
||||
<Form.Group widths="equal"> |
||||
<Form.Field> |
||||
<label>Employee</label> |
||||
<Dropdown |
||||
onChange={changeSelectedEmployee} |
||||
options={employeeChoices} |
||||
placeholder="Select employee" |
||||
selection |
||||
fluid |
||||
search |
||||
noResultsMessage="No approved employees found." |
||||
value={employeeUUID} |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field> |
||||
<label>Price</label> |
||||
<Dropdown |
||||
onChange={changeSelectedPrice} |
||||
options={priceChoices} |
||||
placeholder="Select price" |
||||
selection |
||||
fluid |
||||
search |
||||
disabled={!employeeUUID} |
||||
noResultsMessage="No prices for given employee." |
||||
value={priceUUID} |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field> |
||||
<label>Shift Start Time</label> |
||||
<DatePicker |
||||
selected={startTime} |
||||
onChange={changeShiftStartTime} |
||||
showTimeSelect |
||||
showTimeSelectOnly |
||||
timeIntervals={30} |
||||
dateFormat="LT Z" |
||||
timeFormat="hh:mm" |
||||
placeholderText="Select shift start time" |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field> |
||||
<label>Shift Duration</label> |
||||
<Dropdown |
||||
onChange={changeShiftDuration} |
||||
options={CShiftDurationOptions} |
||||
placeholder="Select duration" |
||||
selection |
||||
fluid |
||||
value={duration} |
||||
/> |
||||
</Form.Field> |
||||
</Form.Group> |
||||
<Form.Field> |
||||
<label>Note</label> |
||||
<TextArea |
||||
placeholder="Employee notes" |
||||
value={note} |
||||
onChange={changeShiftNote} |
||||
/> |
||||
</Form.Field> |
||||
<Form.Field style={{ textAlign: "center" }}> |
||||
<label>Dates</label> |
||||
<DatePicker |
||||
inline |
||||
onSelect={handleSelectDate} |
||||
highlightDates={selectedShiftDates} |
||||
monthsShown={2} |
||||
// https://github.com/Hacker0x01/react-datepicker/pull/1360 |
||||
peekNextMonth={false} // this is broken? Fixed in PR 1360 |
||||
minDate={utc(new Date()).add(1, "day")} |
||||
maxDate={utc(new Date()) |
||||
.add(1, "month") |
||||
.endOf("month")} |
||||
/> |
||||
</Form.Field> |
||||
{!!cShiftRequestErrors.length && ( |
||||
<Error header="" error={cShiftRequestErrors[0]} /> |
||||
)} |
||||
<Message success> |
||||
<Message.Header>Add Shift successful!</Message.Header> |
||||
<p>Shifts successfully scheduled.</p> |
||||
{!!cShiftRequestSuccess && ( |
||||
<Redirect to="/user/profile/client/shifts" /> |
||||
)} |
||||
</Message> |
||||
<Form.Button> |
||||
{!isEditing && "Schedule Shifts"} |
||||
{isEditing && "Edit Shift"} |
||||
</Form.Button> |
||||
</Form> |
||||
</Container> |
||||
); |
||||
|
||||
export default ClientShiftFormView; |
@ -0,0 +1,13 @@ |
||||
export const getEmployeeFromPrice = (priceUUID, selfUser) => { |
||||
const employees = selfUser && selfUser.client && selfUser.client.employees; |
||||
let matchEmployee = null; |
||||
employees.forEach(employee => { |
||||
const priceMatch = employee.prices.filter(price => { |
||||
return price.uuid === priceUUID; |
||||
}); |
||||
if (priceMatch.length > 0) { |
||||
matchEmployee = employee; |
||||
} |
||||
}); |
||||
return matchEmployee; |
||||
}; |
Loading…
Reference in new issue