Merged release/2025-01-31 into feature/IO-2825-Node-22-Update

This commit is contained in:
Dave Richer
2025-02-03 16:46:01 +00:00
9 changed files with 54 additions and 28 deletions

View File

@@ -1,26 +1,23 @@
import { Collapse, Form, Input, InputNumber, Select, Space, Switch } from "antd"; import { Collapse, Form, Input, InputNumber, Select, Space, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component"; import FormItemEmail from "../form-items-formatted/email-form-item.component";
import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component"; import FormItemPhone, { PhoneItemFormatterValidation } from "../form-items-formatted/phone-form-item.component";
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component"; import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
import JobsDetailChangeFilehandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component"; import JobsDetailChangeFilehandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component"; import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
import JobsDetailRatesLabor from "../jobs-detail-rates/jobs-detail-rates.labor.component"; import JobsDetailRatesLabor from "../jobs-detail-rates/jobs-detail-rates.labor.component";
import JobsDetailRatesMaterials from "../jobs-detail-rates/jobs-detail-rates.materials.component"; import JobsDetailRatesMaterials from "../jobs-detail-rates/jobs-detail-rates.materials.component";
import JobsDetailRatesOther from "../jobs-detail-rates/jobs-detail-rates.other.component"; import JobsDetailRatesOther from "../jobs-detail-rates/jobs-detail-rates.other.component";
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
import JobsDetailRatesTaxes from "../jobs-detail-rates/jobs-detail-rates.taxes.component"; import JobsDetailRatesTaxes from "../jobs-detail-rates/jobs-detail-rates.taxes.component";
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component"; import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
@@ -199,7 +196,9 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
</Collapse.Panel> </Collapse.Panel>
<Collapse.Panel forceRender key="financial" header={t("menus.jobsdetail.financials")}> <Collapse.Panel forceRender key="financial" header={t("menus.jobsdetail.financials")}>
<JobsDetailRatesChangeButton form={form} /> <JobsDetailRatesChangeButton form={form} />
<JobsMarkPstExempt form={form} /> {InstanceRenderManager({
imex: <JobsMarkPstExempt form={form} />
})}
<LayoutFormRow> <LayoutFormRow>
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt"> <Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
<CurrencyInput min={0} /> <CurrencyInput min={0} />
@@ -246,7 +245,6 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
</LayoutFormRow> </LayoutFormRow>
) )
})} })}
<LayoutFormRow> <LayoutFormRow>
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab"> <Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
<CurrencyInput /> <CurrencyInput />

View File

