Added manual group changing, stbility improvements.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { Switch } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import { selectSettings } from "../../../redux/application/application.selectors";
|
||||
import DataLabel from "../../atoms/data-label/data-label.atom";
|
||||
const { ipcRenderer } = window;
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
appSettings: selectSettings,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function WatcherPollingMolecule({ appSettings }) {
|
||||
const handleChange = (val) => {
|
||||
ipcRenderer.send(ipcTypes.default.store.set, {
|
||||
enableNotifications: val,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DataLabel label="Notifications Enabled?">
|
||||
<Switch
|
||||
onChange={handleChange}
|
||||
checked={appSettings && appSettings.enableNotifications}
|
||||
/>
|
||||
</DataLabel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(WatcherPollingMolecule);
|
||||
@@ -1,15 +1,20 @@
|
||||
export default (part_type) => {
|
||||
switch (part_type) {
|
||||
case "PAA":
|
||||
case "PAL":
|
||||
case "PAC":
|
||||
return "A/M";
|
||||
case "PAE":
|
||||
return "Exist.";
|
||||
return "Ex.";
|
||||
case "PAN":
|
||||
case "PAP":
|
||||
return "OEM";
|
||||
case "PAP":
|
||||
return "OEMP";
|
||||
case "PAL":
|
||||
return "LKQ";
|
||||
|
||||
case "PAC":
|
||||
return "A/M (PAC)";
|
||||
case "PAR":
|
||||
return "A/M (PAR)";
|
||||
default:
|
||||
return part_type;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function TimeAgoFormatter(props) {
|
||||
}, [m]);
|
||||
|
||||
return props.children ? (
|
||||
<Tooltip placement="top" title={m.format("MM/DD/YYY hh:mm A")}>
|
||||
<Tooltip placement="top" title={m.format("MM/DD/YYYY hh:mm:ss A")}>
|
||||
{timestampString}
|
||||
</Tooltip>
|
||||
) : null;
|
||||
|
||||
53
src/components/molecules/job-group/job-group.molecule.jsx
Normal file
53
src/components/molecules/job-group/job-group.molecule.jsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { DownOutlined, LoadingOutlined } from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Dropdown, Menu, message } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { UPDATE_JOB } from "../../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(JobGroupMolecule);
|
||||
|
||||
export function JobGroupMolecule({ bodyshop, jobId, group }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
|
||||
const handleMenuClick = async (value) => {
|
||||
setLoading(true);
|
||||
const result = await updateJob({
|
||||
variables: { jobId: jobId, job: { group: value.key } },
|
||||
});
|
||||
|
||||
if (!result.errors) {
|
||||
message.success("Close date updated.");
|
||||
} else {
|
||||
message.error("Error updating job.");
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<Menu onClick={handleMenuClick}>
|
||||
{bodyshop.groups.map((g, idx) => (
|
||||
<Menu.Item key={g}>{g}</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown overlay={menu} trigger={["click"]}>
|
||||
<a href=" #" onClick={(e) => e.preventDefault()}>
|
||||
{group}
|
||||
<DownOutlined />
|
||||
{loading && <LoadingOutlined />}
|
||||
</a>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
@@ -2,8 +2,9 @@ import { Descriptions, PageHeader, Skeleton } from "antd";
|
||||
import React from "react";
|
||||
import CurrencyFormatterAtom from "../../atoms/currency-formatter/currency-formatter.atom";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import CloseDateDisplayMolecule from "../close-date-display/close-date-display.molecule";
|
||||
import TimeAgoFormatter from "../../atoms/time-ago-formatter/time-ago-formatter.atom";
|
||||
import CloseDateDisplayMolecule from "../close-date-display/close-date-display.molecule";
|
||||
import JobGroupMolecule from "../job-group/job-group.molecule";
|
||||
|
||||
export default function JobsDetailDescriptionMolecule({ loading, job }) {
|
||||
if (loading) return <Skeleton active />;
|
||||
@@ -15,11 +16,13 @@ export default function JobsDetailDescriptionMolecule({ loading, job }) {
|
||||
<PageHeader ghost={false} title={job.clm_no} subTitle={job.ins_co_nm}>
|
||||
<Descriptions column={{ xxl: 5, xl: 4, lg: 3, md: 3, sm: 2, xs: 1 }}>
|
||||
<Descriptions.Item label="Owner">{`${job.ownr_fn} ${job.ownr_ln}`}</Descriptions.Item>
|
||||
<Descriptions.Item label="Vehicle">{`${job.v_model_yr} ${job.v_makedesc} ${job.v_model}`}</Descriptions.Item>
|
||||
<Descriptions.Item label="Vehicle">{`${job.v_model_yr} ${job.v_makedesc} ${job.v_model} (${job.v_type})`}</Descriptions.Item>
|
||||
<Descriptions.Item label="Claim Total">
|
||||
<CurrencyFormatterAtom>{job.clm_total}</CurrencyFormatterAtom>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Group">{job.group}</Descriptions.Item>
|
||||
<Descriptions.Item label="Group">
|
||||
<JobGroupMolecule jobId={job.id} group={job.group} />
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Age">{job.v_age}</Descriptions.Item>
|
||||
<Descriptions.Item label="Close Date">
|
||||
<CloseDateDisplayMolecule
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Input, Table } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { alphaSort } from "../../../util/sorters";
|
||||
import CurrencyFormatterAtom from "../../atoms/currency-formatter/currency-formatter.atom";
|
||||
import IgnoreJobLine from "../../atoms/ignore-job-line/ignore-job-line.atom";
|
||||
import partTypeConverterAtom from "../../atoms/part-type-converter/part-type-converter.atom";
|
||||
@@ -14,33 +15,38 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
title: "#",
|
||||
dataIndex: "line_no",
|
||||
key: "line_no",
|
||||
sorter: (a, b) => a.line_no - b.line_no,
|
||||
},
|
||||
{
|
||||
title: "S#",
|
||||
dataIndex: "line_ind",
|
||||
key: "line_ind",
|
||||
sorter: (a, b) => alphaSort(a.line_ind, b.line_ind),
|
||||
},
|
||||
{
|
||||
title: "Line Description",
|
||||
dataIndex: "line_desc",
|
||||
key: "line_desc",
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
},
|
||||
{
|
||||
title: "Part Type",
|
||||
dataIndex: "part_type",
|
||||
key: "part_type",
|
||||
sorter: (a, b) => alphaSort(a.part_type, b.part_type),
|
||||
render: (text, record) => partTypeConverterAtom(text),
|
||||
},
|
||||
{
|
||||
title: "Part Number",
|
||||
dataIndex: "oem_partno",
|
||||
key: "oem_partno",
|
||||
sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno),
|
||||
},
|
||||
{
|
||||
title: "Database Price",
|
||||
dataIndex: "db_price",
|
||||
key: "db_price",
|
||||
|
||||
sorter: (a, b) => a.db_price - b.db_price,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatterAtom>{record.db_price}</CurrencyFormatterAtom>
|
||||
),
|
||||
@@ -49,7 +55,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
title: "Actual Price",
|
||||
dataIndex: "act_price",
|
||||
key: "act_price",
|
||||
|
||||
sorter: (a, b) => a.act_price - b.act_price,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatterAtom>{record.act_price}</CurrencyFormatterAtom>
|
||||
),
|
||||
@@ -58,7 +64,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
title: "Price Diff.",
|
||||
dataIndex: "price_diff",
|
||||
key: "price_diff",
|
||||
|
||||
sorter: (a, b) => a.price_diff - b.price_diff,
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatterAtom>{record.price_diff}</CurrencyFormatterAtom>
|
||||
),
|
||||
@@ -67,7 +73,7 @@ export default function JobLinesTableMolecule({ loading, job }) {
|
||||
title: "Price Diff. %",
|
||||
dataIndex: "price_diff_pc",
|
||||
key: "price_diff_pc",
|
||||
|
||||
sorter: (a, b) => a.price_diff_pc - b.price_diff_pc,
|
||||
render: (text, record) => (
|
||||
<PriceDiffPcFormatterAtom
|
||||
price_diff_pc={record.price_diff_pc}
|
||||
|
||||
@@ -25,16 +25,18 @@ export function JobsTargetsStatsMolecule({
|
||||
job,
|
||||
selectedJobTargetPc,
|
||||
}) {
|
||||
const currentRpsDollars = useCallback(CalculateJobRpsDollars(job), [job]);
|
||||
const { actPriceSum, jobRpsDollars } = useCallback(
|
||||
CalculateJobRpsDollars(job, true),
|
||||
[job]
|
||||
);
|
||||
|
||||
const currentRpsPc = useCallback(CalculateJobRpsPc(job, currentRpsDollars), [
|
||||
job,
|
||||
currentRpsDollars,
|
||||
]);
|
||||
const { dbPriceSum, jobRpsPc } = useCallback(
|
||||
CalculateJobRpsPc(job, jobRpsDollars, true),
|
||||
[job, jobRpsDollars]
|
||||
);
|
||||
|
||||
if (loading) return <Skeleton active />;
|
||||
if (!job) return <ErrorResultAtom title="Error displaying job data." />;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -53,12 +55,18 @@ export function JobsTargetsStatsMolecule({
|
||||
<Statistic
|
||||
title="Current RPS %"
|
||||
valueStyle={{
|
||||
color: selectedJobTargetPc > currentRpsPc ? "tomato" : "seagreen",
|
||||
color: selectedJobTargetPc > (jobRpsPc || 0) ? "tomato" : "seagreen",
|
||||
}}
|
||||
value={(currentRpsPc * 100).toFixed(1)}
|
||||
value={((jobRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
<Statistic title="Current RPS $" value={currentRpsDollars.toFormat()} />
|
||||
<Statistic
|
||||
title="Target RPS $"
|
||||
value={actPriceSum.percentage(selectedJobTargetPc * 100).toFormat()}
|
||||
/>
|
||||
<Statistic title="Current RPS $" value={jobRpsDollars.toFormat()} />
|
||||
<Statistic title="DB Price Total" value={dbPriceSum.toFormat()} />
|
||||
<Statistic title="Actual Price Total" value={actPriceSum.toFormat()} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,16 +55,16 @@ export function ReportingTotalsStatsMolecule({ reportingLoading, scoreCard }) {
|
||||
title="Current RPS %"
|
||||
valueStyle={{
|
||||
color:
|
||||
scoreCard.currentRpsPc <= scoreCard.targetRpsPc
|
||||
(scoreCard.currentRpsPc || 0) <= (scoreCard.targetRpsPc || 0)
|
||||
? "tomato"
|
||||
: "seagreen",
|
||||
}}
|
||||
value={(scoreCard.currentRpsPc * 100).toFixed(1)}
|
||||
value={((scoreCard.currentRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
<Statistic
|
||||
title="Target RPS %"
|
||||
value={(scoreCard.targetRpsPc * 100).toFixed(1)}
|
||||
value={((scoreCard.targetRpsPc || 0) * 100).toFixed(1)}
|
||||
suffix="%"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
selectScanEstimates,
|
||||
selectScanLoading,
|
||||
} from "../../../redux/scan/scan.selectors";
|
||||
import { alphaSort } from "../../../util/sorters";
|
||||
import LastScannedAtom from "../../atoms/last-scanned/last-scanned.atom";
|
||||
import ScanRefreshAtom from "../../atoms/scan-refresh/scan-refresh.atom";
|
||||
|
||||
@@ -25,21 +26,25 @@ export function ScanEstimateListMolecule({ scanLoading, estimates }) {
|
||||
title: "Claim No.",
|
||||
dataIndex: "clm_no",
|
||||
key: "clm_no",
|
||||
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
|
||||
},
|
||||
{
|
||||
title: "Ins Co.",
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm),
|
||||
},
|
||||
{
|
||||
title: "First Name",
|
||||
dataIndex: "ownr_fn",
|
||||
key: "ownr_fn",
|
||||
sorter: (a, b) => alphaSort(a.ownr_fn, b.ownr_fn),
|
||||
},
|
||||
{
|
||||
title: "Last Name",
|
||||
dataIndex: "ownr_ln",
|
||||
key: "ownr_ln",
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
},
|
||||
{
|
||||
title: "Vehicle",
|
||||
@@ -48,6 +53,11 @@ export function ScanEstimateListMolecule({ scanLoading, estimates }) {
|
||||
render: (text, record) =>
|
||||
`${record.v_model_yr} ${record.v_makedesc} ${record.v_model} (${record.v_type})`,
|
||||
},
|
||||
{
|
||||
title: "File Path",
|
||||
dataIndex: "filepath",
|
||||
key: "filepath",
|
||||
},
|
||||
{
|
||||
title: "Import",
|
||||
dataIndex: "import",
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { Button, Input, Form, Select, InputNumber, Typography } from "antd";
|
||||
import FormListMoveArrows from "../../atoms/form-list-move-arrows/form-list-move-arrows.atom";
|
||||
import React from "react";
|
||||
import LayoutFormRow from "../../atoms/layout-form-row/layout-form-row.atom";
|
||||
import { DeleteFilled } from "@ant-design/icons";
|
||||
import { Button, Form, Input, InputNumber, Select, Typography } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import FormListMoveArrows from "../../atoms/form-list-move-arrows/form-list-move-arrows.atom";
|
||||
import LayoutFormRow from "../../atoms/layout-form-row/layout-form-row.atom";
|
||||
|
||||
export default function ShopSettingsFormMolecule({ form, saveLoading }) {
|
||||
const [groupOptions, setGroupOptions] = useState(
|
||||
form.getFieldValue("groups") || []
|
||||
);
|
||||
const handleBlur = () => {
|
||||
console.log(form.getFieldValue("groups") || []);
|
||||
setGroupOptions(form.getFieldValue("groups") || []);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography.Title>Shop Settings</Typography.Title>
|
||||
@@ -40,6 +48,19 @@ export default function ShopSettingsFormMolecule({ form, saveLoading }) {
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="groups"
|
||||
label="Available Groupings (Must match below)"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
|
||||
type: "array",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select mode="tags" onBlur={handleBlur} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Typography.Title level={4}>Group Definitions</Typography.Title>
|
||||
<Form.List name={["targets"]}>
|
||||
@@ -59,7 +80,13 @@ export default function ShopSettingsFormMolecule({ form, saveLoading }) {
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
<Select>
|
||||
{groupOptions.map((item, idx) => (
|
||||
<Select.Option key={idx} value={item}>
|
||||
{item}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_JOB_BY_PK } from "../../../graphql/jobs.queries";
|
||||
import { setSelectedJobTargetPc } from "../../../redux/application/application.actions";
|
||||
import { selectSelectedJobId } from "../../../redux/application/application.selectors";
|
||||
import { DeleteJobAtom } from "../../atoms/delete-job/delete-job.atom";
|
||||
import DeleteJobAtom from "../../atoms/delete-job/delete-job.atom";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import JobsPartsGraphAtom from "../../atoms/jobs-parts-graph/jobs-parts-graph.atom";
|
||||
import JobsDetailDescriptionMolecule from "../../molecules/jobs-detail-description/jobs-detail-description.molecule";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Col, Row } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import NotificationsToggleAtom from "../../atoms/notifications-toggle/notifications-toggle.atom";
|
||||
import WatcherPollingMolecule from "../../molecules/watcher-polling/watcher-polling.molecule";
|
||||
import FilePathsListOrganism from "../../organisms/filepaths-list/filepaths-list.organism";
|
||||
import ShopSettingsOrganism from "../../organisms/shop-settings/shop-settings.organism";
|
||||
@@ -21,6 +22,7 @@ export default function SettingsPage() {
|
||||
<Col span={6}>
|
||||
<WatcherManagerOrganism />
|
||||
<WatcherPollingMolecule />
|
||||
<NotificationsToggleAtom />
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user