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>
|
||||
<name>translation</name>
|
||||
<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>
|
||||
<name>associations</name>
|
||||
<children>
|
||||
@@ -3012,6 +3184,27 @@
|
||||
<folder_node>
|
||||
<name>labels</name>
|
||||
<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>
|
||||
<name>available_new_jobs</name>
|
||||
<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 App from "./App";
|
||||
|
||||
|
||||
|
||||
class AppContainer extends Component {
|
||||
state = {
|
||||
client: null,
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function JobsDetailHeader({
|
||||
scheduleModalState
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [scheduleModalVisible, setscheduleModalVisible] = scheduleModalState;
|
||||
const setscheduleModalVisible = scheduleModalState[1];
|
||||
|
||||
const tombstoneTitle = (
|
||||
<div>
|
||||
|
||||
@@ -4,5 +4,5 @@ import "./loading-skeleton.styles.scss";
|
||||
import { Skeleton } from "antd";
|
||||
|
||||
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 { 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.component";
|
||||
const localizer = momentLocalizer(moment);
|
||||
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
|
||||
|
||||
export default function ScheduleCalendarComponent({ data }) {
|
||||
export default function ScheduleCalendarComponent({ data, refetch }) {
|
||||
return (
|
||||
<Calendar
|
||||
events={data}
|
||||
<ScheduleCalendarWrapperComponent
|
||||
data={data}
|
||||
defaultView="week"
|
||||
//views={allViews}
|
||||
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
|
||||
}}
|
||||
refetch={refetch}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import React from "react";
|
||||
import { useQuery } from "react-apollo";
|
||||
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 AlertComponent from "../alert/alert.component";
|
||||
|
||||
export default function ScheduleCalendarContainer() {
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_APPOINTMENTS, {
|
||||
fetchPolicy: "network-only"
|
||||
});
|
||||
const { loading, error, data, refetch } = useQuery(
|
||||
QUERY_ALL_ACTIVE_APPOINTMENTS,
|
||||
{
|
||||
fetchPolicy: "network-only"
|
||||
}
|
||||
);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
@@ -2,22 +2,16 @@ import React from "react";
|
||||
|
||||
export default function ScheduleDateCellWrapper(dateCellWrapperProps) {
|
||||
// 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 = {
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
borderLeft: "1px solid #DDD",
|
||||
backgroundColor: hasAlert ? "#f5f5dc" : "#fff"
|
||||
backgroundColor: "#fff"
|
||||
};
|
||||
return (
|
||||
<div style={style}>
|
||||
DateCellWrapper
|
||||
{hasAlert && <button onClick={e => alert(e)}>Click me</button>}
|
||||
PLACEHOLDER:DATA
|
||||
{dateCellWrapperProps.children}
|
||||
</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 { useTranslation } from "react-i18next";
|
||||
|
||||
export default function Event({ event }) {
|
||||
export default function ScheduleEventComponent({ event, handleCancel }) {
|
||||
const { t } = useTranslation();
|
||||
const popoverContent = (
|
||||
<div>
|
||||
@@ -23,19 +23,35 @@ export default function Event({ event }) {
|
||||
<PhoneFormatter>{event.job.ownr_ph1}</PhoneFormatter>
|
||||
</div>
|
||||
<Link to={`/manage/jobs/${event.job.id}`}>
|
||||
<Button>View Job</Button>
|
||||
<Button>{t("appointments.actions.viewjob")}</Button>
|
||||
</Link>
|
||||
<Button>//TODO: Reschedule</Button>
|
||||
<Button>//TODO: Intake</Button>
|
||||
<Button onClick={() => handleCancel(event.id)}>
|
||||
{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>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover content={popoverContent}>
|
||||
<strong>{`${event.job.ownr_fn || ""} ${event.job.ownr_ln || ""}`}</strong>
|
||||
<div>
|
||||
{`${event.job.vehicle.v_model_yr || ""} ${event.job.vehicle
|
||||
.v_make_desc || ""} ${event.job.vehicle.v_model_desc || ""}`}
|
||||
<strong>{`${event.job.ownr_fn || ""} ${event.job.ownr_ln ||
|
||||
""}`}</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>
|
||||
</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 { Modal, Tabs, DatePicker, TimePicker } from "antd";
|
||||
import moment from "moment";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ScheduleDayViewContainer from "../schedule-day-view/schedule-day-view.container";
|
||||
export default function ScheduleJobModalComponent({
|
||||
appData,
|
||||
setAppData,
|
||||
formData,
|
||||
setFormData,
|
||||
...props
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Modal {...props} maskClosable={false}>
|
||||
<Modal {...props} width={"80%"} maskClosable={false}>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<Tabs.TabPane tab="SMART Scheduling" key="auto">
|
||||
Automatic Job Selection.
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Manual Scheduling" key="manual">
|
||||
Manual Job Selection Scheduled Time
|
||||
<DatePicker
|
||||
value={appData.start}
|
||||
onChange={e => {
|
||||
setAppData({ ...appData, start: e });
|
||||
}}
|
||||
/>
|
||||
<TimePicker
|
||||
value={appData.start}
|
||||
format={"HH:mm"}
|
||||
minuteStep={15}
|
||||
onChange={e => {
|
||||
setAppData({ ...appData, start: e });
|
||||
}}
|
||||
/>
|
||||
<Row>
|
||||
<Col span={14}>
|
||||
Manual Job Selection Scheduled Time
|
||||
<DatePicker
|
||||
value={appData.start}
|
||||
onChange={e => {
|
||||
setAppData({ ...appData, start: e });
|
||||
}}
|
||||
/>
|
||||
<TimePicker
|
||||
value={appData.start}
|
||||
format={"HH:mm"}
|
||||
minuteStep={15}
|
||||
onChange={e => {
|
||||
setAppData({ ...appData, start: e });
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<ScheduleDayViewContainer day={appData.start} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
{
|
||||
//TODO: Build out notifications.
|
||||
}
|
||||
<Checkbox
|
||||
defaultChecked={formData.notifyCustomer}
|
||||
onChange={e =>
|
||||
setFormData({ ...formData, notifyCustomer: e.target.checked })
|
||||
}
|
||||
>
|
||||
{t("jobs.labels.appointmentconfirmation")}
|
||||
</Checkbox>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ import ScheduleJobModalComponent from "./schedule-job-modal.component";
|
||||
import { useMutation } from "react-apollo";
|
||||
import { INSERT_APPOINTMENT } from "../../graphql/appointments.queries";
|
||||
import moment from "moment";
|
||||
|
||||
import { notification } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
export default function ScheduleJobModalContainer({
|
||||
scheduleModalState,
|
||||
jobId
|
||||
@@ -11,11 +12,15 @@ export default function ScheduleJobModalContainer({
|
||||
const [scheduleModalVisible, setscheduleModalVisible] = scheduleModalState;
|
||||
const [appData, setAppData] = useState({ jobid: jobId, start: null });
|
||||
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
|
||||
const [formData, setFormData] = useState({ notifyCustomer: false });
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<ScheduleJobModalComponent
|
||||
appData={appData}
|
||||
setAppData={setAppData}
|
||||
formData={formData}
|
||||
setFormData={setFormData}
|
||||
//Spreadable Modal Props
|
||||
visible={scheduleModalVisible}
|
||||
onCancel={() => setscheduleModalVisible(false)}
|
||||
@@ -25,9 +30,25 @@ export default function ScheduleJobModalContainer({
|
||||
variables: {
|
||||
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";
|
||||
|
||||
export const QUERY_ALL_APPOINTMENTS = gql`
|
||||
query QUERY_ALL_APPOINTMENTS {
|
||||
appointments {
|
||||
export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
|
||||
query QUERY_ALL_ACTIVE_APPOINTMENTS {
|
||||
appointments(where: { canceled: { _eq: false } }) {
|
||||
start
|
||||
id
|
||||
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 } }) {
|
||||
try {
|
||||
const { user } = yield auth.signInWithEmailAndPassword(email, password);
|
||||
let token = yield user.getIdToken();
|
||||
localStorage.setItem("token", token);
|
||||
window.sessionStorage.setItem(`lastTokenRefreshTime`, new Date());
|
||||
yield put(
|
||||
signInSuccess({
|
||||
uid: user.uid,
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
{
|
||||
"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": {
|
||||
"actions": {
|
||||
"activate": "Activate"
|
||||
@@ -176,6 +193,7 @@
|
||||
"vehicle": "Vehicle"
|
||||
},
|
||||
"labels": {
|
||||
"appointmentconfirmation": "Send confirmation to customer?",
|
||||
"available_new_jobs": "",
|
||||
"cards": {
|
||||
"appraiser": "Appraiser",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
{
|
||||
"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": {
|
||||
"actions": {
|
||||
"activate": "Activar"
|
||||
@@ -176,6 +193,7 @@
|
||||
"vehicle": "Vehículo"
|
||||
},
|
||||
"labels": {
|
||||
"appointmentconfirmation": "¿Enviar confirmación al cliente?",
|
||||
"available_new_jobs": "",
|
||||
"cards": {
|
||||
"appraiser": "Tasador",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
{
|
||||
"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": {
|
||||
"actions": {
|
||||
"activate": "Activer"
|
||||
@@ -176,6 +193,7 @@
|
||||
"vehicle": "Véhicule"
|
||||
},
|
||||
"labels": {
|
||||
"appointmentconfirmation": "Envoyer une confirmation au client?",
|
||||
"available_new_jobs": "",
|
||||
"cards": {
|
||||
"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