working edit cshift functionality
This commit is contained in:
@@ -1,17 +1,8 @@
|
||||
import { duration, utc } from "moment";
|
||||
import { utc } from "moment";
|
||||
import React, { Component } from "react";
|
||||
import DatePicker from "react-datepicker";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect } from "react-router-dom";
|
||||
import {
|
||||
Container,
|
||||
Dropdown,
|
||||
Form,
|
||||
Header,
|
||||
Label,
|
||||
Message,
|
||||
TextArea
|
||||
} from "semantic-ui-react";
|
||||
import { Header, Label } from "semantic-ui-react";
|
||||
|
||||
import {
|
||||
setFormEmployeeUUID,
|
||||
@@ -23,24 +14,7 @@ import {
|
||||
setFormShiftDates
|
||||
} from "../../../actions/cShift/reducer.actions";
|
||||
import { createMultipleCShiftRequest } from "../../../actions/cShift/saga.actions";
|
||||
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
|
||||
});
|
||||
}
|
||||
import { ClientShiftFormView } from "./ClientShiftFormView";
|
||||
|
||||
class ClientAddShiftForm extends Component {
|
||||
componentWillMount = () => {
|
||||
@@ -177,7 +151,7 @@ class ClientAddShiftForm extends Component {
|
||||
key => shiftDates[key]
|
||||
);
|
||||
return (
|
||||
<ClientAddShiftFormView
|
||||
<ClientShiftFormView
|
||||
isSendingCShiftRequest={isSendingCShiftRequest}
|
||||
cShiftRequestErrors={cShiftRequestErrors}
|
||||
cShiftRequestSuccess={!!cShiftRequestSuccess.length}
|
||||
@@ -206,124 +180,4 @@ function mapStateToProps(state) {
|
||||
return { ...state.cShift, selfUser: state.user.selfUser };
|
||||
}
|
||||
|
||||
const ClientAddShiftFormView = ({
|
||||
isSendingCShiftRequest,
|
||||
cShiftRequestErrors,
|
||||
cShiftRequestSuccess,
|
||||
user,
|
||||
employeeChoices,
|
||||
priceChoices,
|
||||
employeeUUID,
|
||||
priceUUID,
|
||||
startTime,
|
||||
duration,
|
||||
note,
|
||||
selectedShiftDates,
|
||||
changeSelectedEmployee,
|
||||
changeSelectedPrice,
|
||||
changeShiftStartTime,
|
||||
changeShiftDuration,
|
||||
changeShiftNote,
|
||||
handleSelectDate,
|
||||
onSubmitShifts
|
||||
}) => (
|
||||
<Container>
|
||||
<Header>Schedule Shifts</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>Schedule Shift</Form.Button>
|
||||
</Form>
|
||||
</Container>
|
||||
);
|
||||
|
||||
export default connect(mapStateToProps)(ClientAddShiftForm);
|
||||
|
237
src/components/User/Client/ClientEditShiftForm.jsx
Normal file
237
src/components/User/Client/ClientEditShiftForm.jsx
Normal file
@@ -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);
|
159
src/components/User/Client/ClientShiftFormView.jsx
Normal file
159
src/components/User/Client/ClientShiftFormView.jsx
Normal file
@@ -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;
|
13
src/components/User/Client/ClientShiftShared.js
Normal file
13
src/components/User/Client/ClientShiftShared.js
Normal file
@@ -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;
|
||||
};
|
@@ -7,7 +7,8 @@ import {
|
||||
Header,
|
||||
Item,
|
||||
Segment,
|
||||
Pagination
|
||||
Pagination,
|
||||
Loader
|
||||
} from "semantic-ui-react";
|
||||
import { getCShiftsRequest } from "../../../actions/cShift/saga.actions";
|
||||
|
||||
@@ -91,15 +92,29 @@ const ClientShiftsView = ({
|
||||
Schedule a Shift
|
||||
</Button>
|
||||
</Segment>
|
||||
{!!isSendingCShiftRequest && <Loader content="Loading" />}
|
||||
{!isSendingCShiftRequest &&
|
||||
results.length > 0 && (
|
||||
<Item.Group>
|
||||
<Item.Group divided>
|
||||
{results.map(result => (
|
||||
<Item key={result.uuid}>
|
||||
<Item.Content>
|
||||
<Item.Header content={result.uuid} />
|
||||
<code>{JSON.stringify(result, null, 2)}</code>
|
||||
</Item.Content>
|
||||
<Item.Extra>
|
||||
<Button
|
||||
primary
|
||||
floated="right"
|
||||
as={Link}
|
||||
to={`/user/profile/client/edit-shift/${result.uuid}`}
|
||||
>
|
||||
Edit
|
||||
</Button>
|
||||
<Button color="red" floated="right">
|
||||
Delete
|
||||
</Button>
|
||||
</Item.Extra>
|
||||
</Item>
|
||||
))}
|
||||
</Item.Group>
|
||||
|
Reference in New Issue
Block a user