Merge remote-tracking branch 'origin/release/2023-03-24' into feature/america
This commit is contained in:
@@ -42,6 +42,7 @@ export function ChatConversationContainer({ bodyshop, selectedConversation }) {
|
||||
MARK_MESSAGES_AS_READ_BY_CONVERSATION,
|
||||
{
|
||||
variables: { conversationId: selectedConversation },
|
||||
refetchQueries: ["UNREAD_CONVERSATION_COUNT"],
|
||||
update(cache) {
|
||||
cache.modify({
|
||||
id: cache.identify({
|
||||
|
||||
@@ -10,7 +10,10 @@ import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { CONVERSATION_LIST_QUERY } from "../../graphql/conversations.queries";
|
||||
import {
|
||||
CONVERSATION_LIST_QUERY,
|
||||
UNREAD_CONVERSATION_COUNT,
|
||||
} from "../../graphql/conversations.queries";
|
||||
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
|
||||
import {
|
||||
selectChatVisible,
|
||||
@@ -37,9 +40,17 @@ export function ChatPopupComponent({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [pollInterval, setpollInterval] = useState(0);
|
||||
|
||||
const { data: unreadData } = useQuery(UNREAD_CONVERSATION_COUNT, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
...(pollInterval > 0 ? { pollInterval } : {}),
|
||||
});
|
||||
|
||||
const { loading, data, refetch, called } = useQuery(CONVERSATION_LIST_QUERY, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
skip: !chatVisible,
|
||||
...(pollInterval > 0 ? { pollInterval } : {}),
|
||||
});
|
||||
|
||||
@@ -57,12 +68,14 @@ export function ChatPopupComponent({
|
||||
if (called && chatVisible) refetch();
|
||||
}, [chatVisible, called, refetch]);
|
||||
|
||||
const unreadCount = data
|
||||
? data.conversations.reduce(
|
||||
(acc, val) => val.messages_aggregate.aggregate.count + acc,
|
||||
0
|
||||
)
|
||||
: 0;
|
||||
// const unreadCount = data
|
||||
// ? data.conversations.reduce(
|
||||
// (acc, val) => val.messages_aggregate.aggregate.count + acc,
|
||||
// 0
|
||||
// )
|
||||
// : 0;
|
||||
|
||||
const unreadCount = unreadData?.messages_aggregate.aggregate.count || 0;
|
||||
|
||||
return (
|
||||
<Badge count={unreadCount}>
|
||||
|
||||
@@ -4,7 +4,7 @@ import moment from "moment";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import ContractLicenseDecodeButton from "../contract-license-decode-button/contract-license-decode-button.component";
|
||||
//import ContractLicenseDecodeButton from "../contract-license-decode-button/contract-license-decode-button.component";
|
||||
import ContractStatusSelector from "../contract-status-select/contract-status-select.component";
|
||||
import ContractsRatesChangeButton from "../contracts-rates-change-button/contracts-rates-change-button.component";
|
||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||
@@ -165,7 +165,9 @@ export default function ContractFormComponent({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<ContractLicenseDecodeButton form={form} />
|
||||
{
|
||||
//<ContractLicenseDecodeButton form={form} />
|
||||
}
|
||||
</Space>
|
||||
</div>
|
||||
<LayoutFormRow header={t("contracts.labels.driverinformation")}>
|
||||
|
||||
@@ -3,9 +3,11 @@ import {
|
||||
Button,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Form,
|
||||
Menu,
|
||||
notification,
|
||||
Popover,
|
||||
Select,
|
||||
Space,
|
||||
} from "antd";
|
||||
import parsePhoneNumber from "libphonenumber-js";
|
||||
@@ -59,7 +61,10 @@ export function ScheduleEventComponent({
|
||||
|
||||
const blockContent = (
|
||||
<div>
|
||||
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
|
||||
<Button
|
||||
onClick={() => handleCancel({ id: event.id })}
|
||||
disabled={event.arrived}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
@@ -203,10 +208,46 @@ export function ScheduleEventComponent({
|
||||
<Button>{t("appointments.actions.sendreminder")}</Button>
|
||||
</Dropdown>
|
||||
) : null}
|
||||
|
||||
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={event.arrived}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={({ lost_sale_reason }) => {
|
||||
handleCancel({ id: event.id, lost_sale_reason });
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button htmlType="submit">
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
// onClick={() => handleCancel(event.id)}
|
||||
disabled={event.arrived}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Popover>
|
||||
{event.isintake ? (
|
||||
<Button
|
||||
disabled={event.arrived}
|
||||
@@ -249,7 +290,7 @@ export function ScheduleEventComponent({
|
||||
const RegularEvent = event.isintake ? (
|
||||
<Space
|
||||
wrap
|
||||
size='small'
|
||||
size="small"
|
||||
style={{
|
||||
backgroundColor:
|
||||
event.color && event.color.hex ? event.color.hex : event.color,
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID);
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
const handleCancel = async (id) => {
|
||||
const handleCancel = async ({ id, lost_sale_reason }) => {
|
||||
logImEXEvent("schedule_cancel_appt");
|
||||
|
||||
const cancelAppt = await cancelAppointment({
|
||||
@@ -38,7 +38,8 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion:null,
|
||||
scheduled_completion: null,
|
||||
lost_sale_reason,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -105,6 +105,9 @@ export function JobLinesComponent({
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
onCell: (record) => ({
|
||||
className: record.manual_line && "job-line-manual",
|
||||
style: {
|
||||
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
|
||||
},
|
||||
}),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||
@@ -332,7 +335,7 @@ export function JobLinesComponent({
|
||||
onClick={() => {
|
||||
setJobLineEditContext({
|
||||
actions: { refetch: refetch, submit: form && form.submit },
|
||||
context: record,
|
||||
context: { ...record, jobid: job.id },
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -14,8 +14,12 @@ import UndefinedToNull from "../../utils/undefinedtonull";
|
||||
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
|
||||
import Axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobLineEditModal: selectJobLineEditModal,
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")),
|
||||
@@ -24,7 +28,13 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
function JobLinesUpsertModalContainer({
|
||||
jobLineEditModal,
|
||||
toggleModalVisible,
|
||||
bodyshop,
|
||||
}) {
|
||||
const { CriticalPartsScanning } = useTreatments(
|
||||
["CriticalPartsScanning"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
|
||||
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
|
||||
@@ -109,6 +119,9 @@ function JobLinesUpsertModalContainer({
|
||||
}
|
||||
toggleModalVisible();
|
||||
}
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(jobLineEditModal.context.jobid);
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ import {
|
||||
useApolloClient,
|
||||
useLazyQuery,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQuery
|
||||
} from "@apollo/client";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Col, notification, Row } from "antd";
|
||||
import Axios from "axios";
|
||||
import Dinero from "dinero.js";
|
||||
@@ -19,7 +20,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
DELETE_AVAILABLE_JOB,
|
||||
QUERY_AVAILABLE_JOBS,
|
||||
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK,
|
||||
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK
|
||||
} from "../../graphql/available-jobs.queries";
|
||||
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
|
||||
@@ -27,10 +28,11 @@ import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
selectCurrentUser
|
||||
} from "../../redux/user/user.selectors";
|
||||
import confirmDialog from "../../utils/asyncConfirm";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import CriticalPartsScan from "../../utils/criticalPartsScan";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
|
||||
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
|
||||
@@ -54,6 +56,11 @@ export function JobsAvailableContainer({
|
||||
currentUser,
|
||||
insertAuditTrail,
|
||||
}) {
|
||||
const { CriticalPartsScanning } = useTreatments(
|
||||
["CriticalPartsScanning"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
@@ -160,6 +167,9 @@ export function JobsAvailableContainer({
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
|
||||
}
|
||||
notification["success"]({
|
||||
message: t("jobs.successes.created"),
|
||||
onClick: () => {
|
||||
@@ -247,7 +257,9 @@ export function JobsAvailableContainer({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (CriticalPartsScanning.treatment === "on") {
|
||||
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
|
||||
}
|
||||
if (updateResult.errors) {
|
||||
//error while inserting
|
||||
notification["error"]({
|
||||
|
||||
@@ -43,7 +43,7 @@ export function JobsConvertButton({
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const handleConvert = async ({ employee_csr, ...values }) => {
|
||||
const handleConvert = async ({ employee_csr, category, ...values }) => {
|
||||
if (parentFormIsFieldsTouched()) {
|
||||
alert(t("jobs.labels.savebeforeconversion"));
|
||||
return;
|
||||
@@ -55,6 +55,7 @@ export function JobsConvertButton({
|
||||
job: {
|
||||
converted: true,
|
||||
...(bodyshop.enforce_conversion_csr ? { employee_csr } : {}),
|
||||
...(bodyshop.enforce_conversion_category ? { category } : {}),
|
||||
...values,
|
||||
},
|
||||
},
|
||||
@@ -94,6 +95,7 @@ export function JobsConvertButton({
|
||||
driveable: true,
|
||||
towin: false,
|
||||
employee_csr: job.employee_csr,
|
||||
category: job.category,
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
@@ -197,6 +199,26 @@ export function JobsConvertButton({
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
{bodyshop.enforce_conversion_category && (
|
||||
<Form.Item
|
||||
name={"category"}
|
||||
label={t("jobs.fields.category")}
|
||||
rules={[
|
||||
{
|
||||
required: bodyshop.enforce_conversion_category,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select allowClear>
|
||||
{bodyshop.md_categories.map((s) => (
|
||||
<Select.Option key={s} value={s}>
|
||||
{s}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item
|
||||
label={t("jobs.fields.ca_gst_registrant")}
|
||||
name="ca_gst_registrant"
|
||||
|
||||
@@ -9,7 +9,11 @@ const colSpan = {
|
||||
lg: { span: 12 },
|
||||
};
|
||||
|
||||
export default function JobsCreateVehicleInfoComponent({ loading, vehicles }) {
|
||||
export default function JobsCreateVehicleInfoComponent({
|
||||
loading,
|
||||
vehicles,
|
||||
form,
|
||||
}) {
|
||||
const [state, setState] = useContext(JobCreateContext);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
@@ -58,7 +62,7 @@ export default function JobsCreateVehicleInfoComponent({ loading, vehicles }) {
|
||||
/>
|
||||
</Col>
|
||||
<Col {...colSpan}>
|
||||
<JobsCreateVehicleInfoNewComponent />
|
||||
<JobsCreateVehicleInfoNewComponent form={form}/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ export default function JobsCreateVehicleInfoContainer({ form }) {
|
||||
<JobsCreateVehicleInfoComponent
|
||||
loading={loading}
|
||||
vehicles={data ? data.search_vehicles : null}
|
||||
form={form}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import { useTranslation } from "react-i18next";
|
||||
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import JobsCreateVehicleInfoPredefined from "./jobs-create-vehicle-info.predefined.component";
|
||||
|
||||
export default function JobsCreateVehicleInfoNewComponent() {
|
||||
export default function JobsCreateVehicleInfoNewComponent({ form }) {
|
||||
const [state] = useContext(JobCreateContext);
|
||||
|
||||
const { t } = useTranslation();
|
||||
@@ -25,7 +26,7 @@ export default function JobsCreateVehicleInfoNewComponent() {
|
||||
<Input disabled={!state.vehicle.new} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow>
|
||||
<LayoutFormRow grow noDivider>
|
||||
<Form.Item
|
||||
label={t("vehicles.fields.v_color")}
|
||||
name={["vehicle", "data", "v_color"]}
|
||||
@@ -52,8 +53,9 @@ export default function JobsCreateVehicleInfoNewComponent() {
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
<LayoutFormRow grow>
|
||||
<LayoutFormRow grow noDivider>
|
||||
<Form.Item
|
||||
span={10}
|
||||
label={t("vehicles.fields.v_make_desc")}
|
||||
name={["vehicle", "data", "v_make_desc"]}
|
||||
rules={[
|
||||
@@ -66,6 +68,7 @@ export default function JobsCreateVehicleInfoNewComponent() {
|
||||
<Input disabled={!state.vehicle.new} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
span={11}
|
||||
label={t("vehicles.fields.v_model_desc")}
|
||||
name={["vehicle", "data", "v_model_desc"]}
|
||||
rules={[
|
||||
@@ -77,6 +80,11 @@ export default function JobsCreateVehicleInfoNewComponent() {
|
||||
>
|
||||
<Input disabled={!state.vehicle.new} />
|
||||
</Form.Item>
|
||||
<JobsCreateVehicleInfoPredefined
|
||||
disabled={!state.vehicle.new}
|
||||
form={form}
|
||||
span={1}
|
||||
/>
|
||||
</LayoutFormRow>
|
||||
|
||||
<LayoutFormRow header={t("vehicles.forms.registration")} grow>
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { PlusOutlined, SearchOutlined } from "@ant-design/icons";
|
||||
import { Button, Input, Popover, Table } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PredefinedVehicles from "./predefined-vehicles.js";
|
||||
|
||||
export default function JobsCreateVehicleInfoPredefined({ disabled, form }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
const { t } = useTranslation();
|
||||
const handleOpenChange = (newOpen) => {
|
||||
setOpen(newOpen);
|
||||
setSearch("");
|
||||
};
|
||||
const filteredPredefinedVehicles =
|
||||
search === ""
|
||||
? PredefinedVehicles
|
||||
: PredefinedVehicles.filter(
|
||||
(v) =>
|
||||
v.make.toLowerCase().includes(search.toLowerCase()) ||
|
||||
v.model.toLowerCase().includes(search.toLowerCase())
|
||||
);
|
||||
|
||||
const popContent = () => (
|
||||
<div>
|
||||
<Table
|
||||
size="small"
|
||||
title={() => <Input.Search onSearch={(value) => setSearch(value)} />}
|
||||
dataSource={filteredPredefinedVehicles}
|
||||
columns={[
|
||||
{
|
||||
dataIndex: "make",
|
||||
key: "make",
|
||||
title: t("vehicles.fields.v_make_desc"),
|
||||
},
|
||||
{
|
||||
dataIndex: "model",
|
||||
key: "model",
|
||||
title: t("vehicles.fields.v_model_desc"),
|
||||
},
|
||||
{
|
||||
dataIndex: "select",
|
||||
key: "select",
|
||||
title: t("general.labels.actions"),
|
||||
render: (value, record) => (
|
||||
<Button
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
form.setFieldsValue({
|
||||
vehicle: {
|
||||
data: {
|
||||
v_make_desc: record.make,
|
||||
v_model_desc: record.model,
|
||||
},
|
||||
},
|
||||
});
|
||||
setOpen(false);
|
||||
setSearch("");
|
||||
}}
|
||||
>
|
||||
<PlusOutlined />
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<Popover
|
||||
content={popContent}
|
||||
trigger="click"
|
||||
open={open}
|
||||
placement="left"
|
||||
onOpenChange={handleOpenChange}
|
||||
destroyTooltipOnHide
|
||||
>
|
||||
<SearchOutlined style={{ cursor: "pointer" }} />
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -289,6 +289,12 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
>
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
name="lost_sale_reason"
|
||||
>
|
||||
<Input disabled={jobRO} allowClear />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { DownCircleFilled } from "@ant-design/icons";
|
||||
import { useApolloClient, useMutation } from "@apollo/client";
|
||||
import { Button, Dropdown, Menu, notification, Popconfirm } from "antd";
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
Form,
|
||||
Menu,
|
||||
notification,
|
||||
Popconfirm,
|
||||
Popover,
|
||||
Select,
|
||||
} from "antd";
|
||||
import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -130,35 +139,63 @@ export function JobsDetailHeaderActions({
|
||||
<Menu.Item
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
>
|
||||
<Popconfirm
|
||||
title={t("general.labels.areyousure")}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
<Popover
|
||||
trigger="click"
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
onConfirm={async () => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!jobUpdate.errors) {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.canceled"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
getPopupContainer={(trigger) => trigger.parentNode}
|
||||
content={
|
||||
<Form
|
||||
layout="vertical"
|
||||
onFinish={async ({ lost_sale_reason }) => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
lost_sale_reason,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!jobUpdate.errors) {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.canceled"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name="lost_sale_reason"
|
||||
label={t("jobs.fields.lost_sale_reason")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
|
||||
label: lsr,
|
||||
value: lsr,
|
||||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
htmlType="submit"
|
||||
disabled={
|
||||
job.status !== bodyshop.md_ro_statuses.default_scheduled
|
||||
}
|
||||
>
|
||||
{t("appointments.actions.cancel")}
|
||||
</Button>
|
||||
</Form>
|
||||
}
|
||||
>
|
||||
{t("menus.jobsactions.cancelallappointments")}
|
||||
</Popconfirm>
|
||||
</Popover>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
disabled={
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import cleanAxios from "../../utils/CleanAxios";
|
||||
import formatBytes from "../../utils/formatbytes";
|
||||
import yauzl from "yauzl";
|
||||
//import yauzl from "yauzl";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
@@ -69,44 +69,44 @@ export function JobsDocumentsDownloadButton({
|
||||
setDownload(null);
|
||||
if (Direct_Media_Download.treatment === "on") {
|
||||
try {
|
||||
const parentDir = await window.showDirectoryPicker({
|
||||
id: "media",
|
||||
startIn: "downloads",
|
||||
});
|
||||
// const parentDir = await window.showDirectoryPicker({
|
||||
// id: "media",
|
||||
// startIn: "downloads",
|
||||
// });
|
||||
|
||||
const directory = await parentDir.getDirectoryHandle(identifier, {
|
||||
create: true,
|
||||
});
|
||||
// const directory = await parentDir.getDirectoryHandle(identifier, {
|
||||
// create: true,
|
||||
// });
|
||||
|
||||
yauzl.fromBuffer(
|
||||
Buffer.from(theDownloadedZip.data),
|
||||
{},
|
||||
(err, zipFile) => {
|
||||
if (err) throw err;
|
||||
zipFile.on("entry", (entry) => {
|
||||
zipFile.openReadStream(entry, async (readErr, readStream) => {
|
||||
if (readErr) {
|
||||
zipFile.close();
|
||||
throw readErr;
|
||||
}
|
||||
if (err) throw err;
|
||||
let fileSystemHandle = await directory.getFileHandle(
|
||||
entry.fileName,
|
||||
{
|
||||
create: true,
|
||||
}
|
||||
);
|
||||
const writable = await fileSystemHandle.createWritable();
|
||||
readStream.on("data", async function (chunk) {
|
||||
await writable.write(chunk);
|
||||
});
|
||||
readStream.on("end", async function () {
|
||||
await writable.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
// yauzl.fromBuffer(
|
||||
// Buffer.from(theDownloadedZip.data),
|
||||
// {},
|
||||
// (err, zipFile) => {
|
||||
// if (err) throw err;
|
||||
// zipFile.on("entry", (entry) => {
|
||||
// zipFile.openReadStream(entry, async (readErr, readStream) => {
|
||||
// if (readErr) {
|
||||
// zipFile.close();
|
||||
// throw readErr;
|
||||
// }
|
||||
// if (err) throw err;
|
||||
// let fileSystemHandle = await directory.getFileHandle(
|
||||
// entry.fileName,
|
||||
// {
|
||||
// create: true,
|
||||
// }
|
||||
// );
|
||||
// const writable = await fileSystemHandle.createWritable();
|
||||
// readStream.on("data", async function (chunk) {
|
||||
// await writable.write(chunk);
|
||||
// });
|
||||
// readStream.on("end", async function () {
|
||||
// await writable.close();
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
// );
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
standardMediaDownload(theDownloadedZip.data);
|
||||
|
||||
@@ -260,6 +260,19 @@ export function JobsList({ bodyshop }) {
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.ins_co_nm)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s,
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -272,6 +272,19 @@ export function JobsReadyList({ bodyshop }) {
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
filters:
|
||||
(jobs &&
|
||||
jobs
|
||||
.map((j) => j.ins_co_nm)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: s,
|
||||
value: [s],
|
||||
};
|
||||
})) ||
|
||||
[],
|
||||
onFilter: (value, record) => value.includes(record.ins_co_nm),
|
||||
responsive: ["md"],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,15 +1,40 @@
|
||||
import { Button, Form, notification, PageHeader } from "antd";
|
||||
import { Button, Form, notification, PageHeader, Popconfirm } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { UPDATE_OWNER } from "../../graphql/owners.queries";
|
||||
import { DELETE_OWNER, UPDATE_OWNER } from "../../graphql/owners.queries";
|
||||
import OwnerDetailFormComponent from "./owner-detail-form.component";
|
||||
|
||||
function OwnerDetailFormContainer({ owner, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const history = useHistory();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateOwner] = useMutation(UPDATE_OWNER);
|
||||
const [deleteOwner] = useMutation(DELETE_OWNER);
|
||||
|
||||
const handleDelete = async () => {
|
||||
setLoading(true);
|
||||
const result = await deleteOwner({
|
||||
variables: { id: owner.id },
|
||||
});
|
||||
console.log(result);
|
||||
if (result.errors) {
|
||||
notification["error"]({
|
||||
message: t("owners.errors.deleting", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
setLoading(false);
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("owners.successes.delete"),
|
||||
});
|
||||
setLoading(false);
|
||||
history.push(`/manage/owners`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
@@ -41,15 +66,29 @@ function OwnerDetailFormContainer({ owner, refetch }) {
|
||||
<>
|
||||
<PageHeader
|
||||
title={t("menus.header.owners")}
|
||||
extra={
|
||||
extra={[
|
||||
<Popconfirm
|
||||
trigger="click"
|
||||
onConfirm={handleDelete}
|
||||
disabled={owner.jobs.length !== 0}
|
||||
title={t("owners.labels.deleteconfirm")}
|
||||
>
|
||||
<Button
|
||||
type="danger"
|
||||
loading={loading}
|
||||
disabled={owner.jobs.length !== 0}
|
||||
>
|
||||
{t("general.actions.delete")}
|
||||
</Button>
|
||||
</Popconfirm>,
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
}
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
<Form
|
||||
form={form}
|
||||
|
||||
@@ -124,7 +124,11 @@ function PaymentModalContainer({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) form.resetFields();
|
||||
if (visible) {
|
||||
form.resetFields();
|
||||
form.resetFields();
|
||||
form.setFieldsValue(context);
|
||||
}
|
||||
}, [visible, form, context]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -139,6 +143,7 @@ function PaymentModalContainer({
|
||||
: t("payments.labels.edit")
|
||||
}
|
||||
visible={visible}
|
||||
destroyOnClose
|
||||
okText={t("general.actions.save")}
|
||||
onOk={() => form.submit()}
|
||||
width="50%"
|
||||
|
||||
@@ -55,6 +55,7 @@ export function ProductionListTable({
|
||||
const assoc = bodyshop.associations.find(
|
||||
(a) => a.useremail === currentUser.email
|
||||
);
|
||||
|
||||
if (assoc) {
|
||||
await updateDefaultProdView({
|
||||
variables: { assocId: assoc.id, view: value },
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function ProfileShopsComponent({
|
||||
),
|
||||
},
|
||||
];
|
||||
console.log("🚀 ~ file: profile-shops.component.jsx:45 ~ data", data);
|
||||
|
||||
const filteredData =
|
||||
search === ""
|
||||
? data
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from "react";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import {
|
||||
QUERY_ALL_ASSOCIATIONS,
|
||||
UPDATE_ASSOCIATION,
|
||||
UPDATE_ACTIVE_ASSOCIATION,
|
||||
} from "../../graphql/associations.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import ProfileShopsComponent from "./profile-shops.component";
|
||||
@@ -13,9 +13,13 @@ import { getToken } from "firebase/messaging";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -25,14 +29,18 @@ export default connect(
|
||||
mapDispatchToProps
|
||||
)(ProfileShopsContainer);
|
||||
|
||||
export function ProfileShopsContainer({ bodyshop }) {
|
||||
export function ProfileShopsContainer({ bodyshop, currentUser }) {
|
||||
const { loading, error, data } = useQuery(QUERY_ALL_ASSOCIATIONS, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
email: currentUser.email,
|
||||
},
|
||||
skip: !currentUser,
|
||||
});
|
||||
const [updateAssocation] = useMutation(UPDATE_ASSOCIATION);
|
||||
const [updateActiveAssociation] = useMutation(UPDATE_ACTIVE_ASSOCIATION);
|
||||
|
||||
const updateActiveShop = async (activeShopId) => {
|
||||
const updateActiveShop = async (newActiveAssocId) => {
|
||||
logImEXEvent("profile_change_active_shop");
|
||||
|
||||
try {
|
||||
@@ -46,16 +54,12 @@ export function ProfileShopsContainer({ bodyshop }) {
|
||||
} catch (error) {
|
||||
console.log("No FCM token. Skipping unsubscribe.");
|
||||
}
|
||||
await Promise.all(
|
||||
data.associations.map(async (record) => {
|
||||
await updateAssocation({
|
||||
variables: {
|
||||
assocId: record.id,
|
||||
assocActive: record.id === activeShopId ? true : false,
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
await updateActiveAssociation({
|
||||
variables: {
|
||||
newActiveAssocId: newActiveAssocId,
|
||||
},
|
||||
});
|
||||
|
||||
//Force window refresh.
|
||||
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, Checkbox, Col, PageHeader, Row, Space } from "antd";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Col,
|
||||
PageHeader,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
} from "antd";
|
||||
import { t } from "i18next";
|
||||
import React, { useMemo } from "react";
|
||||
import useLocalStorage from "../../utils/useLocalStorage";
|
||||
@@ -9,22 +18,39 @@ import ScheduleModal from "../schedule-job-modal/schedule-job-modal.container";
|
||||
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
|
||||
import ScheduleProductionList from "../schedule-production-list/schedule-production-list.component";
|
||||
import ScheduleVerifyIntegrity from "../schedule-verify-integrity/schedule-verify-integrity.component";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ScheduleCalendarComponent);
|
||||
|
||||
export default function ScheduleCalendarComponent({ data, refetch }) {
|
||||
export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
|
||||
const [filter, setFilter] = useLocalStorage("filter_events", {
|
||||
intake: true,
|
||||
manual: true,
|
||||
employeevacation: true,
|
||||
ins_co_nm: null,
|
||||
});
|
||||
const filteredData = useMemo(() => {
|
||||
return data.filter(
|
||||
(d) =>
|
||||
d.block ||
|
||||
(filter.intake && d.isintake) ||
|
||||
(filter.manual && !d.isintake && d.block === false) ||
|
||||
(d.__typename === "employee_vacation" &&
|
||||
filter.employeevacation &&
|
||||
!!d.employee)
|
||||
(d.block ||
|
||||
(filter.intake && d.isintake) ||
|
||||
(filter.manual && !d.isintake && d.block === false) ||
|
||||
(d.__typename === "employee_vacation" &&
|
||||
filter.employeevacation &&
|
||||
!!d.employee)) &&
|
||||
(filter.ins_co_nm && filter.ins_co_nm.length > 0
|
||||
? filter.ins_co_nm.includes(d.job.ins_co_nm)
|
||||
: true)
|
||||
);
|
||||
}, [data, filter]);
|
||||
|
||||
@@ -37,6 +63,21 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
|
||||
extra={
|
||||
<Space wrap>
|
||||
<ScheduleAtsSummary appointments={filteredData} />
|
||||
<Select
|
||||
style={{ minWidth: "15rem" }}
|
||||
mode="multiple"
|
||||
placeholder={t("schedule.labels.ins_co_nm_filter")}
|
||||
allowClear
|
||||
onClear={() => setFilter({ ...filter, ins_co_nm: [] })}
|
||||
value={filter?.ins_co_nm}
|
||||
onChange={(e) => {
|
||||
setFilter({ ...filter, ins_co_nm: e });
|
||||
}}
|
||||
options={bodyshop.md_ins_cos.map((i) => ({
|
||||
label: i.name,
|
||||
value: i.name,
|
||||
}))}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={filter?.intake}
|
||||
onChange={(e) => {
|
||||
|
||||
@@ -148,6 +148,7 @@ export function ScheduleJobModalContainer({
|
||||
date_scheduled: new Date(),
|
||||
scheduled_in: values.start,
|
||||
scheduled_completion: values.scheduled_completion,
|
||||
lost_sale_reason: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Button, Card, Tabs } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -8,6 +9,7 @@ import ShopInfoGeneral from "./shop-info.general.component";
|
||||
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
||||
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
||||
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
||||
import ShopInfoPartsScan from "./shop-info.parts-scan";
|
||||
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
||||
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
||||
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||
@@ -23,6 +25,11 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
|
||||
|
||||
export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
||||
const { CriticalPartsScanning } = useTreatments(
|
||||
["CriticalPartsScanning"],
|
||||
{},
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Card
|
||||
@@ -71,6 +78,11 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
||||
<Tabs.TabPane key="laborrates" tab={t("bodyshop.labels.laborrates")}>
|
||||
<ShopInfoLaborRates form={form} />
|
||||
</Tabs.TabPane>
|
||||
{CriticalPartsScanning.treatment === "on" && (
|
||||
<Tabs.TabPane key="partsscan" tab={t("bodyshop.labels.partsscan")}>
|
||||
<ShopInfoPartsScan form={form} />
|
||||
</Tabs.TabPane>
|
||||
)}
|
||||
</Tabs>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -473,6 +473,13 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["enforce_conversion_category"]}
|
||||
label={t("bodyshop.fields.enforce_conversion_category")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["target_touchtime"]}
|
||||
label={t("bodyshop.fields.target_touchtime")}
|
||||
|
||||
81
client/src/components/shop-info/shop-info.parts-scan.jsx
Normal file
81
client/src/components/shop-info/shop-info.parts-scan.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
import { Button, Form, Input, Space } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
export default function ShopInfoPartsScan({ form }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<LayoutFormRow header={t("bodyshop.labels.md_parts_scan")}>
|
||||
<Form.List name={["md_parts_scan"]}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item key={field.key}>
|
||||
<LayoutFormRow noDivider>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_parts_scan.expression")}
|
||||
key={`${index}expression`}
|
||||
name={[field.name, "expression"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_parts_scan.flags")}
|
||||
key={`${index}flags`}
|
||||
name={[field.name, "flags"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
<FormListMoveArrows
|
||||
move={move}
|
||||
index={index}
|
||||
total={fields.length}
|
||||
/>
|
||||
</Space>
|
||||
</LayoutFormRow>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("bodyshop.actions.addpartsrule")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -217,7 +217,9 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
{t("jobs.fields.ponumber")}
|
||||
</Select.Option>
|
||||
<Select.Option value="account_number">
|
||||
{t("jobs.fields.dms.control_type.account_number")}
|
||||
{t(
|
||||
"jobs.fields.dms.control_type.account_number"
|
||||
)}
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
@@ -423,6 +425,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
<Input onBlur={handleBlur} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{bodyshop.cdk_dealerid && (
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.dms.dms_control_override")}
|
||||
key={`${index}dms_control_override`}
|
||||
name={[field.name, "dms_control_override"]}
|
||||
>
|
||||
<Input onBlur={handleBlur} />
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
@@ -546,6 +557,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
|
||||
<Input onBlur={handleBlur} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{bodyshop.cdk_dealerid && (
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.dms.dms_control_override")}
|
||||
key={`${index}dms_control_override`}
|
||||
name={[field.name, "dms_control_override"]}
|
||||
>
|
||||
<Input onBlur={handleBlur} />
|
||||
</Form.Item>
|
||||
)}
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
|
||||
@@ -76,6 +76,19 @@ export default function ShopInfoSchedulingComponent({ form }) {
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_lost_sale_reasons"]}
|
||||
label={t("bodyshop.fields.md_lost_sale_reasons")}
|
||||
rules={[
|
||||
{
|
||||
// required: true,
|
||||
//message: t("general.validation.required"),
|
||||
type: "array",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider>
|
||||
<Space wrap size="large">
|
||||
|
||||
@@ -42,7 +42,9 @@ export default function ShopUsersAuthEdit({ association }) {
|
||||
</div>
|
||||
)}
|
||||
{!visible && (
|
||||
<div style={{ cursor: "pointer" }} onClick={() => setVisible(true)}>
|
||||
<div
|
||||
style={{ cursor: "pointer" }} //onClick={() => setVisible(true)}
|
||||
>
|
||||
{association.authlevel || t("general.labels.na")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -82,9 +82,10 @@ export function TimeTicketModalComponent({
|
||||
label={t("timetickets.fields.ro_number")}
|
||||
rules={[
|
||||
{
|
||||
required:
|
||||
!form.getFieldValue("cost_center") ===
|
||||
"timetickets.labels.shift",
|
||||
required: !(
|
||||
form.getFieldValue("cost_center") ===
|
||||
"timetickets.labels.shift"
|
||||
),
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -1,16 +1,41 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button, Form, notification, PageHeader } from "antd";
|
||||
import { Button, Form, notification, PageHeader, Popconfirm } from "antd";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import VehicleDetailFormComponent from "./vehicle-detail-form.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import moment from "moment";
|
||||
import { UPDATE_VEHICLE } from "../../graphql/vehicles.queries";
|
||||
import { DELETE_VEHICLE, UPDATE_VEHICLE } from "../../graphql/vehicles.queries";
|
||||
import { useHistory } from "react-router-dom";
|
||||
|
||||
function VehicleDetailFormContainer({ vehicle, refetch }) {
|
||||
const { t } = useTranslation();
|
||||
const [updateVehicle] = useMutation(UPDATE_VEHICLE);
|
||||
const [deleteVehicle] = useMutation(DELETE_VEHICLE);
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const history = useHistory();
|
||||
|
||||
const handleDelete = async () => {
|
||||
setLoading(true);
|
||||
const result = await deleteVehicle({
|
||||
variables: { id: vehicle.id },
|
||||
});
|
||||
console.log(result);
|
||||
if (result.errors) {
|
||||
notification["error"]({
|
||||
message: t("vehicles.errors.deleting", {
|
||||
error: JSON.stringify(result.errors),
|
||||
}),
|
||||
});
|
||||
setLoading(false);
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("vehicles.successes.delete"),
|
||||
});
|
||||
setLoading(false);
|
||||
history.push(`/manage/vehicles`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
setLoading(true);
|
||||
@@ -40,15 +65,29 @@ function VehicleDetailFormContainer({ vehicle, refetch }) {
|
||||
<>
|
||||
<PageHeader
|
||||
title={t("menus.header.vehicles")}
|
||||
extra={
|
||||
extra={[
|
||||
<Popconfirm
|
||||
trigger="click"
|
||||
onConfirm={handleDelete}
|
||||
disabled={vehicle.jobs.length !== 0}
|
||||
title={t("vehicles.labels.deleteconfirm")}
|
||||
>
|
||||
<Button
|
||||
type="danger"
|
||||
loading={loading}
|
||||
disabled={vehicle.jobs.length !== 0}
|
||||
>
|
||||
{t("general.actions.delete")}
|
||||
</Button>
|
||||
</Popconfirm>,
|
||||
<Button
|
||||
type="primary"
|
||||
loading={loading}
|
||||
onClick={() => form.submit()}
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
}
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
|
||||
@@ -268,6 +268,7 @@ export const CANCEL_APPOINTMENTS_BY_JOB_ID = gql`
|
||||
scheduled_in
|
||||
scheduled_completion
|
||||
status
|
||||
lost_sale_reason
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
export const QUERY_ALL_ASSOCIATIONS = gql`
|
||||
query QUERY_ALL_ASSOCIATIONS {
|
||||
associations(order_by: { bodyshop: { shopname: asc } }) {
|
||||
query QUERY_ALL_ASSOCIATIONS($email: String) {
|
||||
associations(
|
||||
where: { useremail: { _eq: $email } }
|
||||
order_by: { bodyshop: { shopname: asc } }
|
||||
) {
|
||||
id
|
||||
active
|
||||
bodyshop {
|
||||
@@ -27,6 +30,30 @@ export const UPDATE_ASSOCIATION = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const UPDATE_ACTIVE_ASSOCIATION = gql`
|
||||
mutation UPDATE_ACTIVE_ASSOCIATION($newActiveAssocId: uuid) {
|
||||
nweActive: update_associations(
|
||||
where: { id: { _eq: $newActiveAssocId } }
|
||||
_set: { active: true }
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
shopid
|
||||
active
|
||||
}
|
||||
}
|
||||
inactive: update_associations(
|
||||
where: { id: { _neq: $newActiveAssocId } }
|
||||
_set: { active: false }
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
shopid
|
||||
active
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_ACTIVE_PROD_LIST_VIEW = gql`
|
||||
mutation UPDATE_ACTIVE_PROD_LIST_VIEW($assocId: uuid, $view: String) {
|
||||
|
||||
@@ -12,6 +12,7 @@ export const QUERY_BODYSHOP = gql`
|
||||
query QUERY_BODYSHOP {
|
||||
bodyshops(where: { associations: { active: { _eq: true } } }) {
|
||||
associations {
|
||||
id
|
||||
authlevel
|
||||
useremail
|
||||
default_prod_list_view
|
||||
@@ -112,6 +113,9 @@ export const QUERY_BODYSHOP = gql`
|
||||
localmediaservernetwork
|
||||
localmediatoken
|
||||
enforce_conversion_csr
|
||||
md_lost_sale_reasons
|
||||
md_parts_scan
|
||||
enforce_conversion_category
|
||||
employees {
|
||||
user_email
|
||||
id
|
||||
@@ -222,6 +226,9 @@ export const UPDATE_SHOP = gql`
|
||||
localmediaservernetwork
|
||||
localmediatoken
|
||||
enforce_conversion_csr
|
||||
md_lost_sale_reasons
|
||||
md_parts_scan
|
||||
enforce_conversion_category
|
||||
employees {
|
||||
id
|
||||
first_name
|
||||
|
||||
@@ -31,6 +31,18 @@ import { gql } from "@apollo/client";
|
||||
// }
|
||||
// `;
|
||||
|
||||
export const UNREAD_CONVERSATION_COUNT = gql`
|
||||
query UNREAD_CONVERSATION_COUNT {
|
||||
messages_aggregate(
|
||||
where: { read: { _eq: false }, isoutbound: { _eq: false } }
|
||||
) {
|
||||
aggregate {
|
||||
count
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const CONVERSATION_LIST_QUERY = gql`
|
||||
query CONVERSATION_LIST_QUERY {
|
||||
conversations(
|
||||
|
||||
@@ -536,6 +536,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
driveable
|
||||
towin
|
||||
loss_of_use
|
||||
lost_sale_reason
|
||||
vehicle {
|
||||
id
|
||||
plate_no
|
||||
@@ -722,6 +723,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
convertedtolbr
|
||||
ah_detail_line
|
||||
act_price_before_ppc
|
||||
critical
|
||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||
id
|
||||
quantity
|
||||
@@ -2061,6 +2063,7 @@ export const QUERY_JOB_EXPORT_DMS = gql`
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
ins_co_nm
|
||||
kmin
|
||||
kmout
|
||||
v_make_desc
|
||||
|
||||
@@ -94,6 +94,14 @@ export const UPDATE_OWNER = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_OWNER = gql`
|
||||
mutation DELETE_OWNER($id: uuid!) {
|
||||
delete_owners_by_pk(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_ALL_OWNERS = gql`
|
||||
query QUERY_ALL_OWNERS {
|
||||
owners {
|
||||
|
||||
@@ -55,6 +55,14 @@ export const UPDATE_VEHICLE = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_VEHICLE = gql`
|
||||
mutation DELETE_VEHICLE($id: uuid!) {
|
||||
delete_vehicles_by_pk(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_ALL_VEHICLES = gql`
|
||||
query QUERY_ALL_VEHICLES {
|
||||
vehicles {
|
||||
|
||||
@@ -101,7 +101,10 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
||||
];
|
||||
});
|
||||
});
|
||||
|
||||
socket.on("connect_error", (err) => {
|
||||
console.log(`connect_error due to ${err}`, err);
|
||||
notification.error({ message: err.message });
|
||||
});
|
||||
socket.on("log-event", (payload) => {
|
||||
setLogs((logs) => {
|
||||
return [...logs, payload];
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function JobsCreateComponent({ form }) {
|
||||
const steps = [
|
||||
{
|
||||
title: t("jobs.labels.create.vehicleinfo"),
|
||||
content: <JobsCreateVehicleInfoContainer />,
|
||||
content: <JobsCreateVehicleInfoContainer form={form} />,
|
||||
validation:
|
||||
!!state.vehicle.new ||
|
||||
!!state.vehicle.selectedid ||
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
"addapptcolor": "Add Appointment Color",
|
||||
"addbucket": "Add Definition",
|
||||
"addpartslocation": "Add Parts Location",
|
||||
"addpartsrule": "Add Parts Scan Rule",
|
||||
"addspeedprint": "Add Speed Print",
|
||||
"addtemplate": "Add Template",
|
||||
"newlaborrate": "New Labor Rate",
|
||||
@@ -270,6 +271,7 @@
|
||||
"disablebillwip": "Disable bill WIP for A/P Posting",
|
||||
"disablecontactvehiclecreation": "Disable Contact & Vehicle Updates/Creation",
|
||||
"dms_acctnumber": "DMS Account #",
|
||||
"dms_control_override": "Static Control # Override",
|
||||
"dms_wip_acctnumber": "DMS W.I.P. Account #",
|
||||
"generic_customer_number": "Generic Customer Number",
|
||||
"itc_federal": "Federal Tax is ITC?",
|
||||
@@ -281,6 +283,7 @@
|
||||
},
|
||||
"email": "General Shop Email",
|
||||
"enforce_class": "Enforce Class on Conversion?",
|
||||
"enforce_conversion_category": "Enforce Category on Conversion?",
|
||||
"enforce_conversion_csr": "Enforce CSR on Conversion?",
|
||||
"enforce_referral": "Enforce Referrals",
|
||||
"federal_tax_id": "Federal Tax ID (GST/HST)",
|
||||
@@ -328,7 +331,12 @@
|
||||
"zip": "Zip/Postal Code"
|
||||
},
|
||||
"md_jobline_presets": "Jobline Presets",
|
||||
"md_lost_sale_reasons": "Lost Sale Reasons",
|
||||
"md_parts_order_comment": "Parts Orders Comments",
|
||||
"md_parts_scan": {
|
||||
"expression": "RegEX Expression",
|
||||
"flags": "Flags"
|
||||
},
|
||||
"md_payment_types": "Payment Types",
|
||||
"md_referral_sources": "Referral Sources",
|
||||
"messaginglabel": "Messaging Preset Label",
|
||||
@@ -579,6 +587,7 @@
|
||||
"notespresets": "Notes Presets",
|
||||
"orderstatuses": "Order Statuses",
|
||||
"partslocations": "Parts Locations",
|
||||
"partsscan": "Critical Parts Scanning",
|
||||
"printlater": "Print Later",
|
||||
"qbo": "Use QuickBooks Online?",
|
||||
"qbo_departmentid": "QBO Department ID",
|
||||
@@ -1448,6 +1457,7 @@
|
||||
"loss_date": "Loss Date",
|
||||
"loss_desc": "Loss Description",
|
||||
"loss_of_use": "Loss of Use",
|
||||
"lost_sale_reason": "Lost Sale Reason",
|
||||
"ma2s": "2 Stage Paint",
|
||||
"ma3s": "3 Stage Pain",
|
||||
"mabl": "MABL?",
|
||||
@@ -1985,6 +1995,7 @@
|
||||
"update": "Update Selected Records"
|
||||
},
|
||||
"errors": {
|
||||
"deleting": "Error deleting owner. {{error}}.",
|
||||
"noaccess": "The record does not exist or you do not have access to it. ",
|
||||
"saving": "Error saving owner. {{error}}.",
|
||||
"selectexistingornew": "Select an existing owner record or create a new one. "
|
||||
@@ -2017,6 +2028,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"create_new": "Create a new owner record.",
|
||||
"deleteconfirm": "Are you sure you want to delete this owner? This cannot be undone.",
|
||||
"existing_owners": "Existing Owners",
|
||||
"fromclaim": "Current Claim",
|
||||
"fromowner": "Historical Owner Record",
|
||||
@@ -2024,6 +2036,7 @@
|
||||
"updateowner": "Update Owner"
|
||||
},
|
||||
"successes": {
|
||||
"delete": "Owner deleted successfully.",
|
||||
"save": "Owner saved successfully."
|
||||
}
|
||||
},
|
||||
@@ -2423,6 +2436,7 @@
|
||||
"credits_not_received_date": "Credits not Received by Date",
|
||||
"credits_not_received_date_vendorid": "Credits not Received by Vendor",
|
||||
"csi": "CSI Responses",
|
||||
"customer_list": "Customer List",
|
||||
"cycle_time_analysis": "Cycle Time Analysis",
|
||||
"estimates_written_converted": "Estimates Written/Converted",
|
||||
"estimator_detail": "Jobs by Estimator (Detail)",
|
||||
@@ -2489,6 +2503,7 @@
|
||||
"production_by_target_date": "Production by Target Date",
|
||||
"production_by_technician": "Production by Technician",
|
||||
"production_by_technician_one": "Production filtered by Technician",
|
||||
"production_over_time": "Production Level over Time",
|
||||
"psr_by_make": "Percent of Sales by Vehicle Make",
|
||||
"purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)",
|
||||
"purchase_return_ratio_grouped_by_vendor_summary": "Purchase & Return Ratio by Vendor (Summary)",
|
||||
@@ -2521,6 +2536,7 @@
|
||||
"labels": {
|
||||
"atssummary": "ATS Summary",
|
||||
"employeevacation": "Employee Vacations",
|
||||
"ins_co_nm_filter": "Filter by Insurance Company",
|
||||
"intake": "Intake Events",
|
||||
"manual": "Manual Events",
|
||||
"manualevent": "Add Manual Event"
|
||||
@@ -2781,6 +2797,7 @@
|
||||
},
|
||||
"vehicles": {
|
||||
"errors": {
|
||||
"deleting": "Error deleting vehicle. {{error}}.",
|
||||
"noaccess": "The vehicle does not exist or you do not have access to it.",
|
||||
"selectexistingornew": "Select an existing vehicle record or create a new one. ",
|
||||
"validation": "Please ensure all fields are entered correctly.",
|
||||
@@ -2816,12 +2833,14 @@
|
||||
"registration": "Registration"
|
||||
},
|
||||
"labels": {
|
||||
"deleteconfirm": "Are you sure you want to delete this vehicle? This cannot be undone.",
|
||||
"fromvehicle": "Historical Vehicle Record",
|
||||
"novehinfo": "No Vehicle Information",
|
||||
"relatedjobs": "Related Jobs",
|
||||
"updatevehicle": "Update Vehicle Information"
|
||||
},
|
||||
"successes": {
|
||||
"delete": "Vehicle deleted successfully.",
|
||||
"save": "Vehicle saved successfully."
|
||||
}
|
||||
},
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
"addapptcolor": "",
|
||||
"addbucket": "",
|
||||
"addpartslocation": "",
|
||||
"addpartsrule": "",
|
||||
"addspeedprint": "",
|
||||
"addtemplate": "",
|
||||
"newlaborrate": "",
|
||||
@@ -270,6 +271,7 @@
|
||||
"disablebillwip": "",
|
||||
"disablecontactvehiclecreation": "",
|
||||
"dms_acctnumber": "",
|
||||
"dms_control_override": "",
|
||||
"dms_wip_acctnumber": "",
|
||||
"generic_customer_number": "",
|
||||
"itc_federal": "",
|
||||
@@ -281,6 +283,7 @@
|
||||
},
|
||||
"email": "",
|
||||
"enforce_class": "",
|
||||
"enforce_conversion_category": "",
|
||||
"enforce_conversion_csr": "",
|
||||
"enforce_referral": "",
|
||||
"federal_tax_id": "",
|
||||
@@ -328,7 +331,12 @@
|
||||
"zip": ""
|
||||
},
|
||||
"md_jobline_presets": "",
|
||||
"md_lost_sale_reasons": "",
|
||||
"md_parts_order_comment": "",
|
||||
"md_parts_scan": {
|
||||
"expression": "",
|
||||
"flags": ""
|
||||
},
|
||||
"md_payment_types": "",
|
||||
"md_referral_sources": "",
|
||||
"messaginglabel": "",
|
||||
@@ -579,6 +587,7 @@
|
||||
"notespresets": "",
|
||||
"orderstatuses": "",
|
||||
"partslocations": "",
|
||||
"partsscan": "",
|
||||
"printlater": "",
|
||||
"qbo": "",
|
||||
"qbo_departmentid": "",
|
||||
@@ -1448,6 +1457,7 @@
|
||||
"loss_date": "Fecha de pérdida",
|
||||
"loss_desc": "",
|
||||
"loss_of_use": "",
|
||||
"lost_sale_reason": "",
|
||||
"ma2s": "",
|
||||
"ma3s": "",
|
||||
"mabl": "",
|
||||
@@ -1985,6 +1995,7 @@
|
||||
"update": ""
|
||||
},
|
||||
"errors": {
|
||||
"deleting": "",
|
||||
"noaccess": "El registro no existe o no tiene acceso a él.",
|
||||
"saving": "",
|
||||
"selectexistingornew": ""
|
||||
@@ -2017,6 +2028,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"create_new": "Crea un nuevo registro de propietario.",
|
||||
"deleteconfirm": "",
|
||||
"existing_owners": "Propietarios existentes",
|
||||
"fromclaim": "",
|
||||
"fromowner": "",
|
||||
@@ -2024,6 +2036,7 @@
|
||||
"updateowner": ""
|
||||
},
|
||||
"successes": {
|
||||
"delete": "",
|
||||
"save": "Propietario guardado con éxito."
|
||||
}
|
||||
},
|
||||
@@ -2423,6 +2436,7 @@
|
||||
"credits_not_received_date": "",
|
||||
"credits_not_received_date_vendorid": "",
|
||||
"csi": "",
|
||||
"customer_list": "",
|
||||
"cycle_time_analysis": "",
|
||||
"estimates_written_converted": "",
|
||||
"estimator_detail": "",
|
||||
@@ -2489,6 +2503,7 @@
|
||||
"production_by_target_date": "",
|
||||
"production_by_technician": "",
|
||||
"production_by_technician_one": "",
|
||||
"production_over_time": "",
|
||||
"psr_by_make": "",
|
||||
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
||||
"purchase_return_ratio_grouped_by_vendor_summary": "",
|
||||
@@ -2521,6 +2536,7 @@
|
||||
"labels": {
|
||||
"atssummary": "",
|
||||
"employeevacation": "",
|
||||
"ins_co_nm_filter": "",
|
||||
"intake": "",
|
||||
"manual": "",
|
||||
"manualevent": ""
|
||||
@@ -2781,6 +2797,7 @@
|
||||
},
|
||||
"vehicles": {
|
||||
"errors": {
|
||||
"deleting": "",
|
||||
"noaccess": "El vehículo no existe o usted no tiene acceso a él.",
|
||||
"selectexistingornew": "",
|
||||
"validation": "Asegúrese de que todos los campos se ingresen correctamente.",
|
||||
@@ -2816,12 +2833,14 @@
|
||||
"registration": ""
|
||||
},
|
||||
"labels": {
|
||||
"deleteconfirm": "",
|
||||
"fromvehicle": "",
|
||||
"novehinfo": "",
|
||||
"relatedjobs": "",
|
||||
"updatevehicle": ""
|
||||
},
|
||||
"successes": {
|
||||
"delete": "",
|
||||
"save": "Vehículo guardado con éxito."
|
||||
}
|
||||
},
|
||||
|
||||
@@ -230,6 +230,7 @@
|
||||
"addapptcolor": "",
|
||||
"addbucket": "",
|
||||
"addpartslocation": "",
|
||||
"addpartsrule": "",
|
||||
"addspeedprint": "",
|
||||
"addtemplate": "",
|
||||
"newlaborrate": "",
|
||||
@@ -270,6 +271,7 @@
|
||||
"disablebillwip": "",
|
||||
"disablecontactvehiclecreation": "",
|
||||
"dms_acctnumber": "",
|
||||
"dms_control_override": "",
|
||||
"dms_wip_acctnumber": "",
|
||||
"generic_customer_number": "",
|
||||
"itc_federal": "",
|
||||
@@ -281,6 +283,7 @@
|
||||
},
|
||||
"email": "",
|
||||
"enforce_class": "",
|
||||
"enforce_conversion_category": "",
|
||||
"enforce_conversion_csr": "",
|
||||
"enforce_referral": "",
|
||||
"federal_tax_id": "",
|
||||
@@ -328,7 +331,12 @@
|
||||
"zip": ""
|
||||
},
|
||||
"md_jobline_presets": "",
|
||||
"md_lost_sale_reasons": "",
|
||||
"md_parts_order_comment": "",
|
||||
"md_parts_scan": {
|
||||
"expression": "",
|
||||
"flags": ""
|
||||
},
|
||||
"md_payment_types": "",
|
||||
"md_referral_sources": "",
|
||||
"messaginglabel": "",
|
||||
@@ -579,6 +587,7 @@
|
||||
"notespresets": "",
|
||||
"orderstatuses": "",
|
||||
"partslocations": "",
|
||||
"partsscan": "",
|
||||
"printlater": "",
|
||||
"qbo": "",
|
||||
"qbo_departmentid": "",
|
||||
@@ -1448,6 +1457,7 @@
|
||||
"loss_date": "Date de perte",
|
||||
"loss_desc": "",
|
||||
"loss_of_use": "",
|
||||
"lost_sale_reason": "",
|
||||
"ma2s": "",
|
||||
"ma3s": "",
|
||||
"mabl": "",
|
||||
@@ -1985,6 +1995,7 @@
|
||||
"update": ""
|
||||
},
|
||||
"errors": {
|
||||
"deleting": "",
|
||||
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.",
|
||||
"saving": "",
|
||||
"selectexistingornew": ""
|
||||
@@ -2017,6 +2028,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"create_new": "Créez un nouvel enregistrement de propriétaire.",
|
||||
"deleteconfirm": "",
|
||||
"existing_owners": "Propriétaires existants",
|
||||
"fromclaim": "",
|
||||
"fromowner": "",
|
||||
@@ -2024,6 +2036,7 @@
|
||||
"updateowner": ""
|
||||
},
|
||||
"successes": {
|
||||
"delete": "",
|
||||
"save": "Le propriétaire a bien enregistré."
|
||||
}
|
||||
},
|
||||
@@ -2423,6 +2436,7 @@
|
||||
"credits_not_received_date": "",
|
||||
"credits_not_received_date_vendorid": "",
|
||||
"csi": "",
|
||||
"customer_list": "",
|
||||
"cycle_time_analysis": "",
|
||||
"estimates_written_converted": "",
|
||||
"estimator_detail": "",
|
||||
@@ -2489,6 +2503,7 @@
|
||||
"production_by_target_date": "",
|
||||
"production_by_technician": "",
|
||||
"production_by_technician_one": "",
|
||||
"production_over_time": "",
|
||||
"psr_by_make": "",
|
||||
"purchase_return_ratio_grouped_by_vendor_detail": "",
|
||||
"purchase_return_ratio_grouped_by_vendor_summary": "",
|
||||
@@ -2521,6 +2536,7 @@
|
||||
"labels": {
|
||||
"atssummary": "",
|
||||
"employeevacation": "",
|
||||
"ins_co_nm_filter": "",
|
||||
"intake": "",
|
||||
"manual": "",
|
||||
"manualevent": ""
|
||||
@@ -2781,6 +2797,7 @@
|
||||
},
|
||||
"vehicles": {
|
||||
"errors": {
|
||||
"deleting": "",
|
||||
"noaccess": "Le véhicule n'existe pas ou vous n'y avez pas accès.",
|
||||
"selectexistingornew": "",
|
||||
"validation": "Veuillez vous assurer que tous les champs sont correctement entrés.",
|
||||
@@ -2816,12 +2833,14 @@
|
||||
"registration": ""
|
||||
},
|
||||
"labels": {
|
||||
"deleteconfirm": "",
|
||||
"fromvehicle": "",
|
||||
"novehinfo": "",
|
||||
"relatedjobs": "",
|
||||
"updatevehicle": ""
|
||||
},
|
||||
"successes": {
|
||||
"delete": "",
|
||||
"save": "Le véhicule a été enregistré avec succès."
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
import NumberFormat from "react-number-format";
|
||||
import { NumericFormat } from "react-number-format";
|
||||
|
||||
export default function CurrencyFormatter(props) {
|
||||
return (
|
||||
<NumberFormat
|
||||
<NumericFormat
|
||||
thousandSeparator={true}
|
||||
decimalScale={2}
|
||||
fixedDecimalScale={true}
|
||||
|
||||
@@ -142,7 +142,7 @@ middlewares.push(
|
||||
|
||||
const cache = new InMemoryCache({});
|
||||
|
||||
export default new ApolloClient({
|
||||
const client = new ApolloClient({
|
||||
link: ApolloLink.from(middlewares),
|
||||
cache,
|
||||
connectToDevTools: process.env.NODE_ENV !== "production",
|
||||
@@ -161,3 +161,4 @@ export default new ApolloClient({
|
||||
},
|
||||
},
|
||||
});
|
||||
export default client;
|
||||
|
||||
@@ -1841,6 +1841,34 @@ export const TemplateList = (type, context) => {
|
||||
},
|
||||
group: "purchases",
|
||||
},
|
||||
production_over_time: {
|
||||
title: i18n.t("reportcenter.templates.production_over_time"),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.production_over_time"
|
||||
),
|
||||
key: "production_over_time",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.actual_in"),
|
||||
},
|
||||
group: "jobs",
|
||||
},
|
||||
customer_list: {
|
||||
title: i18n.t("reportcenter.templates.customer_list"),
|
||||
subject: i18n.t(
|
||||
"reportcenter.templates.customer_list"
|
||||
),
|
||||
key: "customer_list",
|
||||
//idtype: "vendor",
|
||||
disabled: false,
|
||||
rangeFilter: {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_invoiced"),
|
||||
},
|
||||
group: "customers",
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(!type || type === "courtesycarcontract"
|
||||
|
||||
12
client/src/utils/criticalPartsScan.js
Normal file
12
client/src/utils/criticalPartsScan.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import axios from "axios";
|
||||
import { notification } from "antd";
|
||||
|
||||
async function CriticalPartsScan(jobid) {
|
||||
try {
|
||||
await axios.post("/job/partsscan", { jobid });
|
||||
} catch (error) {
|
||||
notification.open({ type: "error", message: JSON.stringify(error) });
|
||||
}
|
||||
}
|
||||
|
||||
export default CriticalPartsScan;
|
||||
Reference in New Issue
Block a user