Merged in feature/IO-2970-Production-Board-Unassigned-Filter (pull request #2096)

feature/IO-2970-Production-Board-Unassigned-Filter - Implementation
This commit is contained in:
Dave Richer
2025-01-31 18:24:50 +00:00
8 changed files with 46 additions and 18 deletions

View File

@@ -1,9 +1,14 @@
import { Button, Input, Space, Spin } from "antd";
import React, { useState } from "react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
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 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 }) {
const { t } = useTranslation();
const [alertFilter, setAlertFilter] = useState(false);
const [unassignedFilter, setUnassignedFilter] = useState(false);
const toggleAlertFilter = () => {
const newAlertFilter = !alertFilter;
@@ -27,6 +33,12 @@ export function ProductionBoardFilters({ bodyshop, filter, setFilter, loading })
setFilter({ ...filter, alert: newAlertFilter });
};
const toggleUnassignedFilter = () => {
const newUnassignedFilter = !unassignedFilter;
setUnassignedFilter(newUnassignedFilter);
setFilter({ ...filter, unassigned: newUnassignedFilter });
};
return (
<Space wrap>
{loading && <Spin />}
@@ -52,6 +64,13 @@ export function ProductionBoardFilters({ bodyshop, filter, setFilter, loading })
>
{t("production.labels.alerts")}
</Button>
<Button
type={unassignedFilter ? "primary" : "default"}
onClick={toggleUnassignedFilter}
icon={unassignedFilter ? <UserDeleteOutlined /> : <UsergroupDeleteOutlined />}
>
{t("production.labels.unassigned")}
</Button>
</Space>
);
}

View File

@@ -29,7 +29,7 @@ const sortByParentId = (arr) => {
// Function to create board data based on statuses and jobs, with optional filtering
export const createBoardData = ({ statuses, data, filter, cardSettings }) => {
const { search, employeeId, alert } = filter;
const { search, employeeId, alert, unassigned } = filter;
const lanes = statuses.map((status) => ({
id: status,
@@ -40,6 +40,13 @@ export const createBoardData = ({ statuses, data, filter, cardSettings }) => {
let filteredJobs =
(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
if (cardSettings?.selectedMdInsCos?.length > 0) {
filteredJobs = filteredJobs.filter((job) => cardSettings.selectedMdInsCos.includes(job.ins_co_nm));

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client";
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 { UPDATE_KANBAN_SETTINGS } from "../../../graphql/user.queries.js";
import { defaultKanbanSettings, mergeWithDefaults } from "./defaultKanbanSettings.js";
@@ -11,6 +11,7 @@ import FilterSettings from "./FilterSettings.jsx";
import PropTypes from "prop-types";
import { isFunction } from "lodash";
import { useNotification } from "../../../contexts/Notifications/notificationContext.jsx";
import { SettingOutlined } from "@ant-design/icons";
function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bodyshop, data, onSettingsChange }) {
const [form] = Form.useForm();
@@ -153,7 +154,7 @@ function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bod
return (
<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")}
</Button>
</Popover>

View File

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

View File

@@ -1,4 +1,3 @@
import React from "react";
import PropTypes from "prop-types";
import { Button } from "antd";
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.buttonIconStyle={}] - Style object for the icon within the button.
* @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.
*/
const ShareToTeamsComponent = ({
@@ -40,8 +38,7 @@ const ShareToTeamsComponent = ({
noIconStyle = {},
buttonStyle = {},
buttonIconStyle = {},
linkText,
...additionalProps
linkText
}) => {
const location = useLocation();
const { t } = useTranslation();
@@ -87,7 +84,7 @@ const ShareToTeamsComponent = ({
if (noIcon) {
return (
<div style={{ cursor: "pointer", ...noIconStyle }} onClick={handleShare} {...additionalProps}>
<div style={{ cursor: "pointer", ...noIconStyle }} onClick={handleShare}>
{!linkText ? t("general.actions.sharetoteams") : linkText}
</div>
);
@@ -104,7 +101,6 @@ const ShareToTeamsComponent = ({
icon={<SiMicrosoftteams style={{ color: "#FFFFFF", ...buttonIconStyle }} />}
onClick={handleShare}
title={linkText === null ? t("general.actions.sharetoteams") : linkText}
{...additionalProps}
/>
);
};
@@ -117,8 +113,7 @@ ShareToTeamsComponent.propTypes = {
noIconStyle: PropTypes.object,
buttonStyle: PropTypes.object,
buttonIconStyle: PropTypes.object,
linkText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
additionalProps: PropTypes.oneOfType([PropTypes.object, PropTypes.node])
linkText: PropTypes.oneOfType([PropTypes.string, PropTypes.node])
};
export default connect(mapStateToProps)(ShareToTeamsComponent);

View File

@@ -2859,7 +2859,8 @@
"touchtime": "T/T",
"vertical": "Vertical",
"viewname": "View Name",
"wide": "Wide"
"wide": "Wide",
"unassigned": "Unassigned"
},
"options": {
"horizontal": "Horizontal",

View File

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

View File

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