Table updates for new appointments fields. Schedule modal baseline functionality. Refactor schedule components to be more reusable.
This commit is contained in:
@@ -18,6 +18,178 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>translation</name>
|
<name>translation</name>
|
||||||
<children>
|
<children>
|
||||||
|
<folder_node>
|
||||||
|
<name>appointments</name>
|
||||||
|
<children>
|
||||||
|
<folder_node>
|
||||||
|
<name>actions</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>cancel</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>intake</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>reschedule</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>viewjob</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>errors</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>saving</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>labels</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>nodateselected</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>successes</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>created</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>associations</name>
|
<name>associations</name>
|
||||||
<children>
|
<children>
|
||||||
@@ -3012,6 +3184,27 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>labels</name>
|
<name>labels</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>appointmentconfirmation</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>available_new_jobs</name>
|
<name>available_new_jobs</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import SpinnerComponent from "../components/loading-spinner/loading-spinner.comp
|
|||||||
import errorLink from "../graphql/apollo-error-handling";
|
import errorLink from "../graphql/apollo-error-handling";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AppContainer extends Component {
|
class AppContainer extends Component {
|
||||||
state = {
|
state = {
|
||||||
client: null,
|
client: null,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default function JobsDetailHeader({
|
|||||||
scheduleModalState
|
scheduleModalState
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [scheduleModalVisible, setscheduleModalVisible] = scheduleModalState;
|
const setscheduleModalVisible = scheduleModalState[1];
|
||||||
|
|
||||||
const tombstoneTitle = (
|
const tombstoneTitle = (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ import "./loading-skeleton.styles.scss";
|
|||||||
import { Skeleton } from "antd";
|
import { Skeleton } from "antd";
|
||||||
|
|
||||||
export default function LoadingSkeleton(props) {
|
export default function LoadingSkeleton(props) {
|
||||||
return <Skeleton {...props} className='loading-skeleton' active />;
|
return <Skeleton {...props} className="loading-skeleton" active />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import moment from "moment";
|
||||||
|
import React from "react";
|
||||||
|
import { Calendar, momentLocalizer } from "react-big-calendar";
|
||||||
|
import "react-big-calendar/lib/css/react-big-calendar.css";
|
||||||
|
import DateCellWrapper from "../schedule-datecellwrapper/schedule-datecellwrapper.component";
|
||||||
|
import Event from "../schedule-event/schedule-event.container";
|
||||||
|
const localizer = momentLocalizer(moment);
|
||||||
|
|
||||||
|
export default function ScheduleCalendarWrapperComponent({
|
||||||
|
data,
|
||||||
|
refetch,
|
||||||
|
defaultView,
|
||||||
|
...otherProps
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Calendar
|
||||||
|
events={data}
|
||||||
|
defaultView={defaultView}
|
||||||
|
step={30}
|
||||||
|
showMultiDayTimes
|
||||||
|
localizer={localizer}
|
||||||
|
min={new Date("2020-01-01T06:00:00")} //TODO: Read from business settings.
|
||||||
|
max={new Date("2020-01-01T20:00:00")}
|
||||||
|
components={{
|
||||||
|
event: e => {
|
||||||
|
return Event({ event: e.event, refetch: refetch });
|
||||||
|
},
|
||||||
|
dateCellWrapper: DateCellWrapper
|
||||||
|
}}
|
||||||
|
{...otherProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,27 +1,13 @@
|
|||||||
import moment from "moment";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Calendar, momentLocalizer } from "react-big-calendar";
|
|
||||||
import "react-big-calendar/lib/css/react-big-calendar.css";
|
import "react-big-calendar/lib/css/react-big-calendar.css";
|
||||||
import DateCellWrapper from "../schedule-datecellwrapper/schedule-datecellwrapper.component";
|
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
|
||||||
import Event from "../schedule-event/schedule-event.component";
|
|
||||||
const localizer = momentLocalizer(moment);
|
|
||||||
|
|
||||||
export default function ScheduleCalendarComponent({ data }) {
|
export default function ScheduleCalendarComponent({ data, refetch }) {
|
||||||
return (
|
return (
|
||||||
<Calendar
|
<ScheduleCalendarWrapperComponent
|
||||||
events={data}
|
data={data}
|
||||||
defaultView="week"
|
defaultView="week"
|
||||||
//views={allViews}
|
refetch={refetch}
|
||||||
step={30}
|
|
||||||
showMultiDayTimes
|
|
||||||
localizer={localizer}
|
|
||||||
min={new Date("2020-01-01T06:00:00")} //TODO: Read from business settings.
|
|
||||||
max={new Date("2020-01-01T20:00:00")}
|
|
||||||
//onSelectEvent={event => console.log("event", event)}
|
|
||||||
components={{
|
|
||||||
event: Event,
|
|
||||||
dateCellWrapper: DateCellWrapper
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useQuery } from "react-apollo";
|
import { useQuery } from "react-apollo";
|
||||||
import ScheduleCalendarComponent from "./schedule-calendar.component";
|
import ScheduleCalendarComponent from "./schedule-calendar.component";
|
||||||
import { QUERY_ALL_APPOINTMENTS } from "../../graphql/appointments.queries";
|
import { QUERY_ALL_ACTIVE_APPOINTMENTS } from "../../graphql/appointments.queries";
|
||||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
|
||||||
export default function ScheduleCalendarContainer() {
|
export default function ScheduleCalendarContainer() {
|
||||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_APPOINTMENTS, {
|
const { loading, error, data, refetch } = useQuery(
|
||||||
fetchPolicy: "network-only"
|
QUERY_ALL_ACTIVE_APPOINTMENTS,
|
||||||
});
|
{
|
||||||
|
fetchPolicy: "network-only"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (loading) return <LoadingSpinner />;
|
if (loading) return <LoadingSpinner />;
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|||||||
@@ -2,22 +2,16 @@ import React from "react";
|
|||||||
|
|
||||||
export default function ScheduleDateCellWrapper(dateCellWrapperProps) {
|
export default function ScheduleDateCellWrapper(dateCellWrapperProps) {
|
||||||
// Show 'click me' text in arbitrary places by using the range prop
|
// Show 'click me' text in arbitrary places by using the range prop
|
||||||
const hasAlert = dateCellWrapperProps.range
|
|
||||||
? dateCellWrapperProps.range.some(date => {
|
|
||||||
return date.getDate() % 12 === 0;
|
|
||||||
})
|
|
||||||
: false;
|
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flex: 1,
|
flex: 1,
|
||||||
borderLeft: "1px solid #DDD",
|
borderLeft: "1px solid #DDD",
|
||||||
backgroundColor: hasAlert ? "#f5f5dc" : "#fff"
|
backgroundColor: "#fff"
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div style={style}>
|
<div style={style}>
|
||||||
DateCellWrapper
|
PLACEHOLDER:DATA
|
||||||
{hasAlert && <button onClick={e => alert(e)}>Click me</button>}
|
|
||||||
{dateCellWrapperProps.children}
|
{dateCellWrapperProps.children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import React from "react";
|
||||||
|
import "react-big-calendar/lib/css/react-big-calendar.css";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
|
||||||
|
|
||||||
|
export default function ScheduleDayViewComponent({ data, day }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
if (data)
|
||||||
|
//TODO Remove addtional calendar elements from day view.
|
||||||
|
return (
|
||||||
|
<ScheduleCalendarWrapperComponent
|
||||||
|
events={data}
|
||||||
|
defaultView="day"
|
||||||
|
style={{ height: "40vh" }}
|
||||||
|
defaultDate={new Date(day)}
|
||||||
|
//onNavigate={e => console.log("e", e)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
else return <div>{t("appointments.labels.nodateselected")}</div>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ScheduleDayViewComponent from "./schedule-day-view.component";
|
||||||
|
import { useQuery } from "react-apollo";
|
||||||
|
import { QUERY_APPOINTMENT_BY_DATE } from "../../graphql/appointments.queries";
|
||||||
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
|
import moment from "moment";
|
||||||
|
|
||||||
|
export default function ScheduleDayViewContainer({ day }) {
|
||||||
|
const { loading, error, data } = useQuery(QUERY_APPOINTMENT_BY_DATE, {
|
||||||
|
variables: {
|
||||||
|
start: moment(day).startOf("day"),
|
||||||
|
end: moment(day).endOf("day")
|
||||||
|
},
|
||||||
|
skip: !day,
|
||||||
|
fetchPolicy: "network-only"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading) return <LoadingSkeleton paragraph={{ rows: 4 }} />;
|
||||||
|
if (error) return <div>{error.message}</div>;
|
||||||
|
let normalizedData;
|
||||||
|
if (data) {
|
||||||
|
normalizedData = data.appointments.map(e => {
|
||||||
|
//Required becuase Hasura returns a string instead of a date object.
|
||||||
|
return Object.assign(
|
||||||
|
{},
|
||||||
|
e,
|
||||||
|
{ start: new Date(e.start) },
|
||||||
|
{ end: new Date(e.end) }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScheduleDayViewComponent data={data ? normalizedData : null} day={day} />
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import PhoneFormatter from "../../utils/PhoneFormatter";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export default function Event({ event }) {
|
export default function ScheduleEventComponent({ event, handleCancel }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const popoverContent = (
|
const popoverContent = (
|
||||||
<div>
|
<div>
|
||||||
@@ -23,19 +23,35 @@ export default function Event({ event }) {
|
|||||||
<PhoneFormatter>{event.job.ownr_ph1}</PhoneFormatter>
|
<PhoneFormatter>{event.job.ownr_ph1}</PhoneFormatter>
|
||||||
</div>
|
</div>
|
||||||
<Link to={`/manage/jobs/${event.job.id}`}>
|
<Link to={`/manage/jobs/${event.job.id}`}>
|
||||||
<Button>View Job</Button>
|
<Button>{t("appointments.actions.viewjob")}</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Button>//TODO: Reschedule</Button>
|
<Button onClick={() => handleCancel(event.id)}>
|
||||||
<Button>//TODO: Intake</Button>
|
{t("appointments.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
<Button>
|
||||||
|
{
|
||||||
|
//TODO: Add reschedule Func.
|
||||||
|
}
|
||||||
|
{t("appointments.actions.reschedule")}
|
||||||
|
</Button>
|
||||||
|
<Button>
|
||||||
|
{
|
||||||
|
//TODO: Add intake func.
|
||||||
|
}
|
||||||
|
{t("appointments.actions.intake")}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover content={popoverContent}>
|
<Popover content={popoverContent}>
|
||||||
<strong>{`${event.job.ownr_fn || ""} ${event.job.ownr_ln || ""}`}</strong>
|
|
||||||
<div>
|
<div>
|
||||||
{`${event.job.vehicle.v_model_yr || ""} ${event.job.vehicle
|
<strong>{`${event.job.ownr_fn || ""} ${event.job.ownr_ln ||
|
||||||
.v_make_desc || ""} ${event.job.vehicle.v_model_desc || ""}`}
|
""}`}</strong>
|
||||||
|
<span style={{ margin: 4 }}>
|
||||||
|
{`${event.job.vehicle.v_model_yr || ""} ${event.job.vehicle
|
||||||
|
.v_make_desc || ""} ${event.job.vehicle.v_model_desc || ""}`}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useMutation } from "react-apollo";
|
||||||
|
import { CANCEL_APPOINTMENT_BY_ID } from "../../graphql/appointments.queries";
|
||||||
|
import ScheduleEventComponent from "./schedule-event.component";
|
||||||
|
|
||||||
|
export default function ScheduleEventContainer({ event, refetch }) {
|
||||||
|
const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID);
|
||||||
|
console.log("refetch", refetch);
|
||||||
|
const handleCancel = id => {
|
||||||
|
cancelAppointment({ variables: { appid: event.id } }).then(r => {
|
||||||
|
if (refetch) refetch();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ScheduleEventComponent event={event} handleCancel={handleCancel} />;
|
||||||
|
}
|
||||||
@@ -1,35 +1,57 @@
|
|||||||
|
import { Checkbox, DatePicker, Modal, Tabs, TimePicker, Col, Row } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Modal, Tabs, DatePicker, TimePicker } from "antd";
|
import { useTranslation } from "react-i18next";
|
||||||
import moment from "moment";
|
import ScheduleDayViewContainer from "../schedule-day-view/schedule-day-view.container";
|
||||||
export default function ScheduleJobModalComponent({
|
export default function ScheduleJobModalComponent({
|
||||||
appData,
|
appData,
|
||||||
setAppData,
|
setAppData,
|
||||||
|
formData,
|
||||||
|
setFormData,
|
||||||
...props
|
...props
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Modal {...props} maskClosable={false}>
|
<Modal {...props} width={"80%"} maskClosable={false}>
|
||||||
<Tabs defaultActiveKey="1">
|
<Tabs defaultActiveKey="1">
|
||||||
<Tabs.TabPane tab="SMART Scheduling" key="auto">
|
<Tabs.TabPane tab="SMART Scheduling" key="auto">
|
||||||
Automatic Job Selection.
|
Automatic Job Selection.
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane tab="Manual Scheduling" key="manual">
|
<Tabs.TabPane tab="Manual Scheduling" key="manual">
|
||||||
Manual Job Selection Scheduled Time
|
<Row>
|
||||||
<DatePicker
|
<Col span={14}>
|
||||||
value={appData.start}
|
Manual Job Selection Scheduled Time
|
||||||
onChange={e => {
|
<DatePicker
|
||||||
setAppData({ ...appData, start: e });
|
value={appData.start}
|
||||||
}}
|
onChange={e => {
|
||||||
/>
|
setAppData({ ...appData, start: e });
|
||||||
<TimePicker
|
}}
|
||||||
value={appData.start}
|
/>
|
||||||
format={"HH:mm"}
|
<TimePicker
|
||||||
minuteStep={15}
|
value={appData.start}
|
||||||
onChange={e => {
|
format={"HH:mm"}
|
||||||
setAppData({ ...appData, start: e });
|
minuteStep={15}
|
||||||
}}
|
onChange={e => {
|
||||||
/>
|
setAppData({ ...appData, start: e });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={10}>
|
||||||
|
<ScheduleDayViewContainer day={appData.start} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
{
|
||||||
|
//TODO: Build out notifications.
|
||||||
|
}
|
||||||
|
<Checkbox
|
||||||
|
defaultChecked={formData.notifyCustomer}
|
||||||
|
onChange={e =>
|
||||||
|
setFormData({ ...formData, notifyCustomer: e.target.checked })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t("jobs.labels.appointmentconfirmation")}
|
||||||
|
</Checkbox>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import ScheduleJobModalComponent from "./schedule-job-modal.component";
|
|||||||
import { useMutation } from "react-apollo";
|
import { useMutation } from "react-apollo";
|
||||||
import { INSERT_APPOINTMENT } from "../../graphql/appointments.queries";
|
import { INSERT_APPOINTMENT } from "../../graphql/appointments.queries";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import { notification } from "antd";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
export default function ScheduleJobModalContainer({
|
export default function ScheduleJobModalContainer({
|
||||||
scheduleModalState,
|
scheduleModalState,
|
||||||
jobId
|
jobId
|
||||||
@@ -11,11 +12,15 @@ export default function ScheduleJobModalContainer({
|
|||||||
const [scheduleModalVisible, setscheduleModalVisible] = scheduleModalState;
|
const [scheduleModalVisible, setscheduleModalVisible] = scheduleModalState;
|
||||||
const [appData, setAppData] = useState({ jobid: jobId, start: null });
|
const [appData, setAppData] = useState({ jobid: jobId, start: null });
|
||||||
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
|
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
|
||||||
|
const [formData, setFormData] = useState({ notifyCustomer: false });
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScheduleJobModalComponent
|
<ScheduleJobModalComponent
|
||||||
appData={appData}
|
appData={appData}
|
||||||
setAppData={setAppData}
|
setAppData={setAppData}
|
||||||
|
formData={formData}
|
||||||
|
setFormData={setFormData}
|
||||||
//Spreadable Modal Props
|
//Spreadable Modal Props
|
||||||
visible={scheduleModalVisible}
|
visible={scheduleModalVisible}
|
||||||
onCancel={() => setscheduleModalVisible(false)}
|
onCancel={() => setscheduleModalVisible(false)}
|
||||||
@@ -25,9 +30,25 @@ export default function ScheduleJobModalContainer({
|
|||||||
variables: {
|
variables: {
|
||||||
app: { ...appData, end: moment(appData.start).add(60, "minutes") }
|
app: { ...appData, end: moment(appData.start).add(60, "minutes") }
|
||||||
}
|
}
|
||||||
}).then(r => {
|
})
|
||||||
setscheduleModalVisible(false);
|
.then(r => {
|
||||||
});
|
notification["success"]({
|
||||||
|
message: t("appointments.successes.created")
|
||||||
|
});
|
||||||
|
|
||||||
|
if (formData.notifyCustomer) {
|
||||||
|
//TODO: Implement customer reminder on scheduling.
|
||||||
|
alert("Chosed to notify the customer somehow!");
|
||||||
|
}
|
||||||
|
setscheduleModalVisible(false);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
notification["error"]({
|
||||||
|
message: t("appointments.errors.saving", {
|
||||||
|
message: error.message
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { gql } from "apollo-boost";
|
import { gql } from "apollo-boost";
|
||||||
|
|
||||||
export const QUERY_ALL_APPOINTMENTS = gql`
|
export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
|
||||||
query QUERY_ALL_APPOINTMENTS {
|
query QUERY_ALL_ACTIVE_APPOINTMENTS {
|
||||||
appointments {
|
appointments(where: { canceled: { _eq: false } }) {
|
||||||
start
|
start
|
||||||
id
|
id
|
||||||
end
|
end
|
||||||
@@ -35,3 +35,42 @@ export const INSERT_APPOINTMENT = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_APPOINTMENT_BY_DATE = gql`
|
||||||
|
query QUERY_APPOINTMENT_BY_DATE($start: timestamptz, $end: timestamptz) {
|
||||||
|
appointments(where: { start: { _lte: $end, _gte: $start } }) {
|
||||||
|
start
|
||||||
|
id
|
||||||
|
end
|
||||||
|
job {
|
||||||
|
ro_number
|
||||||
|
ownr_ln
|
||||||
|
ownr_fn
|
||||||
|
ownr_ph1
|
||||||
|
ownr_ea
|
||||||
|
clm_total
|
||||||
|
id
|
||||||
|
clm_no
|
||||||
|
vehicle {
|
||||||
|
id
|
||||||
|
v_model_yr
|
||||||
|
v_make_desc
|
||||||
|
v_model_desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CANCEL_APPOINTMENT_BY_ID = gql`
|
||||||
|
mutation CANCEL_APPOINTMENT_BY_ID($appid: uuid!) {
|
||||||
|
update_appointments(
|
||||||
|
where: { id: { _eq: $appid } }
|
||||||
|
_set: { canceled: true }
|
||||||
|
) {
|
||||||
|
returning {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ import UserActionTypes from "./user.types";
|
|||||||
export function* signInWithEmail({ payload: { email, password } }) {
|
export function* signInWithEmail({ payload: { email, password } }) {
|
||||||
try {
|
try {
|
||||||
const { user } = yield auth.signInWithEmailAndPassword(email, password);
|
const { user } = yield auth.signInWithEmailAndPassword(email, password);
|
||||||
|
let token = yield user.getIdToken();
|
||||||
|
localStorage.setItem("token", token);
|
||||||
|
window.sessionStorage.setItem(`lastTokenRefreshTime`, new Date());
|
||||||
yield put(
|
yield put(
|
||||||
signInSuccess({
|
signInSuccess({
|
||||||
uid: user.uid,
|
uid: user.uid,
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
{
|
{
|
||||||
"translation": {
|
"translation": {
|
||||||
|
"appointments": {
|
||||||
|
"actions": {
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"intake": "Intake",
|
||||||
|
"reschedule": "Reschedule",
|
||||||
|
"viewjob": "View Job"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"saving": "Error scheduling appointment. {{message}}"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"nodateselected": "No date has been selected."
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"created": "Appointment scheduled successfully."
|
||||||
|
}
|
||||||
|
},
|
||||||
"associations": {
|
"associations": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"activate": "Activate"
|
"activate": "Activate"
|
||||||
@@ -176,6 +193,7 @@
|
|||||||
"vehicle": "Vehicle"
|
"vehicle": "Vehicle"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"appointmentconfirmation": "Send confirmation to customer?",
|
||||||
"available_new_jobs": "",
|
"available_new_jobs": "",
|
||||||
"cards": {
|
"cards": {
|
||||||
"appraiser": "Appraiser",
|
"appraiser": "Appraiser",
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
{
|
{
|
||||||
"translation": {
|
"translation": {
|
||||||
|
"appointments": {
|
||||||
|
"actions": {
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"intake": "Consumo",
|
||||||
|
"reschedule": "Reprogramar",
|
||||||
|
"viewjob": "Ver trabajo"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"saving": "Error al programar la cita. {{message}}"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"nodateselected": "No se ha seleccionado ninguna fecha."
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"created": "Cita programada con éxito."
|
||||||
|
}
|
||||||
|
},
|
||||||
"associations": {
|
"associations": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"activate": "Activar"
|
"activate": "Activar"
|
||||||
@@ -176,6 +193,7 @@
|
|||||||
"vehicle": "Vehículo"
|
"vehicle": "Vehículo"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"appointmentconfirmation": "¿Enviar confirmación al cliente?",
|
||||||
"available_new_jobs": "",
|
"available_new_jobs": "",
|
||||||
"cards": {
|
"cards": {
|
||||||
"appraiser": "Tasador",
|
"appraiser": "Tasador",
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
{
|
{
|
||||||
"translation": {
|
"translation": {
|
||||||
|
"appointments": {
|
||||||
|
"actions": {
|
||||||
|
"cancel": "annuler",
|
||||||
|
"intake": "Admission",
|
||||||
|
"reschedule": "Replanifier",
|
||||||
|
"viewjob": "Voir le travail"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"saving": "Erreur lors de la planification du rendez-vous. {{message}}"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"nodateselected": "Aucune date n'a été sélectionnée."
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"created": "Rendez-vous planifié avec succès."
|
||||||
|
}
|
||||||
|
},
|
||||||
"associations": {
|
"associations": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"activate": "Activer"
|
"activate": "Activer"
|
||||||
@@ -176,6 +193,7 @@
|
|||||||
"vehicle": "Véhicule"
|
"vehicle": "Véhicule"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"appointmentconfirmation": "Envoyer une confirmation au client?",
|
||||||
"available_new_jobs": "",
|
"available_new_jobs": "",
|
||||||
"cards": {
|
"cards": {
|
||||||
"appraiser": "Expert",
|
"appraiser": "Expert",
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- authid
|
||||||
|
- email
|
||||||
|
- created_at
|
||||||
|
- updated_at
|
||||||
|
filter: {}
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: anonymous
|
||||||
|
table:
|
||||||
|
name: users
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- args:
|
||||||
|
role: anonymous
|
||||||
|
table:
|
||||||
|
name: users
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check: {}
|
||||||
|
columns:
|
||||||
|
- authid
|
||||||
|
- created_at
|
||||||
|
- email
|
||||||
|
- updated_at
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: anonymous
|
||||||
|
table:
|
||||||
|
name: users
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- args:
|
||||||
|
role: anonymous
|
||||||
|
table:
|
||||||
|
name: users
|
||||||
|
schema: public
|
||||||
|
type: drop_insert_permission
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- authid
|
||||||
|
computed_fields: []
|
||||||
|
filter: {}
|
||||||
|
role: anonymous
|
||||||
|
table:
|
||||||
|
name: users
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- args:
|
||||||
|
role: anonymous
|
||||||
|
table:
|
||||||
|
name: users
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
- args:
|
||||||
|
sql: ALTER TABLE "public"."appointments" DROP COLUMN "canceled";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
- args:
|
||||||
|
sql: ALTER TABLE "public"."appointments" ADD COLUMN "canceled" boolean NOT NULL
|
||||||
|
DEFAULT false;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
- args:
|
||||||
|
sql: ALTER TABLE "public"."appointments" DROP COLUMN "arrived";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
- args:
|
||||||
|
sql: ALTER TABLE "public"."appointments" ADD COLUMN "arrived" boolean NOT NULL
|
||||||
|
DEFAULT false;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: drop_insert_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- created_at
|
||||||
|
- end
|
||||||
|
- start
|
||||||
|
- updated_at
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: drop_insert_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- arrived
|
||||||
|
- canceled
|
||||||
|
- created_at
|
||||||
|
- end
|
||||||
|
- start
|
||||||
|
- updated_at
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: create_insert_permission
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- created_at
|
||||||
|
- end
|
||||||
|
- start
|
||||||
|
- updated_at
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- arrived
|
||||||
|
- canceled
|
||||||
|
- created_at
|
||||||
|
- end
|
||||||
|
- start
|
||||||
|
- updated_at
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- created_at
|
||||||
|
- end
|
||||||
|
- start
|
||||||
|
- updated_at
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
filter:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- arrived
|
||||||
|
- canceled
|
||||||
|
- created_at
|
||||||
|
- end
|
||||||
|
- start
|
||||||
|
- updated_at
|
||||||
|
- id
|
||||||
|
- jobid
|
||||||
|
filter:
|
||||||
|
job:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
localPresets:
|
||||||
|
- key: ""
|
||||||
|
value: ""
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: appointments
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
Reference in New Issue
Block a user