From 92a3e57205d92a23582f6f269d2ca334ee7c5e4f Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Tue, 28 Apr 2026 15:46:40 -0700 Subject: [PATCH 1/4] IO-3667 Visual Production Title Color Signed-off-by: Allan Carr --- .../production-board-kanban-card.component.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx b/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx index dbb96a6ba..81ebb197c 100644 --- a/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx @@ -431,6 +431,7 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe Date: Tue, 28 Apr 2026 16:44:23 -0700 Subject: [PATCH 2/4] IO-3650 Pagination Corrections Signed-off-by: Allan Carr --- .../owners-list/owners-list.component.jsx | 17 +++++++++------ .../owners-list/owners-list.container.jsx | 11 +++++++--- .../parts-queue.list.component.jsx | 18 +++++++++++----- .../vehicles-list/vehicles-list.component.jsx | 17 +++++++++------ .../vehicles-list/vehicles-list.container.jsx | 10 ++++++--- .../export-logs.page.component.jsx | 21 +++++++++++++------ 6 files changed, 65 insertions(+), 29 deletions(-) diff --git a/client/src/components/owners-list/owners-list.component.jsx b/client/src/components/owners-list/owners-list.component.jsx index eefec34ca..e6fe1efb9 100644 --- a/client/src/components/owners-list/owners-list.component.jsx +++ b/client/src/components/owners-list/owners-list.component.jsx @@ -11,12 +11,13 @@ import ResponsiveTable from "../responsive-table/responsive-table.component"; export default function OwnersListComponent({ loading, owners, total, refetch }) { const search = queryString.parse(useLocation().search); - const { - page - // sortcolumn, sortorder - } = search; + const { page, pageSize } = search; const history = useNavigate(); + const currentPage = Number.parseInt(page || "1", 10); + const parsedPageSize = Number.parseInt(pageSize || String(pageLimit), 10); + const currentPageSize = Number.isNaN(parsedPageSize) ? pageLimit : parsedPageSize; + const [state, setState] = useState({ sortedInfo: {}, filteredInfo: { text: "" } @@ -71,10 +72,14 @@ export default function OwnersListComponent({ loading, owners, total, refetch }) ]; const handleTableChange = (pagination, filters, sorter) => { + const nextPageSize = pagination?.pageSize || currentPageSize; + const pageSizeChanged = nextPageSize !== currentPageSize; + setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); const updatedSearch = { ...search, - page: pagination.current, + pageSize: nextPageSize, + page: pageSizeChanged ? 1 : pagination.current, sortcolumn: sorter.columnKey, sortorder: sorter.order }; @@ -119,7 +124,7 @@ export default function OwnersListComponent({ loading, owners, total, refetch }) > { - // searchParams.page = pagination.current; + const nextPageSize = pagination?.pageSize || currentPageSize; + const pageSizeChanged = nextPageSize !== currentPageSize; + + searchParams.pageSize = nextPageSize; + searchParams.page = pageSizeChanged ? 1 : pagination.current; searchParams.sortcolumn = sorter.columnKey; searchParams.sortorder = sorter.order; @@ -315,9 +322,10 @@ export function PartsQueueListComponent({ bodyshop }) { loading={loading} pagination={{ placement: "top", - pageSize: pageLimit - // current: parseInt(page || 1), - // total: data && data.jobs_aggregate.aggregate.count, + pageSize: currentPageSize, + current: currentPage, + showSizeChanger: true, + total: jobs.length }} columns={columns} mobileColumnKeys={["ro_number", "ownr_ln", "status", "vehicle", "partsstatus"]} diff --git a/client/src/components/vehicles-list/vehicles-list.component.jsx b/client/src/components/vehicles-list/vehicles-list.component.jsx index 41cc1d764..5d6b33fc8 100644 --- a/client/src/components/vehicles-list/vehicles-list.component.jsx +++ b/client/src/components/vehicles-list/vehicles-list.component.jsx @@ -11,12 +11,13 @@ import ResponsiveTable from "../responsive-table/responsive-table.component"; export default function VehiclesListComponent({ loading, vehicles, total, refetch, basePath = "/manage" }) { const search = queryString.parse(useLocation().search); - const { - page - //sortcolumn, sortorder, - } = search; + const { page, pageSize } = search; const history = useNavigate(); + const currentPage = Number.parseInt(page || "1", 10); + const parsedPageSize = Number.parseInt(pageSize || String(pageLimit), 10); + const currentPageSize = Number.isNaN(parsedPageSize) ? pageLimit : parsedPageSize; + const [state, setState] = useState({ sortedInfo: {}, filteredInfo: { text: "" } @@ -62,10 +63,14 @@ export default function VehiclesListComponent({ loading, vehicles, total, refetc ]; const handleTableChange = (pagination, filters, sorter) => { + const nextPageSize = pagination?.pageSize || currentPageSize; + const pageSizeChanged = nextPageSize !== currentPageSize; + setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); const updatedSearch = { ...search, - page: pagination.current, + pageSize: nextPageSize, + page: pageSizeChanged ? 1 : pagination.current, sortcolumn: sorter.columnKey, sortorder: sorter.order }; @@ -106,7 +111,7 @@ export default function VehiclesListComponent({ loading, vehicles, total, refetc > ; const handleTableChange = (pagination, filters, sorter) => { - searchParams.page = pagination.current; + const nextPageSize = pagination?.pageSize || currentPageSize; + const pageSizeChanged = nextPageSize !== currentPageSize; + + searchParams.pageSize = nextPageSize; + searchParams.page = pageSizeChanged ? 1 : pagination.current; searchParams.sortcolumn = sorter.columnKey; searchParams.sortorder = sorter.order; if (filters.status) { @@ -191,8 +199,9 @@ export function ExportLogsPageComponent() { loading={loading} pagination={{ placement: "top", - pageSize: pageLimit, - current: parseInt(page || 1, 10), + pageSize: currentPageSize, + current: currentPage, + showSizeChanger: true, total: data && data.search_exportlog_aggregate.aggregate.count }} columns={columns} From 614420d7d223301652535849082e6dac22343503 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Tue, 28 Apr 2026 18:06:11 -0700 Subject: [PATCH 3/4] IO-3562 Visual Production Board Statistics - Exclude Suspended Jobs Signed-off-by: Allan Carr --- .../production-board-kanban.statistics.jsx | 94 ++++++++++++------- .../settings/StatisticsSettings.jsx | 11 ++- .../settings/defaultKanbanSettings.js | 3 +- client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 6 files changed, 76 insertions(+), 35 deletions(-) diff --git a/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx b/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx index cc8c46332..b31d58b84 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.statistics.jsx @@ -28,11 +28,14 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => { const { t } = useTranslation(); const calculateTotal = (items, key, subKey) => { - return items.reduce((acc, item) => acc + (item[key]?.aggregate?.sum?.[subKey] || 0), 0); + return items.reduce((acc, item) => acc + (item?.[key]?.aggregate?.sum?.[subKey] ?? 0), 0); }; const calculateTotalAmount = (items, key) => { - return items.reduce((acc, item) => acc.add(Dinero(item[key]?.totals?.subtotal ?? Dinero())), Dinero({ amount: 0 })); + return items.reduce( + (acc, item) => acc.add(Dinero(item?.[key]?.totals?.subtotal ?? Dinero())), + Dinero({ amount: 0 }) + ); }; const calculateReducerTotalAmount = (lanes, key) => { @@ -67,58 +70,83 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => { return value; }; + const filteredData = cardSettings.excludeSuspended === true ? data.filter((item) => item.suspended !== true) : data; + const filteredReducerData = + cardSettings.excludeSuspended === true + ? { + ...reducerData, + lanes: reducerData.lanes.map((lane) => ({ + ...lane, + cards: lane.cards.filter((card) => card.metadata.suspended !== true) + })) + } + : reducerData; + const totalHrs = cardSettings.totalHrs - ? parseFloat((calculateTotal(data, "labhrs", "mod_lb_hrs") + calculateTotal(data, "larhrs", "mod_lb_hrs")).toFixed(2)) + ? parseFloat( + ( + calculateTotal(filteredData, "labhrs", "mod_lb_hrs") + calculateTotal(filteredData, "larhrs", "mod_lb_hrs") + ).toFixed(2) + ) : null; const totalLAB = cardSettings.totalLAB - ? parseFloat(calculateTotal(data, "labhrs", "mod_lb_hrs").toFixed(2)) + ? parseFloat(calculateTotal(filteredData, "labhrs", "mod_lb_hrs").toFixed(2)) : null; const totalLAR = cardSettings.totalLAR - ? parseFloat(calculateTotal(data, "larhrs", "mod_lb_hrs").toFixed(2)) + ? parseFloat(calculateTotal(filteredData, "larhrs", "mod_lb_hrs").toFixed(2)) : null; - const jobsInProduction = cardSettings.jobsInProduction ? data.length : null; + const jobsInProduction = cardSettings.jobsInProduction ? filteredData.length : null; const totalAmountInProduction = cardSettings.totalAmountInProduction - ? calculateTotalAmount(data, "job_totals").toFormat("$0,0.00") + ? calculateTotalAmount(filteredData, "job_totals").toFormat("$0,0.00") : null; - const totalAmountOnBoard = reducerData && cardSettings.totalAmountOnBoard - ? calculateReducerTotalAmount(reducerData.lanes, "job_totals").toFormat("$0,0.00") - : null; + const totalAmountOnBoard = + filteredReducerData && cardSettings.totalAmountOnBoard + ? calculateReducerTotalAmount(filteredReducerData.lanes, "job_totals").toFormat("$0,0.00") + : null; - const totalHrsOnBoard = reducerData && cardSettings.totalHrsOnBoard - ? parseFloat(( - calculateReducerTotal(reducerData.lanes, "labhrs", "mod_lb_hrs") + - calculateReducerTotal(reducerData.lanes, "larhrs", "mod_lb_hrs") - ).toFixed(2)) - : null; + const totalHrsOnBoard = + filteredReducerData && cardSettings.totalHrsOnBoard + ? parseFloat( + ( + calculateReducerTotal(filteredReducerData.lanes, "labhrs", "mod_lb_hrs") + + calculateReducerTotal(filteredReducerData.lanes, "larhrs", "mod_lb_hrs") + ).toFixed(2) + ) + : null; - const totalLABOnBoard = reducerData && cardSettings.totalLABOnBoard - ? parseFloat(calculateReducerTotal(reducerData.lanes, "labhrs", "mod_lb_hrs").toFixed(2)) - : null; + const totalLABOnBoard = + filteredReducerData && cardSettings.totalLABOnBoard + ? parseFloat(calculateReducerTotal(filteredReducerData.lanes, "labhrs", "mod_lb_hrs").toFixed(2)) + : null; - const totalLAROnBoard = reducerData && cardSettings.totalLAROnBoard - ? parseFloat(calculateReducerTotal(reducerData.lanes, "larhrs", "mod_lb_hrs").toFixed(2)) - : null; + const totalLAROnBoard = + filteredReducerData && cardSettings.totalLAROnBoard + ? parseFloat(calculateReducerTotal(filteredReducerData.lanes, "larhrs", "mod_lb_hrs").toFixed(2)) + : null; - const jobsOnBoard = reducerData && cardSettings.jobsOnBoard - ? reducerData.lanes.reduce((acc, lane) => acc + lane.cards.length, 0) - : null; + const jobsOnBoard = + filteredReducerData && cardSettings.jobsOnBoard + ? filteredReducerData.lanes.reduce((acc, lane) => acc + lane.cards.length, 0) + : null; const tasksInProduction = cardSettings.tasksInProduction - ? data.reduce((acc, item) => acc + (item.tasks_aggregate?.aggregate?.count || 0), 0) + ? filteredData.reduce((acc, item) => acc + (item.tasks_aggregate?.aggregate?.count || 0), 0) : null; - const tasksOnBoard = reducerData && cardSettings.tasksOnBoard - ? reducerData.lanes.reduce((acc, lane) => { - return ( - acc + lane.cards.reduce((laneAcc, card) => laneAcc + (card.metadata.tasks_aggregate?.aggregate?.count || 0), 0) - ); - }, 0) - : null; + const tasksOnBoard = + filteredReducerData && cardSettings.tasksOnBoard + ? filteredReducerData.lanes.reduce((acc, lane) => { + return ( + acc + + lane.cards.reduce((laneAcc, card) => laneAcc + (card.metadata.tasks_aggregate?.aggregate?.count || 0), 0) + ); + }, 0) + : null; const statistics = mergeStatistics(statisticsItems, [ { id: 0, value: totalHrs, type: StatisticType.HOURS }, diff --git a/client/src/components/production-board-kanban/settings/StatisticsSettings.jsx b/client/src/components/production-board-kanban/settings/StatisticsSettings.jsx index 1645bc05d..d283f6122 100644 --- a/client/src/components/production-board-kanban/settings/StatisticsSettings.jsx +++ b/client/src/components/production-board-kanban/settings/StatisticsSettings.jsx @@ -14,7 +14,16 @@ const StatisticsSettings = ({ t, statisticsOrder, setStatisticsOrder, setHasChan }; return ( - + + + {t("production.settings.statistics.exclude_suspended")} + + + } + > {(provided) => ( diff --git a/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js b/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js index 1c7a264b7..93486a11d 100644 --- a/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js +++ b/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js @@ -91,7 +91,8 @@ const defaultKanbanSettings = { subtotal: false, statisticsOrder: statisticsItems.map((item) => item.id), selectedMdInsCos: [], - selectedEstimators: [] + selectedEstimators: [], + excludeSuspended: false }; const defaultFilters = { search: "", employeeId: null, alert: false }; diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 976222c46..9e41f90d8 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -3265,6 +3265,7 @@ "information": "Information", "layout": "Layout", "statistics": { + "exclude_suspended": "Exclude Suspended Jobs", "jobs_in_production": "Jobs in Production", "tasks_in_production": "Tasks in Production", "tasks_in_view": "Tasks in View", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 619b7f0b1..987149079 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -3259,6 +3259,7 @@ "information": "", "layout": "", "statistics": { + "exclude_suspended": "", "jobs_in_production": "", "tasks_in_production": "", "tasks_in_view": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index a0c5be98e..357d6aa4b 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -3259,6 +3259,7 @@ "information": "", "layout": "", "statistics": { + "exclude_suspended": "", "jobs_in_production": "", "tasks_in_production": "", "tasks_in_view": "", From 6242e0f30993405b265cafaca89963fc63858454 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Tue, 28 Apr 2026 18:10:49 -0700 Subject: [PATCH 4/4] IO-3667 - Correction for Styling Signed-off-by: Allan Carr --- .../production-board-kanban-card.component.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx b/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx index 81ebb197c..c12a7c6c9 100644 --- a/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban-card.component.jsx @@ -431,7 +431,7 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe