diff --git a/client/src/components/production-list-columns/production-list-columns.add.component.jsx b/client/src/components/production-list-columns/production-list-columns.add.component.jsx index 1d1ea8867..24cca5537 100644 --- a/client/src/components/production-list-columns/production-list-columns.add.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.add.component.jsx @@ -2,7 +2,6 @@ import React from "react"; import { Button, Dropdown } from "antd"; import dataSource from "./production-list-columns.data"; import { useTranslation } from "react-i18next"; - import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectTechnician } from "../../redux/tech/tech.selectors"; @@ -10,16 +9,23 @@ import { selectBodyshop } from "../../redux/user/user.selectors"; import { useSplitTreatments } from "@splitsoftware/splitio-react"; const mapStateToProps = createStructuredSelector({ - //currentUser: selectCurrentUser technician: selectTechnician, bodyshop: selectBodyshop }); -const mapDispatchToProps = (dispatch) => ({ - //setUserLanguage: language => dispatch(setUserLanguage(language)) -}); -export default connect(mapStateToProps, mapDispatchToProps)(ProductionColumnsComponent); -export function ProductionColumnsComponent({ columnState, technician, bodyshop, data, tableState, refetch }) { +const mapDispatchToProps = (dispatch) => ({ + // Add any necessary dispatch actions here +}); + +export function ProductionColumnsComponent({ + columnState, + technician, + bodyshop, + data, + tableState, + refetch, + onColumnAdd +}) { const [columns, setColumns] = columnState; const { t } = useTranslation(); const { @@ -29,18 +35,26 @@ export function ProductionColumnsComponent({ columnState, technician, bodyshop, names: ["Enhanced_Payroll"], splitKey: bodyshop.imexshopid }); + const handleAdd = (e) => { - setColumns([ - ...columns, - ...dataSource({ - bodyshop, - technician, - state: tableState, - data, - activeStatuses: bodyshop.md_ro_statuses.active_statuses, - treatments: { Enhanced_Payroll } - }).filter((i) => i.key === e.key) - ]); + const newColumn = dataSource({ + bodyshop, + technician, + state: tableState, + data, + activeStatuses: bodyshop.md_ro_statuses.active_statuses, + treatments: { Enhanced_Payroll } + }).find((i) => i.key === e.key); + + if (newColumn) { + const updatedColumns = [...columns, newColumn]; + setColumns(updatedColumns); + + // Call the onColumnAdd function passed as a prop + if (onColumnAdd) { + onColumnAdd(newColumn); + } + } }; const columnKeys = columns.map((i) => i.key); @@ -76,12 +90,4 @@ export function ProductionColumnsComponent({ columnState, technician, bodyshop, ); } -// c.key)} -// render={(item) => item.title} -// onChange={(nextTargetKeys, direction, moveKeys) => { -// setColumns(dataSource.filter((i) => nextTargetKeys.includes(i.key))); -// }} -// /> +export default connect(mapStateToProps, mapDispatchToProps)(ProductionColumnsComponent); diff --git a/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx b/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx index bdbb2b5a6..a5f0b0f40 100644 --- a/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx +++ b/client/src/components/production-list-save-config-button/production-list-save-config-button.component.jsx @@ -7,6 +7,7 @@ import { Button, Form, Input, notification, Popover, Space } from "antd"; import { useTranslation } from "react-i18next"; import { UPDATE_SHOP } from "../../graphql/bodyshop.queries"; import { logImEXEvent } from "../../firebase/firebase.utils"; +import { isFunction } from "lodash"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser @@ -16,7 +17,7 @@ const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) }); -export function ProductionListSaveConfigButton({ columns, bodyshop, tableState }) { +export function ProductionListSaveConfigButton({ columns, bodyshop, tableState, onSave }) { const [updateShop] = useMutation(UPDATE_SHOP); const [loading, setLoading] = useState(false); const [open, setOpen] = useState(false); @@ -49,6 +50,9 @@ export function ProductionListSaveConfigButton({ columns, bodyshop, tableState } }); if (!!!result.errors) { notification["success"]({ message: t("bodyshop.successes.save") }); + if (onSave && isFunction(onSave)) { + onSave(); + } } else { notification["error"]({ message: t("bodyshop.errors.saving", { diff --git a/client/src/components/production-list-table/production-list-table-view-select.component.jsx b/client/src/components/production-list-table/production-list-table-view-select.component.jsx index 9a98ede10..f8297fe02 100644 --- a/client/src/components/production-list-table/production-list-table-view-select.component.jsx +++ b/client/src/components/production-list-table/production-list-table-view-select.component.jsx @@ -11,6 +11,7 @@ import { selectTechnician } from "../../redux/tech/tech.selectors"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; import ProductionListColumns from "../production-list-columns/production-list-columns.data"; import { useSplitTreatments } from "@splitsoftware/splitio-react"; +import { isFunction } from "lodash"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -18,7 +19,17 @@ const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser }); -export function ProductionListTable({ refetch, bodyshop, technician, currentUser, state, data, setColumns, setState }) { +export function ProductionListTable({ + refetch, + bodyshop, + technician, + currentUser, + state, + data, + setColumns, + setState, + onProfileChange +}) { const { t } = useTranslation(); const [updateDefaultProdView] = useMutation(UPDATE_ACTIVE_PROD_LIST_VIEW); const [updateShop] = useMutation(UPDATE_SHOP); @@ -32,25 +43,25 @@ export function ProductionListTable({ refetch, bodyshop, technician, currentUser }); const handleSelect = async (value, option) => { - setColumns( - bodyshop.production_config - .filter((pc) => pc.name === value)[0] - .columns.columnKeys.map((k) => { - return { - ...ProductionListColumns({ - bodyshop, - refetch, - technician, - state, - data: data, - activeStatuses: bodyshop.md_ro_statuses.active_statuses, - treatments: { Enhanced_Payroll } - }).find((e) => e.key === k.key), - width: k.width - }; - }) - ); - setState(bodyshop.production_config.filter((pc) => pc.name === value)[0].columns.tableState); + const newColumns = bodyshop.production_config + .filter((pc) => pc.name === value)[0] + .columns.columnKeys.map((k) => { + return { + ...ProductionListColumns({ + bodyshop, + refetch, + technician, + state, + data: data, + activeStatuses: bodyshop.md_ro_statuses.active_statuses, + treatments: { Enhanced_Payroll } + }).find((e) => e.key === k.key), + width: k.width + }; + }); + setColumns(newColumns); + const newState = bodyshop.production_config.filter((pc) => pc.name === value)[0].columns.tableState; + setState(newState); const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email); @@ -72,6 +83,10 @@ export function ProductionListTable({ refetch, bodyshop, technician, currentUser } }); } + + if (onProfileChange && isFunction(onProfileChange)) { + onProfileChange({ value, option, newColumns, newState, assoc }); + } }; const handleTrash = async (name) => { diff --git a/client/src/components/production-list-table/production-list-table.component.jsx b/client/src/components/production-list-table/production-list-table.component.jsx index 12938829d..6e9e1f918 100644 --- a/client/src/components/production-list-table/production-list-table.component.jsx +++ b/client/src/components/production-list-table/production-list-table.component.jsx @@ -1,8 +1,6 @@ -import { SyncOutlined } from "@ant-design/icons"; -import { useSplitTreatments } from "@splitsoftware/splitio-react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; import { Button, Dropdown, Input, Space, Statistic, Table } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; -import React, { useEffect, useMemo, useState } from "react"; import ReactDragListView from "react-drag-listview"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; @@ -16,6 +14,11 @@ import ProductionListSaveConfigButton from "../production-list-save-config-butto import ProductionListPrint from "./production-list-print.component"; import ProductionListTableViewSelect from "./production-list-table-view-select.component"; import ResizeableTitle from "./production-list-table.resizeable.component"; +import { useSplitTreatments } from "@splitsoftware/splitio-react"; +import { SyncOutlined } from "@ant-design/icons"; +import Prompt from "../../utils/prompt.js"; +import _ from "lodash"; +import AlertComponent from "../alert/alert.component.jsx"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -25,6 +28,7 @@ const mapStateToProps = createStructuredSelector({ export function ProductionListTable({ loading, data, refetch, bodyshop, technician, currentUser }) { const [searchText, setSearchText] = useState(""); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); const { treatments: { Production_List_Status_Colors, Enhanced_Payroll } @@ -35,10 +39,9 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici }); const assoc = bodyshop.associations.find((a) => a.useremail === currentUser.email); - const defaultView = assoc && assoc.default_prod_list_view; - const [state, setState] = useState( + const initialStateRef = useRef( (bodyshop.production_config && bodyshop.production_config.find((p) => p.name === defaultView)?.columns.tableState) || bodyshop.production_config[0]?.columns.tableState || { @@ -47,80 +50,102 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici } ); - const { t } = useTranslation(); - - const matchingColumnConfig = useMemo(() => { - return bodyshop.production_config.find((p) => p.name === defaultView); - }, [bodyshop.production_config, defaultView]); - - const [columns, setColumns] = useState( - (state && - matchingColumnConfig && - matchingColumnConfig.columns.columnKeys.map((k) => { - return { - ...ProductionListColumns({ - bodyshop, - refetch, - technician, - state, - data, - activeStatuses: bodyshop.md_ro_statuses.active_statuses, - treatments: { Production_List_Status_Colors, Enhanced_Payroll } - }).find((e) => e.key === k.key), - width: k.width ?? 100 - }; - })) || - [] - ); - - useEffect(() => { - const newColumns = - (state && - matchingColumnConfig && - matchingColumnConfig.columns.columnKeys.map((k) => { + const initialColumnsRef = useRef( + (initialStateRef.current && + bodyshop.production_config + .find((p) => p.name === defaultView) + ?.columns.columnKeys.map((k) => { return { ...ProductionListColumns({ bodyshop, - technician, refetch, - state, - data: data, + technician, + state: initialStateRef.current, + data, activeStatuses: bodyshop.md_ro_statuses.active_statuses, treatments: { Production_List_Status_Colors, Enhanced_Payroll } }).find((e) => e.key === k.key), width: k.width ?? 100 }; })) || - []; - setColumns(newColumns); - // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + const [state, setState] = useState(initialStateRef.current); + const [columns, setColumns] = useState(initialColumnsRef.current); + + const { t } = useTranslation(); + + const matchingColumnConfig = useMemo(() => { + return bodyshop.production_config.find((p) => p.name === defaultView); + }, [bodyshop.production_config, defaultView]); + + useEffect(() => { + const newColumns = + matchingColumnConfig?.columns.columnKeys.map((k) => { + return { + ...ProductionListColumns({ + bodyshop, + technician, + refetch, + state, + data: data, + activeStatuses: bodyshop.md_ro_statuses.active_statuses, + treatments: { Production_List_Status_Colors, Enhanced_Payroll } + }).find((e) => e.key === k.key), + width: k.width ?? 100 + }; + }) || []; + + // Only update columns if they haven't been manually changed by the user + if (_.isEqual(initialColumnsRef.current, columns)) { + setColumns(newColumns); + } }, [ - //state, matchingColumnConfig, bodyshop, technician, - data - ]); //State removed from dependency array as it causes race condition when removing columns from table view and is not needed. + data, + Enhanced_Payroll, + Production_List_Status_Colors, + refetch, + state, + columns + ]); const handleTableChange = (pagination, filters, sorter) => { - setState({ + const newState = { ...state, filteredInfo: filters, sortedInfo: { columnKey: sorter.columnKey, order: sorter.order } - }); + }; + if (!_.isEqual(newState, state)) { + setState(newState); + setHasUnsavedChanges(true); + } }; const onDragEnd = (fromIndex, toIndex) => { - const columnsCopy = columns.slice(); - const item = columnsCopy.splice(fromIndex, 1)[0]; - columnsCopy.splice(toIndex, 0, item); - setColumns(columnsCopy); + if (fromIndex === toIndex) return; + + const columnsCopy = [...columns]; + const [movedItem] = columnsCopy.splice(fromIndex, 1); + columnsCopy.splice(toIndex, 0, movedItem); + + if (!_.isEqual(columnsCopy, columns)) { + setColumns(columnsCopy); + setHasUnsavedChanges(true); + } }; const removeColumn = (e) => { const { key } = e; const newColumns = columns.filter((i) => i.key !== key); - setColumns(newColumns); + + if (!_.isEqual(newColumns, columns)) { + setColumns(newColumns); + setHasUnsavedChanges(true); + } }; const handleResize = @@ -131,9 +156,21 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici ...nextColumns[index], width: size.width }; - setColumns(nextColumns); + + if (!_.isEqual(nextColumns, columns)) { + setColumns(nextColumns); + setHasUnsavedChanges(true); + } }; + const addColumn = (newColumn) => { + const updatedColumns = [...columns, newColumn]; + if (!_.isEqual(updatedColumns, columns)) { + setColumns(updatedColumns); + setHasUnsavedChanges(true); + } + }; + const headerItem = (col) => { const menu = { onClick: removeColumn, @@ -152,29 +189,29 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici ); }; - const dataSource = - searchText === "" - ? data - : data.filter( - (j) => - (j.ro_number || "").toString().toLowerCase().includes(searchText.toLowerCase()) || - (j.ownr_co_nm || "").toLowerCase().includes(searchText.toLowerCase()) || - (j.ownr_fn || "").toLowerCase().includes(searchText.toLowerCase()) || - (j.ownr_ln || "").toLowerCase().includes(searchText.toLowerCase()) || - (j.status || "").toLowerCase().includes(searchText.toLowerCase()) || - (j.ins_co_nm || "").toLowerCase().includes(searchText.toLowerCase()) || - (j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) || - (j.v_model_desc || "").toLowerCase().includes(searchText.toLowerCase()) || - (j.v_make_desc || "").toLowerCase().includes(searchText.toLowerCase()) - ); + const resetChanges = () => { + setState(initialStateRef.current); + setColumns(initialColumnsRef.current); + setHasUnsavedChanges(false); + }; - // const handleSelectRecord = (record) => { - // if (selected !== record.id) { - // setSelected(record.id); - // } else { - // setSelected(null); - // } - // }; + const filterData = (item, searchText) => { + const fieldsToSearch = [ + item.ro_number, + item.ownr_co_nm, + item.ownr_fn, + item.ownr_ln, + item.status, + item.ins_co_nm, + item.clm_no, + item.v_model_desc, + item.v_make_desc + ]; + + return fieldsToSearch.some((field) => (field || "").toString().toLowerCase().includes(searchText.toLowerCase())); + }; + + const dataSource = searchText === "" ? data : data.filter((j) => filterData(j, searchText)); if (!!!columns) return
No columns found.
; @@ -186,8 +223,29 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici .toFixed(1); const totalLAB = data.reduce((acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0), 0).toFixed(1); const totalLAR = data.reduce((acc, val) => acc + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0), 0).toFixed(1); + return (
+ + {hasUnsavedChanges && ( + + {t("general.messages.unsavedchanges")} + + {t("general.actions.reset")} + +
+ } + /> + )} @@ -199,20 +257,37 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici } extra={ - - - - + + { + setHasUnsavedChanges(false); + }} + /> { + initialStateRef.current = state; + setHasUnsavedChanges(false); + }} refetch={refetch} data={data} /> - setSearchText(e.target.value)} placeholder={t("general.labels.search")}