diff --git a/src/actions/cShift/reducer.actions.js b/src/actions/cShift/reducer.actions.js index 012a246..61cbf2b 100644 --- a/src/actions/cShift/reducer.actions.js +++ b/src/actions/cShift/reducer.actions.js @@ -10,6 +10,7 @@ import { SET_FORM_SHIFT_DURATION, SET_FORM_SHIFT_NOTE, SET_FORM_SHIFT_DATES, + SET_CSHIFT_UUID, SET_CLEAR_CSHIFT_STATE } from "../../constants/cShift.constants"; import { parseError } from "../common.actions"; @@ -43,7 +44,7 @@ export function setCShiftRequestErrors(exceptions) { return { type: SET_CSHIFT_REQUEST_ERRORS, data: errors - } + }; } export function clearCShiftRequestError() { @@ -107,6 +108,13 @@ export function setFormShiftDates(dates) { }; } +export function setCShiftUUID(uuid) { + return { + type: SET_CSHIFT_UUID, + data: uuid + }; +} + export function setClearCShiftState() { return { type: SET_CLEAR_CSHIFT_STATE diff --git a/src/actions/cShift/saga.actions.js b/src/actions/cShift/saga.actions.js index 319d642..00817b9 100644 --- a/src/actions/cShift/saga.actions.js +++ b/src/actions/cShift/saga.actions.js @@ -1,6 +1,8 @@ import { CREATE_MULTIPLE_CSHIFT_REQUEST, - GET_CSHIFTS_REQUEST + GET_CSHIFTS_REQUEST, + GET_CSHIFT_REQUEST, + EDIT_CSHIFT_REQUEST } from "../../constants/cShift.constants"; /** @@ -20,3 +22,17 @@ export function getCShiftsRequest(params) { data: params }; } + +export function getCShiftRequest(payload) { + return { + type: GET_CSHIFT_REQUEST, + data: payload + }; +} + +export function editCShiftRequest(payload) { + return { + type: EDIT_CSHIFT_REQUEST, + data: payload + } +} diff --git a/src/api/cShift.api.js b/src/api/cShift.api.js index fbdba4a..cd76d04 100644 --- a/src/api/cShift.api.js +++ b/src/api/cShift.api.js @@ -1,4 +1,4 @@ -import { get, post } from "./baseApi"; +import { get, put, post } from "./baseApi"; export function createCShifts(postBodies) { return Promise.all( @@ -15,3 +15,11 @@ export function createCShifts(postBodies) { export function getCShifts(params) { return get("/cshift/", params).then(resp => Promise.resolve(resp)); } + +export function getCShift(uuid, params) { + return get(`/cshift/${uuid}/`, params).then(resp => Promise.resolve(resp)); +} + +export function editCShift(uuid, payload) { + return put(`/cshift/${uuid}/`, payload).then(resp => Promise.resolve(resp)); +} diff --git a/src/components/App.jsx b/src/components/App.jsx index 2657166..3165060 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -16,6 +16,7 @@ import UpdatePriceForm from "./User/Client/UpdatePriceForm"; import ClientAddProviderForm from "./User/Client/ClientAddProviderForm"; 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 CompleteRegistration from "./User/CompleteRegistration"; import EditProfile from "./User/EditProfile"; @@ -92,6 +93,10 @@ class App extends Component { path="/user/profile/client/add-shift" component={ClientAddShiftForm} /> + { @@ -177,7 +151,7 @@ class ClientAddShiftForm extends Component { key => shiftDates[key] ); return ( - ( - - Schedule Shifts - - - - Employee - - - - Price - - - - Shift Start Time - - - - Shift Duration - - - - - Note - - - - Dates - - - {!!cShiftRequestErrors.length && ( - - )} - - Add Shift successful! - Shifts successfully scheduled. - {!!cShiftRequestSuccess && ( - - )} - - Schedule Shift - - -); - export default connect(mapStateToProps)(ClientAddShiftForm); diff --git a/src/components/User/Client/ClientEditShiftForm.jsx b/src/components/User/Client/ClientEditShiftForm.jsx new file mode 100644 index 0000000..0c5317a --- /dev/null +++ b/src/components/User/Client/ClientEditShiftForm.jsx @@ -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 ; + } + if (cShiftUUID === true) { + return ; + } + + 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: ( + + + {work_type.label} + + + ) + })) + ); + } + + const selectedShiftDates = Object.keys(shiftDates).map( + key => shiftDates[key] + ); + return ( + + ); + } +} + +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 ; + } else { + return ; + } + } +} + +export default connect(mapStateToProps)(EditClientShiftWrapper); diff --git a/src/components/User/Client/ClientShiftFormView.jsx b/src/components/User/Client/ClientShiftFormView.jsx new file mode 100644 index 0000000..502705c --- /dev/null +++ b/src/components/User/Client/ClientShiftFormView.jsx @@ -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 +}) => ( + + + {!isEditing && "Schedule Shifts"} + {isEditing && "Edit Shift"} + + + + + Employee + + + + Price + + + + Shift Start Time + + + + Shift Duration + + + + + Note + + + + Dates + + + {!!cShiftRequestErrors.length && ( + + )} + + Add Shift successful! + Shifts successfully scheduled. + {!!cShiftRequestSuccess && ( + + )} + + + {!isEditing && "Schedule Shifts"} + {isEditing && "Edit Shift"} + + + +); + +export default ClientShiftFormView; diff --git a/src/components/User/Client/ClientShiftShared.js b/src/components/User/Client/ClientShiftShared.js new file mode 100644 index 0000000..d721b43 --- /dev/null +++ b/src/components/User/Client/ClientShiftShared.js @@ -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; +}; diff --git a/src/components/User/Client/ClientShifts.jsx b/src/components/User/Client/ClientShifts.jsx index 5a8194e..bc4a534 100644 --- a/src/components/User/Client/ClientShifts.jsx +++ b/src/components/User/Client/ClientShifts.jsx @@ -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 + {!!isSendingCShiftRequest && } {!isSendingCShiftRequest && results.length > 0 && ( - + {results.map(result => ( {JSON.stringify(result, null, 2)} + + + Edit + + + Delete + + ))} diff --git a/src/constants/cShift.constants.js b/src/constants/cShift.constants.js index 9313361..7031da0 100644 --- a/src/constants/cShift.constants.js +++ b/src/constants/cShift.constants.js @@ -10,8 +10,11 @@ export const SET_FORM_SHIFT_START_TIME = "SET_FORM_SHIFT_START_TIME"; export const SET_FORM_SHIFT_DURATION = "SET_FORM_SHIFT_DURATION"; export const SET_FORM_SHIFT_NOTE = "SET_FORM_SHIFT_NOTE"; export const SET_FORM_SHIFT_DATES = "SET_FORM_SHIFT_DATES"; +export const SET_CSHIFT_UUID = "SET_CSHIFT_UUID"; export const SET_CLEAR_CSHIFT_STATE = "SET_CLEAR_CSHIFT_STATE"; // Saga CShift Action Constants export const CREATE_MULTIPLE_CSHIFT_REQUEST = "CREATE_MULTIPLE_CSHIFT_REQUEST"; export const GET_CSHIFTS_REQUEST = "GET_CSHIFTS_REQUEST"; +export const GET_CSHIFT_REQUEST = "GET_CSHIFT_REQUEST"; +export const EDIT_CSHIFT_REQUEST = "EDIT_CSHIFT_REQUEST"; diff --git a/src/reducers/cShiftReducer.js b/src/reducers/cShiftReducer.js index 3083443..b53e653 100644 --- a/src/reducers/cShiftReducer.js +++ b/src/reducers/cShiftReducer.js @@ -10,6 +10,7 @@ import { SET_FORM_SHIFT_DURATION, SET_FORM_SHIFT_NOTE, SET_FORM_SHIFT_DATES, + SET_CSHIFT_UUID, SET_CLEAR_CSHIFT_STATE } from "../constants/cShift.constants"; @@ -22,7 +23,8 @@ const initialState = { startTime: null, // When does the shift begin? moment instance duration: "", // Duration of shift in minutes note: "", // Optional note - shiftDates: {} // Dates, map of "YYYY-MM-DD" > moment instance + shiftDates: {}, // Dates, map of "YYYY-MM-DD" > moment instance + cShiftUUID: "" }; function cShiftReducer(state = initialState, action) { @@ -82,6 +84,11 @@ function cShiftReducer(state = initialState, action) { ...state, shiftDates: action.data }; + case SET_CSHIFT_UUID: + return { + ...state, + cShiftUUID: action.data + }; case SET_CLEAR_CSHIFT_STATE: return { ...initialState diff --git a/src/sagas/cShift.sagas.js b/src/sagas/cShift.sagas.js index ab407f0..45edb7b 100644 --- a/src/sagas/cShift.sagas.js +++ b/src/sagas/cShift.sagas.js @@ -11,9 +11,15 @@ import { setFormShiftDates, setFormShiftDuration, setFormShiftNote, - setFormShiftStartTime + setFormShiftStartTime, + setCShiftUUID } from "../actions/cShift/reducer.actions"; -import { createCShifts, getCShifts } from "../api/cShift.api"; +import { + createCShifts, + getCShifts, + getCShift, + editCShift +} from "../api/cShift.api"; function* createCShiftsCall(postBodies) { yield effects.put(isSendingCShiftRequest(true)); @@ -48,6 +54,32 @@ function* getCShiftsCall(params) { } } +function* getCShiftCall({ uuid, params }) { + yield effects.put(isSendingCShiftRequest(true)); + try { + return yield effects.call(getCShift, uuid, params); + } catch (exception) { + yield effects.put(setCShiftRequestError(exception)); + return false; + } finally { + yield effects.put(isSendingCShiftRequest(false)); + } +} + +function* editCShiftCall(payload) { + yield effects.put(isSendingCShiftRequest(true)); + try { + const edit = yield effects.call(editCShift, payload.uuid, payload); + yield effects.put(setCShiftUUID(true)); + return edit; + } catch (exception) { + yield effects.put(setCShiftRequestError(exception)); + return false; + } finally { + yield effects.put(isSendingCShiftRequest(false)); + } +} + export function* createCShiftsFlow(request) { yield effects.put(clearCShiftRequestSuccess()); yield effects.put(clearCShiftRequestError()); @@ -78,3 +110,20 @@ export function* getCShiftsFlow(request) { yield effects.put(setCShiftRequestSuccess(isSuccessful)); } } + +export function* getCShiftFlow(request) { + yield effects.put(clearCShiftRequestSuccess()); + yield effects.put(clearCShiftRequestError()); + const wasSuccessful = yield effects.call(getCShiftCall, request.data); + if (wasSuccessful) { + yield effects.put(setCShiftRequestSuccess(wasSuccessful)); + } +} + +export function* editCShiftFlow(request) { + yield effects.put(clearCShiftRequestError()); + const wasSuccessful = yield effects.call(editCShiftCall, request.data); + if (wasSuccessful) { + yield effects.put(setCShiftRequestSuccess(wasSuccessful)); + } +} diff --git a/src/sagas/index.js b/src/sagas/index.js index 0a28cac..03b59ae 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -71,9 +71,16 @@ import { } from "./price.sagas"; import { CREATE_MULTIPLE_CSHIFT_REQUEST, - GET_CSHIFTS_REQUEST + GET_CSHIFTS_REQUEST, + GET_CSHIFT_REQUEST, + EDIT_CSHIFT_REQUEST } from "../constants/cShift.constants"; -import { createCShiftsFlow, getCShiftsFlow } from "./cShift.sagas"; +import { + createCShiftsFlow, + getCShiftsFlow, + getCShiftFlow, + editCShiftFlow +} from "./cShift.sagas"; export default function* rootSaga() { yield takeLatest(SEND_REGISTER_REQUEST, registerUserFlow); @@ -103,4 +110,6 @@ export default function* rootSaga() { yield takeLatest(DELETE_PRICE_REQUEST, deletePriceFlow); yield takeLatest(CREATE_MULTIPLE_CSHIFT_REQUEST, createCShiftsFlow); yield takeLatest(GET_CSHIFTS_REQUEST, getCShiftsFlow); + yield takeLatest(GET_CSHIFT_REQUEST, getCShiftFlow); + yield takeLatest(EDIT_CSHIFT_REQUEST, editCShiftFlow); }
Shifts successfully scheduled.
{JSON.stringify(result, null, 2)}