working details view, approve/reject

This commit is contained in:
Alexander Wong 2018-04-23 11:07:33 -06:00
parent 0fc6ac31f5
commit 805f20e040
No known key found for this signature in database
GPG Key ID: E90E5D6448C2C663
10 changed files with 389 additions and 47 deletions

View File

@ -1,4 +1,8 @@
import { GET_PSHIFTS_REQUEST } from "../../constants/pShift.constants";
import {
GET_PSHIFTS_REQUEST,
GET_PSHIFT_REQUEST,
UPDATE_PSHIFT_REQUEST
} from "../../constants/pShift.constants";
export function getPShiftsRequest(params) {
return {
@ -6,3 +10,17 @@ export function getPShiftsRequest(params) {
data: params
};
}
export function getPShiftRequest(payload) {
return {
type: GET_PSHIFT_REQUEST,
data: payload
}
}
export function updatePShiftRequest(payload) {
return {
type: UPDATE_PSHIFT_REQUEST,
data: payload
};
}

View File

@ -1,5 +1,13 @@
import { get } from "./baseApi";
import { get, put } from "./baseApi";
export function getPShifts(params) {
return get("/pshift/", params).then(resp => Promise.resolve(resp));
}
export function getPShift(uuid) {
return get(`/pshift/${uuid}/`).then(resp => Promise.resolve(resp));
}
export function updatePShift(uuid, params) {
return put(`/pshift/${uuid}/`, params).then(resp => Promise.resolve(resp));
}

View File

@ -18,6 +18,7 @@ import ClientShifts from "./User/Client/ClientShifts";
import ClientAddShiftForm from "./User/Client/ClientAddShiftForm";
import ClientEditShiftForm from "./User/Client/ClientEditShiftForm";
import ProviderClients from "./User/Provider/ProviderClients";
import ProviderShift from "./User/Provider/ProviderShift";
import ProviderShifts from "./User/Provider/ProviderShifts";
import CompleteRegistration from "./User/CompleteRegistration";
import EditProfile from "./User/EditProfile";
@ -102,6 +103,10 @@ class App extends Component {
path="/user/profile/provider/clients"
component={ProviderClients}
/>
<PrivateRoute
path="/user/profile/provider/shifts/:shiftUUID"
component={ProviderShift}
/>
<PrivateRoute
path="/user/profile/provider/shifts"
component={ProviderShifts}

View File

@ -20,6 +20,7 @@ import {
} from "../../../actions/cShift/saga.actions";
import { ClientShiftFormView } from "./ClientShiftFormView";
import { getEmployeeFromPrice } from "./ClientShiftShared";
import Error from "../../Shared/Error";
class ClientEditShiftForm extends Component {
constructor(props) {
@ -224,11 +225,13 @@ class EditClientShiftWrapper extends Component {
}
render() {
const { cShiftRequestSuccess } = this.props;
const { cShiftRequestSuccess, cShiftRequestErrors } = this.props;
if (cShiftRequestSuccess.uuid) {
return <ClientEditShiftForm {...this.props} />;
} else if (!cShiftRequestErrors.length > 0) {
return <Loader active />;
} else {
return <Loader />;
return <Error error={cShiftRequestErrors[0]} />;
}
}
}

View File

@ -105,29 +105,33 @@ const ClientShiftsView = ({
Schedule a Shift
</Button>
</Segment>
{!!isSendingCShiftRequest && <Loader content="Loading" />}
{!!isSendingCShiftRequest && <Loader content="Loading" active />}
{!isSendingCShiftRequest &&
results.length > 0 && (
<Item.Group divided>
{results.map(result => {
const employee = getEmployeeFromPrice(result.price, user);
const price = getPriceFromPrice(result.price, user);
const employee = getEmployeeFromPrice(result.price, user) || {};
const provider = employee.provider || {};
const price = getPriceFromPrice(result.price, user) || {};
const workType = price.work_type;
const min = duration(
utc(result.set_end, ISO_8601) - utc(result.set_start, ISO_8601),
"milliseconds"
).as("minutes");
let displayText = duration(min, "minutes").humanize();
let displayDuration = duration(min, "minutes").humanize();
if (min % 60) {
displayText = duration(
displayDuration = duration(
Math.floor(min / 60),
"hours"
).humanize();
displayText += ` and ${duration(
displayDuration += ` and ${duration(
min % 60,
"minutes"
).humanize()}`;
}
const approved = !!result.approved;
const rejected = !result.approved && result.approved !== null;
const pending = result.approved === null;
return (
<Item key={result.uuid}>
<Item.Content>
@ -142,24 +146,32 @@ const ClientShiftsView = ({
/>
{workType.label}
</Item.Header>
<Item.Extra>
<Label
color={approved ? "green" : rejected ? "red" : "grey"}
tag
size="small"
>
{approved && "Approved"}
{pending && "Approval Pending"}
{rejected && "Rejected"}
</Label>
</Item.Extra>
<Item.Meta>
{"At " +
utc(result.set_start, ISO_8601)
.local(false)
.format("dddd, MMMM Do YYYY, h:mm a Z") +
"; for " +
displayText +
"; rate $" +
price.amount +
"/hour"}
.format("dddd, MMMM Do YYYY, h:mm a Z")}
</Item.Meta>
<Item.Description>{result.description}</Item.Description>
<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.Meta>
<a href={"mailto:" + employee.provider.email}>
{employee.provider.email}
</a>
</Item.Meta>
<Item.Description>
{`${provider.first_name} ${provider.last_name}`.trim() ||
"No Name!"}{" "}
<a href={"mailto:" + provider.email}>{provider.email}</a>
</Item.Description>
</Item.Content>
{result.actual_start === null && (
<Item.Extra>

View File

@ -0,0 +1,174 @@
import { utc, duration, ISO_8601 } from "moment";
import React, { Component } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import {
Button,
Container,
Header,
Label,
List,
Loader,
Message
} from "semantic-ui-react";
import { setClearPShiftState } from "../../../actions/pShift/reducer.actions";
import {
getPShiftRequest,
updatePShiftRequest
} from "../../../actions/pShift/saga.actions";
import Error from "../../Shared/Error";
import {
getEmployerFromPrice,
getWorkTypeFromPrice,
getPriceFromPrice
} from "./ProviderShiftsShared";
class ProviderShift extends Component {
handleChangePShiftApproval = (uuid, approved) => {
return () => {
this.props.dispatch(
updatePShiftRequest({
uuid,
approved,
action: "",
chart: null,
single: true
})
);
};
};
render() {
const {
isSendingPShiftRequest,
pShiftRequestError,
pShiftRequestSuccess,
selfUser
} = this.props;
if (!selfUser.provider) {
return <Redirect to="/" />;
}
return (
<ProviderShiftFormView
isSendingPShiftRequest={isSendingPShiftRequest}
pShiftRequestError={pShiftRequestError}
shift={pShiftRequestSuccess}
user={selfUser}
handleChangePShiftApproval={this.handleChangePShiftApproval}
/>
);
}
}
const ProviderShiftFormView = ({
isSendingPShiftRequest,
pShiftRequestError,
shift,
user,
handleChangePShiftApproval
}) => {
const employer = getEmployerFromPrice(shift.price, user) || {};
const client = employer.client || {};
const workType = getWorkTypeFromPrice(shift.price, user) || {};
const price = getPriceFromPrice(shift.price, user) || {};
const min = duration(
utc(shift.set_end, ISO_8601) - utc(shift.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()}`;
}
const approved = !!shift.approved;
const rejected = !shift.approved && shift.approved !== null;
const pending = shift.approved === null;
return (
<Container>
<Header>Shift Details</Header>
<Loader active={isSendingPShiftRequest} />
<Label color={approved ? "green" : rejected ? "red" : "grey"} tag size="small">
{approved && "Approved"}
{pending && "Approval Pending"}
{rejected && "Rejected"}
</Label>
<Label
circular
empty
style={{
backgroundColor: workType.color,
borderColor: workType.color
}}
/>{" "}
{workType.label}
<List bulleted>
<List.Item>
{"Scheduled at " +
utc(shift.set_start, ISO_8601)
.local(false)
.format("dddd, MMMM Do YYYY, h:mm a Z")}
</List.Item>
<List.Item>{displayDuration}</List.Item>
<List.Item>{`Rate $${price.amount}/hour`}</List.Item>
<List.Item>
<strong>Employer/Client:</strong>{" "}
{`${client.first_name} ${client.last_name}`.trim() || "No Name!"}{" "}
<a href={"mailto:" + client.email}>{client.email}</a>
</List.Item>
</List>
{shift.description && (
<Message>
<Message.Header>Description</Message.Header>
<p>{shift.description}</p>
</Message>
)}
<Button.Group>
{!approved && (
<Button
color="green"
onClick={handleChangePShiftApproval(shift.uuid, true)}
>
Approve
</Button>
)}
{!rejected && (
<Button
color="red"
onClick={handleChangePShiftApproval(shift.uuid, false)}
>
Reject
</Button>
)}
</Button.Group>
<Error error={pShiftRequestError}/>
</Container>
);
};
function mapStateToProps(state) {
return { ...state.pShift, selfUser: state.user.selfUser };
}
class ProviderShiftWrapper extends Component {
constructor(props) {
super(props);
this.props.dispatch(setClearPShiftState());
this.props.dispatch(
getPShiftRequest({ uuid: this.props.match.params.shiftUUID })
);
}
render() {
const { pShiftRequestSuccess, pShiftRequestError } = this.props;
if (pShiftRequestSuccess.uuid) {
return <ProviderShift {...this.props} />;
} else if (!pShiftRequestError) {
return <Loader active />;
} else {
return <Error error={pShiftRequestError} />;
}
}
}
export default connect(mapStateToProps)(ProviderShiftWrapper);

View File

@ -1,8 +1,9 @@
import { utc, ISO_8601, duration } from "moment";
import React, { Component } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import { Redirect, Link } from "react-router-dom";
import {
Button,
Container,
Header,
Item,
@ -14,7 +15,10 @@ import {
getEmployerFromPrice,
getPriceFromPrice
} from "./ProviderShiftsShared";
import { getPShiftsRequest } from "../../../actions/pShift/saga.actions";
import {
getPShiftsRequest,
updatePShiftRequest
} from "../../../actions/pShift/saga.actions";
class ProviderShifts extends Component {
constructor(props) {
@ -44,6 +48,14 @@ class ProviderShifts extends Component {
this.setState({ page: activePage });
};
handleChangePShiftApproval = (uuid, approved) => {
return () => {
this.props.dispatch(
updatePShiftRequest({ uuid, approved, action: "", chart: null })
);
};
};
render() {
const {
isSendingPShiftRequest,
@ -60,6 +72,7 @@ class ProviderShifts extends Component {
pageSize={pageSize}
user={selfUser}
handlePaginationChange={this.handlePaginationChange}
handleChangePShiftApproval={this.handleChangePShiftApproval}
/>
);
} else {
@ -78,35 +91,40 @@ const ProviderShiftsView = ({
user,
page,
pageSize,
handlePaginationChange
handlePaginationChange,
handleChangePShiftApproval
}) => {
const { count = 0, results = [] } = pShiftRequestSuccess;
return (
<Container>
<Header>Shifts</Header>
{!!isSendingPShiftRequest && <Loader content="Loading" />}
{!!isSendingPShiftRequest && <Loader content="Loading" active />}
{!isSendingPShiftRequest &&
results.length > 0 && (
<Item.Group divided>
{results.map(result => {
const employer = getEmployerFromPrice(result.price, user);
const price = getPriceFromPrice(result.price, user);
const workType = price.work_type;
const employer = getEmployerFromPrice(result.price, user) || {};
const client = employer.client || {};
const price = getPriceFromPrice(result.price, user) || {};
const workType = price.work_type || {};
const min = duration(
utc(result.set_end, ISO_8601) - utc(result.set_start, ISO_8601),
"milliseconds"
).as("minutes");
let displayText = duration(min, "minutes").humanize();
let displayDuration = duration(min, "minutes").humanize();
if (min % 60) {
displayText = duration(
displayDuration = duration(
Math.floor(min / 60),
"hours"
).humanize();
displayText += ` and ${duration(
displayDuration += ` and ${duration(
min % 60,
"minutes"
).humanize()}`;
}
const approved = !!result.approved;
const rejected = !result.approved && result.approved !== null;
const pending = result.approved === null;
return (
<Item key={result.uuid}>
<Item.Content>
@ -121,24 +139,65 @@ const ProviderShiftsView = ({
/>
{workType.label}
</Item.Header>
<Item.Extra>
<Label
color={approved ? "green" : rejected ? "red" : "grey"}
tag
size="small"
>
{approved && "Approved"}
{pending && "Approval Pending"}
{rejected && "Rejected"}
</Label>
</Item.Extra>
<Item.Meta>
{"At " +
utc(result.set_start, ISO_8601)
.local(false)
.format("dddd, MMMM Do YYYY, h:mm a Z") +
"; for " +
displayText +
"; rate $" +
price.amount +
"/hour"}
.format("dddd, MMMM Do YYYY, h:mm a Z")}
</Item.Meta>
<Item.Description>{result.description}</Item.Description>
<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.Meta>
<a href={"mailto:" + employer.client.email}>
{employer.client.email}
</a>
</Item.Meta>
<Item.Description>
{`${client.first_name} ${client.last_name}`.trim() ||
"No Name!"}{" "}
<a href={"mailto:" + client.email}>{client.email}</a>
</Item.Description>
<Item.Extra>
<Button.Group floated="right">
{!approved && (
<Button
color="green"
onClick={handleChangePShiftApproval(
result.uuid,
true
)}
>
Approve
</Button>
)}
{!rejected && (
<Button
color="red"
onClick={handleChangePShiftApproval(
result.uuid,
false
)}
>
Reject
</Button>
)}
</Button.Group>
<Button
floated="right"
as={Link}
to={`/user/profile/provider/shifts/${result.uuid}`}
>
Details &amp; Check In
</Button>
</Item.Extra>
</Item.Content>
</Item>
);

View File

@ -8,3 +8,5 @@ export const SET_CLEAR_PSHIFT_STATE = "SET_CLEAR_PSHIFT_STATE";
// Saga PShift Action Constants
export const GET_PSHIFTS_REQUEST = "GET_PSHIFTS_REQUEST";
export const GET_PSHIFT_REQUEST = "GET_PSHIFT_REQUEST";
export const UPDATE_PSHIFT_REQUEST = "UPDATE_PSHIFT_REQUEST";

View File

@ -83,8 +83,16 @@ import {
editCShiftFlow,
deleteCShiftFlow
} from "./cShift.sagas";
import { GET_PSHIFTS_REQUEST } from "../constants/pShift.constants";
import { getPShiftsFlow } from "./pShift.sagas";
import {
GET_PSHIFTS_REQUEST,
GET_PSHIFT_REQUEST,
UPDATE_PSHIFT_REQUEST
} from "../constants/pShift.constants";
import {
getPShiftsFlow,
getPShiftFlow,
updatePShiftFlow
} from "./pShift.sagas";
export default function* rootSaga() {
yield takeLatest(SEND_REGISTER_REQUEST, registerUserFlow);
@ -118,4 +126,6 @@ export default function* rootSaga() {
yield takeLatest(EDIT_CSHIFT_REQUEST, editCShiftFlow);
yield takeLatest(DELETE_CSHIFT_REQUEST, deleteCShiftFlow);
yield takeLatest(GET_PSHIFTS_REQUEST, getPShiftsFlow);
yield takeLatest(GET_PSHIFT_REQUEST, getPShiftFlow);
yield takeLatest(UPDATE_PSHIFT_REQUEST, updatePShiftFlow);
}

View File

@ -6,7 +6,11 @@ import {
clearPShiftRequestError,
clearPShiftRequestSuccess
} from "../actions/pShift/reducer.actions";
import { getPShifts } from "../api/pShift.api";
import { getPShifts, getPShift, updatePShift } from "../api/pShift.api";
import {
getPShiftsRequest,
getPShiftRequest
} from "../actions/pShift/saga.actions";
function* getPShiftsCall(params) {
yield effects.put(isSendingPShiftRequest(true));
@ -20,6 +24,30 @@ function* getPShiftsCall(params) {
}
}
function* getPShiftCall(payload) {
yield effects.put(isSendingPShiftRequest(true));
try {
return yield effects.call(getPShift, payload.uuid);
} catch (exception) {
yield effects.put(setPShiftRequestError(exception));
return false;
} finally {
yield effects.put(isSendingPShiftRequest(false));
}
}
function* updatePShiftCall(payload) {
yield effects.put(isSendingPShiftRequest(true));
try {
return yield effects.call(updatePShift, payload.uuid, payload);
} catch (exception) {
yield effects.put(setPShiftRequestError(exception));
return false;
} finally {
yield effects.put(isSendingPShiftRequest(false));
}
}
export function* getPShiftsFlow(request) {
yield effects.put(clearPShiftRequestSuccess());
yield effects.put(clearPShiftRequestError());
@ -28,3 +56,26 @@ export function* getPShiftsFlow(request) {
yield effects.put(setPShiftRequestSuccess(wasSuccessful));
}
}
export function* getPShiftFlow(request) {
yield effects.put(clearPShiftRequestSuccess());
yield effects.put(clearPShiftRequestError());
const wasSuccessful = yield effects.call(getPShiftCall, request.data);
if (wasSuccessful) {
yield effects.put(setPShiftRequestSuccess(wasSuccessful));
}
}
export function* updatePShiftFlow(request) {
yield effects.put(clearPShiftRequestSuccess());
yield effects.put(clearPShiftRequestError());
const wasSuccessful = yield effects.call(updatePShiftCall, request.data);
if (wasSuccessful) {
yield effects.put(setPShiftRequestSuccess(wasSuccessful));
if (request.data.single) {
yield effects.put(getPShiftRequest({ uuid: request.data.uuid }));
} else {
yield effects.put(getPShiftsRequest({ page: 1 }));
}
}
}