@@ -1,9 +1,14 @@
import { Button, Input, Space, Spin } from "antd"; import { Button, Input, Space, Spin } from "antd";
import React, { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { ExclamationCircleFilled, ExclamationCircleOutlined } from "@ant-design/icons"; import {
ExclamationCircleFilled,
ExclamationCircleOutlined,
UserDeleteOutlined,
UsergroupDeleteOutlined
} from "@ant-design/icons";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import EmployeeSearchSelectComponent from "../employee-search-select/employee-search-select.component"; import EmployeeSearchSelectComponent from "../employee-search-select/employee-search-select.component";
@@ -20,6 +25,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(ProductionBoardFilte
export function ProductionBoardFilters({ bodyshop, filter, setFilter, loading }) { export function ProductionBoardFilters({ bodyshop, filter, setFilter, loading }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [alertFilter, setAlertFilter] = useState(false); const [alertFilter, setAlertFilter] = useState(false);
const [unassignedFilter, setUnassignedFilter] = useState(false);
const toggleAlertFilter = () => { const toggleAlertFilter = () => {
const newAlertFilter = !alertFilter; const newAlertFilter = !alertFilter;
@@ -27,6 +33,12 @@ export function ProductionBoardFilters({ bodyshop, filter, setFilter, loading })
setFilter({ ...filter, alert: newAlertFilter }); setFilter({ ...filter, alert: newAlertFilter });
}; };
const toggleUnassignedFilter = () => {
const newUnassignedFilter = !unassignedFilter;
setUnassignedFilter(newUnassignedFilter);
setFilter({ ...filter, unassigned: newUnassignedFilter });
};
return ( return (
<Space wrap> <Space wrap>
{loading && <Spin />} {loading && <Spin />}
@@ -52,6 +64,13 @@ export function ProductionBoardFilters({ bodyshop, filter, setFilter, loading })
> >
{t("production.labels.alerts")} {t("production.labels.alerts")}
</Button> </Button>
<Button
type={unassignedFilter ? "primary" : "default"}
onClick={toggleUnassignedFilter}
icon={unassignedFilter ? <UserDeleteOutlined /> : <UsergroupDeleteOutlined />}
>
{t("production.labels.unassigned")}
</Button>
</Space> </Space>
); );
} }

View File

@@ -29,7 +29,7 @@ const sortByParentId = (arr) => {
// Function to create board data based on statuses and jobs, with optional filtering // Function to create board data based on statuses and jobs, with optional filtering
export const createBoardData = ({ statuses, data, filter, cardSettings }) => { export const createBoardData = ({ statuses, data, filter, cardSettings }) => {
const { search, employeeId, alert } = filter; const { search, employeeId, alert, unassigned } = filter;
const lanes = statuses.map((status) => ({ const lanes = statuses.map((status) => ({
id: status, id: status,
@@ -40,6 +40,13 @@ export const createBoardData = ({ statuses, data, filter, cardSettings }) => {
let filteredJobs = let filteredJobs =
(search === "" || !search) && !employeeId ? data : data.filter((job) => checkFilter(search, employeeId, job)); (search === "" || !search) && !employeeId ? data : data.filter((job) => checkFilter(search, employeeId, job));
// Apply "Unassigned" filter
if (unassigned) {
filteredJobs = filteredJobs.filter(
(job) => !job.employee_body && !job.employee_prep && !job.employee_refinish && !job.employee_csr
);
}
// Filter jobs by selectedMdInsCos if it has values // Filter jobs by selectedMdInsCos if it has values
if (cardSettings?.selectedMdInsCos?.length > 0) { if (cardSettings?.selectedMdInsCos?.length > 0) {
filteredJobs = filteredJobs.filter((job) => cardSettings.selectedMdInsCos.includes(job.ins_co_nm)); filteredJobs = filteredJobs.filter((job) => cardSettings.selectedMdInsCos.includes(job.ins_co_nm));

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Button, Card, Col, Form, Popover, Row, Tabs } from "antd"; import { Button, Card, Col, Form, Popover, Row, Tabs } from "antd";
import React, { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { UPDATE_KANBAN_SETTINGS } from "../../../graphql/user.queries.js"; import { UPDATE_KANBAN_SETTINGS } from "../../../graphql/user.queries.js";
import { defaultKanbanSettings, mergeWithDefaults } from "./defaultKanbanSettings.js"; import { defaultKanbanSettings, mergeWithDefaults } from "./defaultKanbanSettings.js";
@@ -11,6 +11,7 @@ import FilterSettings from "./FilterSettings.jsx";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { isFunction } from "lodash"; import { isFunction } from "lodash";
import { useNotification } from "../../../contexts/Notifications/notificationContext.jsx"; import { useNotification } from "../../../contexts/Notifications/notificationContext.jsx";
import { SettingOutlined } from "@ant-design/icons";
function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bodyshop, data, onSettingsChange }) { function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bodyshop, data, onSettingsChange }) {
const [form] = Form.useForm(); const [form] = Form.useForm();
@@ -153,7 +154,7 @@ function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bod
return ( return (
<Popover content={overlay} open={open} placement="topRight"> <Popover content={overlay} open={open} placement="topRight">
<Button loading={loading} onClick={() => setOpen(!open)}> <Button icon={<SettingOutlined />} loading={loading} onClick={() => setOpen(!open)}>
{t("production.settings.board_settings")} {t("production.settings.board_settings")}
</Button> </Button>
</Popover> </Popover>

View File

@@ -1,5 +1,5 @@
import { Button, Dropdown } from "antd"; import { Button, Dropdown } from "antd";
import React, { useState } from "react"; import { useState } from "react";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocument } from "../../utils/RenderTemplate";
@@ -7,6 +7,7 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx"; import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
import { PrinterFilled } from "@ant-design/icons";
const ProdTemplates = TemplateList("production"); const ProdTemplates = TemplateList("production");
const { production_by_technician_one, production_by_category_one, production_by_repair_status_one } = const { production_by_technician_one, production_by_category_one, production_by_repair_status_one } =
@@ -123,7 +124,9 @@ export function ProductionListPrint({ bodyshop }) {
return ( return (
<Dropdown trigger="click" menu={menu}> <Dropdown trigger="click" menu={menu}>
<Button loading={loading}>{t("general.labels.print")}</Button> <Button icon={<PrinterFilled />} loading={loading}>
{t("general.labels.print")}
</Button>
</Dropdown> </Dropdown>
); );
} }

View File

@@ -1,4 +1,3 @@
import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Button } from "antd"; import { Button } from "antd";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
@@ -28,7 +27,6 @@ const mapStateToProps = createStructuredSelector({
* @param {Object} [props.buttonStyle={}] - Style object for the Ant Design button. * @param {Object} [props.buttonStyle={}] - Style object for the Ant Design button.
* @param {Object} [props.buttonIconStyle={}] - Style object for the icon within the button. * @param {Object} [props.buttonIconStyle={}] - Style object for the icon within the button.
* @param {string} [props.linkText] - Text to display on the button or link. * @param {string} [props.linkText] - Text to display on the button or link.
* @param {Object} [props.additionalProps] - Additional props to pass to the rendered component.
* @returns {React.ReactElement} A button or text link for sharing to Microsoft Teams. * @returns {React.ReactElement} A button or text link for sharing to Microsoft Teams.
*/ */
const ShareToTeamsComponent = ({ const ShareToTeamsComponent = ({
@@ -40,8 +38,7 @@ const ShareToTeamsComponent = ({
noIconStyle = {}, noIconStyle = {},
buttonStyle = {}, buttonStyle = {},
buttonIconStyle = {}, buttonIconStyle = {},
linkText, linkText
...additionalProps
}) => { }) => {
const location = useLocation(); const location = useLocation();
const { t } = useTranslation(); const { t } = useTranslation();
@@ -87,7 +84,7 @@ const ShareToTeamsComponent = ({
if (noIcon) { if (noIcon) {
return ( return (
<div style={{ cursor: "pointer", ...noIconStyle }} onClick={handleShare} {...additionalProps}> <div style={{ cursor: "pointer", ...noIconStyle }} onClick={handleShare}>
{!linkText ? t("general.actions.sharetoteams") : linkText} {!linkText ? t("general.actions.sharetoteams") : linkText}
</div> </div>
); );
@@ -104,7 +101,6 @@ const ShareToTeamsComponent = ({
icon={<SiMicrosoftteams style={{ color: "#FFFFFF", ...buttonIconStyle }} />} icon={<SiMicrosoftteams style={{ color: "#FFFFFF", ...buttonIconStyle }} />}
onClick={handleShare} onClick={handleShare}
title={linkText === null ? t("general.actions.sharetoteams") : linkText} title={linkText === null ? t("general.actions.sharetoteams") : linkText}
{...additionalProps}
/> />
); );
}; };
@@ -117,8 +113,7 @@ ShareToTeamsComponent.propTypes = {
noIconStyle: PropTypes.object, noIconStyle: PropTypes.object,
buttonStyle: PropTypes.object, buttonStyle: PropTypes.object,
buttonIconStyle: PropTypes.object, buttonIconStyle: PropTypes.object,
linkText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), linkText: PropTypes.oneOfType([PropTypes.string, PropTypes.node])
additionalProps: PropTypes.oneOfType([PropTypes.object, PropTypes.node])
}; };
export default connect(mapStateToProps)(ShareToTeamsComponent); export default connect(mapStateToProps)(ShareToTeamsComponent);

View File

@@ -1527,7 +1527,7 @@
"addDocuments": "Add Job Documents", "addDocuments": "Add Job Documents",
"addNote": "Add Note", "addNote": "Add Note",
"addtopartsqueue": "Add to Parts Queue", "addtopartsqueue": "Add to Parts Queue",
"addtoproduction": "Add In Production Flag", "addtoproduction": "Add to Production",
"addtoscoreboard": "Add to Scoreboard", "addtoscoreboard": "Add to Scoreboard",
"allocate": "Allocate", "allocate": "Allocate",
"autoallocate": "Auto Allocate", "autoallocate": "Auto Allocate",
@@ -1570,7 +1570,7 @@
"printCenter": "Print Center", "printCenter": "Print Center",
"recalculate": "Recalculate", "recalculate": "Recalculate",
"reconcile": "Reconcile", "reconcile": "Reconcile",
"removefromproduction": "Remove In Production Flag", "removefromproduction": "Remove from Production",
"schedule": "Schedule", "schedule": "Schedule",
"sendcsi": "Send CSI", "sendcsi": "Send CSI",
"sendpartspricechange": "Send Parts Price Change", "sendpartspricechange": "Send Parts Price Change",
@@ -2859,7 +2859,8 @@
"touchtime": "T/T", "touchtime": "T/T",
"vertical": "Vertical", "vertical": "Vertical",
"viewname": "View Name", "viewname": "View Name",
"wide": "Wide" "wide": "Wide",
"unassigned": "Unassigned"
}, },
"options": { "options": {
"horizontal": "Horizontal", "horizontal": "Horizontal",

View File

@@ -2859,7 +2859,8 @@
"touchtime": "", "touchtime": "",
"vertical": "", "vertical": "",
"viewname": "", "viewname": "",
"wide": "" "wide": "",
"unassigned": ""
}, },
"options": { "options": {
"horizontal": "", "horizontal": "",

View File

@@ -2859,7 +2859,8 @@
"touchtime": "", "touchtime": "",
"vertical": "", "vertical": "",
"viewname": "", "viewname": "",
"wide": "" "wide": "",
"unassigned": ""
}, },
"options": { "options": {
"horizontal": "", "horizontal": "",