Added length of appointment to config + fixed appointments not showing in scheduling modal + added appointment confirmation template. BOD-141 BOD-149 BOD-148
This commit is contained in:
@@ -8,20 +8,21 @@ export default function EmailOverlayComponent({
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
To:
|
||||
<Input
|
||||
defaultValue={messageOptions.to}
|
||||
value={messageOptions.to}
|
||||
onChange={handleConfigChange}
|
||||
name="to"
|
||||
/>
|
||||
CC
|
||||
CC:
|
||||
<Input
|
||||
defaultValue={messageOptions.cc}
|
||||
value={messageOptions.cc}
|
||||
onChange={handleConfigChange}
|
||||
name="cc"
|
||||
/>
|
||||
Subject
|
||||
Subject:
|
||||
<Input
|
||||
defaultValue={messageOptions.subject}
|
||||
value={messageOptions.subject}
|
||||
onChange={handleConfigChange}
|
||||
name="subject"
|
||||
/>
|
||||
|
||||
@@ -33,6 +33,7 @@ export function EmailOverlayContainer({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [sending, setSending] = useState(false);
|
||||
const defaultEmailFrom = {
|
||||
from: {
|
||||
name: bodyshop.shopname || EmailSettings.fromNameDefault,
|
||||
@@ -45,21 +46,19 @@ export function EmailOverlayContainer({
|
||||
html: "",
|
||||
});
|
||||
|
||||
const handleOk = () => {
|
||||
//sendEmail(messageOptions);
|
||||
axios
|
||||
.post("/sendemail", messageOptions)
|
||||
.then((response) => {
|
||||
console.log(JSON.stringify(response));
|
||||
notification["success"]({ message: t("emails.successes.sent") });
|
||||
toggleEmailOverlayVisible();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(JSON.stringify(error));
|
||||
notification["error"]({
|
||||
message: t("emails.errors.notsent", { message: error.message }),
|
||||
});
|
||||
const handleOk = async () => {
|
||||
setSending(true);
|
||||
try {
|
||||
const emailResponse = await axios.post("/sendemail", messageOptions);
|
||||
notification["success"]({ message: t("emails.successes.sent") });
|
||||
toggleEmailOverlayVisible();
|
||||
} catch (error) {
|
||||
console.log(JSON.stringify(error));
|
||||
notification["error"]({
|
||||
message: t("emails.errors.notsent", { message: error.message }),
|
||||
});
|
||||
}
|
||||
setSending(false);
|
||||
};
|
||||
|
||||
const handleConfigChange = (event) => {
|
||||
@@ -72,6 +71,7 @@ export function EmailOverlayContainer({
|
||||
|
||||
const render = async () => {
|
||||
setLoading(true);
|
||||
console.log("emailConfig", emailConfig);
|
||||
let html = await RenderTemplate(emailConfig.template, bodyshop);
|
||||
setMessageOptions({
|
||||
...emailConfig.messageOptions,
|
||||
@@ -93,7 +93,9 @@ export function EmailOverlayContainer({
|
||||
onOk={handleOk}
|
||||
onCancel={() => {
|
||||
toggleEmailOverlayVisible();
|
||||
}}>
|
||||
}}
|
||||
okButtonProps={{ loading: sending }}
|
||||
>
|
||||
<LoadingSpinner loading={loading}>
|
||||
<EmailOverlayComponent
|
||||
handleConfigChange={handleConfigChange}
|
||||
@@ -102,10 +104,10 @@ export function EmailOverlayContainer({
|
||||
/>
|
||||
<button
|
||||
onClick={() => {
|
||||
console.log(messageOptions.html);
|
||||
navigator.clipboard.writeText(messageOptions.html);
|
||||
}}>
|
||||
Get HTML
|
||||
}}
|
||||
>
|
||||
Copy HTML
|
||||
</button>
|
||||
</LoadingSpinner>
|
||||
</Modal>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { Input } from "antd";
|
||||
import { MailFilled } from "@ant-design/icons";
|
||||
import React, { forwardRef } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
function FormItemEmail(props, ref) {
|
||||
return (
|
||||
<Input
|
||||
{...props}
|
||||
addonAfter={
|
||||
props.email ? (
|
||||
<a href={`mailto:${props.email}`}>
|
||||
props.defaultValue ? (
|
||||
<a href={`mailto:${props.defaultValue}`}>
|
||||
<MailFilled />
|
||||
</a>
|
||||
) : (
|
||||
|
||||
@@ -46,7 +46,7 @@ export function JobsDetailHeader({
|
||||
|
||||
const tombstoneTitle = (
|
||||
<div>
|
||||
<Avatar size='large' alt='Vehicle Image' src={CarImage} />
|
||||
<Avatar size="large" alt="Vehicle Image" src={CarImage} />
|
||||
{job.ro_number
|
||||
? `${t("jobs.fields.ro_number")} ${job.ro_number}`
|
||||
: `EST-${job.est_number}`}
|
||||
@@ -57,7 +57,8 @@ export function JobsDetailHeader({
|
||||
<Menu
|
||||
onClick={(e) => {
|
||||
updateJobStatus(e.key);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{bodyshop.md_ro_statuses.statuses.map((item) => (
|
||||
<Menu.Item key={item}>{item}</Menu.Item>
|
||||
))}
|
||||
@@ -65,12 +66,12 @@ export function JobsDetailHeader({
|
||||
);
|
||||
|
||||
const menuExtra = [
|
||||
<Dropdown overlay={statusmenu} key='changestatus'>
|
||||
<Dropdown overlay={statusmenu} key="changestatus">
|
||||
<Button>
|
||||
{t("jobs.actions.changestatus")} <DownCircleFilled />
|
||||
</Button>
|
||||
</Dropdown>,
|
||||
<Badge key='schedule' count={job.appointments_aggregate.aggregate.count}>
|
||||
<Badge key="schedule" count={job.appointments_aggregate.aggregate.count}>
|
||||
<Button
|
||||
//TODO Enabled logic based on status.
|
||||
onClick={() => {
|
||||
@@ -78,15 +79,17 @@ export function JobsDetailHeader({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
jobId: job.id,
|
||||
job: job,
|
||||
},
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.schedule")}
|
||||
</Button>
|
||||
</Badge>,
|
||||
<Button
|
||||
key='convert'
|
||||
type='dashed'
|
||||
key="convert"
|
||||
type="dashed"
|
||||
disabled={job.converted}
|
||||
onClick={() => {
|
||||
mutationConvertJob({
|
||||
@@ -98,11 +101,12 @@ export function JobsDetailHeader({
|
||||
message: t("jobs.successes.converted"),
|
||||
});
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.convert")}
|
||||
</Button>,
|
||||
<JobsDetailHeaderActions key='actions' job={job} refetch={refetch} />,
|
||||
<Button type='primary' key='submit' htmlType='submit'>
|
||||
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />,
|
||||
<Button type="primary" key="submit" htmlType="submit">
|
||||
{t("general.actions.save")}
|
||||
</Button>,
|
||||
];
|
||||
@@ -115,48 +119,53 @@ export function JobsDetailHeader({
|
||||
title={tombstoneTitle}
|
||||
//subTitle={tombstoneSubtitle}
|
||||
tags={
|
||||
<span key='job-status'>
|
||||
{job.status ? <Tag color='blue'>{job.status}</Tag> : null}
|
||||
<span key="job-status">
|
||||
{job.status ? <Tag color="blue">{job.status}</Tag> : null}
|
||||
{job.inproduction ? (
|
||||
<Tag color='#f50'>{t("jobs.labels.inproduction")}</Tag>
|
||||
<Tag color="#f50">{t("jobs.labels.inproduction")}</Tag>
|
||||
) : null}
|
||||
<OwnerTagPopoverComponent job={job} />
|
||||
<VehicleTagPopoverComponent job={job} />
|
||||
<BarcodePopup value={job.id} />
|
||||
</span>
|
||||
}
|
||||
extra={menuExtra}>
|
||||
<Descriptions size='small' column={5}>
|
||||
<Descriptions.Item key='total' label={t("jobs.fields.repairtotal")}>
|
||||
extra={menuExtra}
|
||||
>
|
||||
<Descriptions size="small" column={5}>
|
||||
<Descriptions.Item key="total" label={t("jobs.fields.repairtotal")}>
|
||||
<CurrencyFormatter>{job.clm_total}</CurrencyFormatter>
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item
|
||||
key='custowing'
|
||||
label={t("jobs.fields.customerowing")}>
|
||||
key="custowing"
|
||||
label={t("jobs.fields.customerowing")}
|
||||
>
|
||||
<CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item
|
||||
key='scp'
|
||||
label={t("jobs.fields.specialcoveragepolicy")}>
|
||||
key="scp"
|
||||
label={t("jobs.fields.specialcoveragepolicy")}
|
||||
>
|
||||
<Checkbox checked={job.special_coverage_policy} />
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item
|
||||
key='sched_comp'
|
||||
label={t("jobs.fields.scheduled_completion")}>
|
||||
key="sched_comp"
|
||||
label={t("jobs.fields.scheduled_completion")}
|
||||
>
|
||||
{job.scheduled_completion ? (
|
||||
<Moment format='MM/DD/YYYY'>{job.scheduled_completion}</Moment>
|
||||
<Moment format="MM/DD/YYYY">{job.scheduled_completion}</Moment>
|
||||
) : null}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item key='servicecar' label={t("jobs.fields.servicecar")}>
|
||||
<Descriptions.Item key="servicecar" label={t("jobs.fields.servicecar")}>
|
||||
{job.cccontracts &&
|
||||
job.cccontracts.map((item) => (
|
||||
<Link
|
||||
key={item.id}
|
||||
to={`/manage/courtesycars/contracts/${item.id}`}>
|
||||
to={`/manage/courtesycars/contracts/${item.id}`}
|
||||
>
|
||||
<div>{`${item.agreementnumber} - ${item.start} - ${item.scheduledreturn}`}</div>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
@@ -15,16 +15,16 @@ export default function ScheduleCalendarWrapperComponent({
|
||||
refetch,
|
||||
defaultView,
|
||||
setDateRangeCallback,
|
||||
date,
|
||||
...otherProps
|
||||
}) {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
|
||||
return (
|
||||
<Calendar
|
||||
events={data}
|
||||
defaultView={search.view || defaultView || "week"}
|
||||
date={new Date(search.date || Date.now())}
|
||||
date={new Date(date || search.date || Date.now())}
|
||||
onNavigate={(date, view, action) => {
|
||||
search.date = date.toISOString().substr(0, 10);
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
@@ -45,6 +45,7 @@ export default function ScheduleCalendarWrapperComponent({
|
||||
components={{
|
||||
event: (e) => Event({ event: e.event, refetch: refetch }),
|
||||
header: HeaderComponent,
|
||||
toolbar: null,
|
||||
}}
|
||||
{...otherProps}
|
||||
/>
|
||||
|
||||
@@ -6,15 +6,13 @@ import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/sched
|
||||
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"
|
||||
views={["day"]}
|
||||
style={{ height: "40vh" }}
|
||||
defaultDate={new Date(day)}
|
||||
//onNavigate={e => console.log("e", e)}
|
||||
date={day}
|
||||
/>
|
||||
);
|
||||
else return <div>{t("appointments.labels.nodateselected")}</div>;
|
||||
|
||||
@@ -4,22 +4,24 @@ import { useQuery } from "@apollo/react-hooks";
|
||||
import { QUERY_APPOINTMENT_BY_DATE } from "../../graphql/appointments.queries";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import moment from "moment";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
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")
|
||||
end: moment(day).endOf("day"),
|
||||
},
|
||||
skip: !day,
|
||||
fetchPolicy: "network-only"
|
||||
skip: !!!day,
|
||||
fetchPolicy: "network-only",
|
||||
});
|
||||
|
||||
const { t } = useTranslation();
|
||||
if (!!!day) return <div>{t("appointments.labels.nodateselected")}</div>;
|
||||
if (loading) return <LoadingSkeleton paragraph={{ rows: 4 }} />;
|
||||
if (error) return <div>{error.message}</div>;
|
||||
let normalizedData;
|
||||
|
||||
if (data) {
|
||||
normalizedData = data.appointments.map(e => {
|
||||
normalizedData = data.appointments.map((e) => {
|
||||
//Required becuase Hasura returns a string instead of a date object.
|
||||
return Object.assign(
|
||||
{},
|
||||
@@ -31,6 +33,6 @@ export default function ScheduleDayViewContainer({ day }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<ScheduleDayViewComponent data={data ? normalizedData : null} day={day} />
|
||||
<ScheduleDayViewComponent data={data ? normalizedData : []} day={day} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Checkbox, Col, Row, Tabs } from "antd";
|
||||
import { Checkbox, Col, Row, Input, Button } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
|
||||
@@ -6,6 +6,7 @@ import ScheduleDayViewContainer from "../schedule-day-view/schedule-day-view.con
|
||||
import ScheduleExistingAppointmentsList from "../schedule-existing-appointments-list/schedule-existing-appointments-list.component";
|
||||
import axios from "axios";
|
||||
import { auth } from "../../firebase/firebase.utils";
|
||||
import EmailInput from "../form-items-formatted/email-form-item.component";
|
||||
|
||||
export default function ScheduleJobModalComponent({
|
||||
existingAppointments,
|
||||
@@ -37,31 +38,24 @@ export default function ScheduleJobModalComponent({
|
||||
return (
|
||||
<Row>
|
||||
<Col span={14}>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<Tabs.TabPane tab="SMART Scheduling" key="auto">
|
||||
Automatic Job Selection.
|
||||
<button onClick={handleAuto}>Get dates.</button>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="Manual Scheduling" key="manual">
|
||||
<Row>
|
||||
Manual Job Selection Scheduled Time
|
||||
<div style={{ height: "300px" }}>
|
||||
<DateTimePicker
|
||||
value={appData.start}
|
||||
onChange={(e) => {
|
||||
setAppData({ ...appData, start: e });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Row>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<div style={{ display: "flex", alignContent: "middle" }}>
|
||||
<strong>{t("appointments.fields.time")}</strong>
|
||||
<DateTimePicker
|
||||
value={appData.start}
|
||||
onChange={(e) => {
|
||||
setAppData({ ...appData, start: e });
|
||||
}}
|
||||
/>
|
||||
<Button onClick={handleAuto}>
|
||||
{t("appointments.actions.smartscheduling")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{t("appointments.labels.history")}
|
||||
<ScheduleExistingAppointmentsList
|
||||
existingAppointments={existingAppointments}
|
||||
/>
|
||||
{
|
||||
//TODO Build out notifications.
|
||||
}
|
||||
|
||||
<Checkbox
|
||||
defaultChecked={formData.notifyCustomer}
|
||||
onChange={(e) =>
|
||||
@@ -70,6 +64,11 @@ export default function ScheduleJobModalComponent({
|
||||
>
|
||||
{t("jobs.labels.appointmentconfirmation")}
|
||||
</Checkbox>
|
||||
<EmailInput
|
||||
defaultValue={formData.email}
|
||||
title={t("owner.fields.ownr_ea")}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<ScheduleDayViewContainer day={appData.start} />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import ScheduleJobModalComponent from "./schedule-job-modal.component";
|
||||
import { useMutation, useQuery } from "@apollo/react-hooks";
|
||||
import {
|
||||
@@ -9,12 +9,13 @@ import moment from "moment";
|
||||
import { notification, Modal } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_JOBS } from "../../graphql/jobs.queries";
|
||||
|
||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { selectSchedule } from "../../redux/modals/modals.selectors";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -22,22 +23,37 @@ const mapStateToProps = createStructuredSelector({
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("schedule")),
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
});
|
||||
export function ScheduleJobModalContainer({
|
||||
scheduleModal,
|
||||
bodyshop,
|
||||
toggleModalVisible,
|
||||
setEmailOptions,
|
||||
}) {
|
||||
const { visible, context, actions } = scheduleModal;
|
||||
const { jobId } = context;
|
||||
const { jobId, job } = context;
|
||||
const { refetch } = actions;
|
||||
|
||||
const [appData, setAppData] = useState({
|
||||
start: null,
|
||||
});
|
||||
|
||||
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
|
||||
const [updateJobStatus] = useMutation(UPDATE_JOBS);
|
||||
const [formData, setFormData] = useState({ notifyCustomer: false });
|
||||
const [formData, setFormData] = useState({
|
||||
notifyCustomer: false,
|
||||
email: (job && job.ownr_ea) || "",
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setFormData({
|
||||
notifyCustomer: !!(job && job.ownr_ea),
|
||||
email: (job && job.ownr_ea) || "",
|
||||
start: null,
|
||||
});
|
||||
}, [job, setFormData]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const existingAppointments = useQuery(QUERY_APPOINTMENTS_BY_JOBID, {
|
||||
@@ -47,47 +63,67 @@ export function ScheduleJobModalContainer({
|
||||
});
|
||||
|
||||
//TODO Customize the amount of minutes it will add.
|
||||
const handleOk = () => {
|
||||
insertAppointment({
|
||||
const handleOk = async () => {
|
||||
const appt = await insertAppointment({
|
||||
variables: {
|
||||
app: {
|
||||
...appData,
|
||||
jobid: jobId,
|
||||
bodyshopid: bodyshop.id,
|
||||
end: moment(appData.start).add(60, "minutes"),
|
||||
end: moment(appData.start).add(bodyshop.appt_length || 60, "minutes"),
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
updateJobStatus({
|
||||
variables: {
|
||||
jobIds: [jobId],
|
||||
fields: {
|
||||
status: bodyshop.md_ro_statuses.default_scheduled,
|
||||
date_scheduled: new Date(),
|
||||
scheduled_in: appData.start,
|
||||
},
|
||||
},
|
||||
}).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!");
|
||||
}
|
||||
toggleModalVisible();
|
||||
if (refetch) refetch();
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!!appt.errors) {
|
||||
notification["error"]({
|
||||
message: t("appointments.errors.saving", {
|
||||
message: JSON.stringify(appt.errors),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.created"),
|
||||
});
|
||||
if (jobId) {
|
||||
const jobUpdate = await updateJobStatus({
|
||||
variables: {
|
||||
jobIds: [jobId],
|
||||
fields: {
|
||||
status: bodyshop.md_ro_statuses.default_scheduled,
|
||||
date_scheduled: new Date(),
|
||||
scheduled_in: appData.start,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!!jobUpdate.errors) {
|
||||
notification["error"]({
|
||||
message: t("appointments.errors.saving", {
|
||||
message: error.message,
|
||||
message: JSON.stringify(jobUpdate.errors),
|
||||
}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
toggleModalVisible();
|
||||
if (formData.notifyCustomer) {
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
to: formData.email,
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
name: TemplateList.appointment_confirmation.key,
|
||||
variables: {
|
||||
id: appt.data.insert_appointments.returning[0].id,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
if (refetch) refetch();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -9,71 +9,92 @@ export default function ShopInfoComponent({ form }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<Button type='primary' htmlType='submit'>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Collapse defaultActiveKey='shopinfo'>
|
||||
<Collapse.Panel key='shopinfo' header={t("bodyshop.labels.shopinfo")}>
|
||||
<Form.Item label={t("bodyshop.fields.shopname")} name='shopname'>
|
||||
<Collapse defaultActiveKey="shopinfo">
|
||||
<Collapse.Panel key="shopinfo" header={t("bodyshop.labels.shopinfo")}>
|
||||
<Form.Item label={t("bodyshop.fields.shopname")} name="shopname">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("bodyshop.fields.address1")} name='address1'>
|
||||
<Form.Item label={t("bodyshop.fields.address1")} name="address1">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("bodyshop.fields.address2")} name='address2'>
|
||||
<Form.Item label={t("bodyshop.fields.address2")} name="address2">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("bodyshop.fields.city")} name='city'>
|
||||
<Form.Item label={t("bodyshop.fields.city")} name="city">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("bodyshop.fields.state")} name='state'>
|
||||
<Form.Item label={t("bodyshop.fields.state")} name="state">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("bodyshop.fields.zip_post")} name='zip_post'>
|
||||
<Form.Item label={t("bodyshop.fields.zip_post")} name="zip_post">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("bodyshop.fields.country")} name='country'>
|
||||
<Form.Item label={t("bodyshop.fields.country")} name="country">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("bodyshop.fields.email")} name='email'>
|
||||
<Form.Item label={t("bodyshop.fields.email")} name="email">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.federal_tax_id")}
|
||||
name='federal_tax_id'>
|
||||
name="federal_tax_id"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.insurance_vendor_id")}
|
||||
name='insurance_vendor_id'>
|
||||
name="insurance_vendor_id"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.logo_img_path")}
|
||||
name='logo_img_path'>
|
||||
name="logo_img_path"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.state_tax_id")}
|
||||
name='state_tax_id'>
|
||||
name="state_tax_id"
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
||||
name={["invoice_tax_rates", "federal_tax_rate"]}>
|
||||
name={["invoice_tax_rates", "federal_tax_rate"]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
||||
name={["invoice_tax_rates", "state_tax_rate"]}>
|
||||
name={["invoice_tax_rates", "state_tax_rate"]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
||||
name={["invoice_tax_rates", "local_tax_rate"]}>
|
||||
name={["invoice_tax_rates", "local_tax_rate"]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.appt_length")}
|
||||
name={"appt_length"}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={15} precisio={0} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.accountingtiers")}
|
||||
rules={[
|
||||
@@ -82,7 +103,8 @@ export default function ShopInfoComponent({ form }) {
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["accountingconfig", "tiers"]}>
|
||||
name={["accountingconfig", "tiers"]}
|
||||
>
|
||||
<Radio.Group>
|
||||
<Radio value={2}>2</Radio>
|
||||
<Radio value={3}>3</Radio>
|
||||
@@ -101,13 +123,15 @@ export default function ShopInfoComponent({ form }) {
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
name={["accountingconfig", "twotierpref"]}>
|
||||
name={["accountingconfig", "twotierpref"]}
|
||||
>
|
||||
<Radio.Group
|
||||
disabled={
|
||||
form.getFieldValue(["accountingconfig", "tiers"]) === 3
|
||||
}>
|
||||
<Radio value='name'>{t("bodyshop.labels.2tiername")}</Radio>
|
||||
<Radio value='source'>
|
||||
}
|
||||
>
|
||||
<Radio value="name">{t("bodyshop.labels.2tiername")}</Radio>
|
||||
<Radio value="source">
|
||||
{t("bodyshop.labels.2tiersource")}
|
||||
</Radio>
|
||||
</Radio.Group>
|
||||
@@ -117,18 +141,21 @@ export default function ShopInfoComponent({ form }) {
|
||||
</Form.Item>
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel
|
||||
key='roStatus'
|
||||
header={t("bodyshop.labels.jobstatuses")}>
|
||||
key="roStatus"
|
||||
header={t("bodyshop.labels.jobstatuses")}
|
||||
>
|
||||
<ShopInfoROStatusComponent form={form} />
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel
|
||||
key='orderStatus'
|
||||
header={t("bodyshop.labels.orderstatuses")}>
|
||||
key="orderStatus"
|
||||
header={t("bodyshop.labels.orderstatuses")}
|
||||
>
|
||||
<ShopInfoOrderStatusComponent form={form} />
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel
|
||||
key='responsibilityCenters'
|
||||
header={t("bodyshop.labels.responsibilitycenters.title")}>
|
||||
key="responsibilityCenters"
|
||||
header={t("bodyshop.labels.responsibilitycenters.title")}
|
||||
>
|
||||
<ShopInfoResponsibilityCenterComponent form={form} />
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
|
||||
Reference in New Issue
Block a user