- Add Tasks to production board (and required refactors)

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-08-07 10:36:00 -04:00
parent 547e279693
commit 5d1f61753b
12 changed files with 121 additions and 64 deletions

View File

@@ -0,0 +1,51 @@
import { Col, List, Space, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
const CardColorLegend = ({ bodyshop }) => {
const { t } = useTranslation();
const data = bodyshop.ssbuckets.map((bucket) => {
let color = { r: 255, g: 255, b: 255 };
if (bucket.color) {
color = bucket.color;
if (bucket.color.rgb) {
color = bucket.color.rgb;
}
}
return {
label: bucket.label,
color
};
});
return (
<Col>
<Typography>{t("production.labels.legend")}</Typography>
<List
grid={{
gutter: 16
}}
dataSource={data}
renderItem={(item) => (
<List.Item>
<Space>
<div
style={{
width: "1.5rem",
aspectRatio: "1/1",
backgroundColor: `rgba(${item.color.r},${item.color.g},${item.color.b},${item.color.a})`
}}
></div>
<div>{item.label}</div>
</Space>
</List.Item>
)}
/>
</Col>
);
};
export default CardColorLegend;

View File

@@ -0,0 +1,444 @@
import {
BranchesOutlined,
CalendarOutlined,
DownloadOutlined,
EyeFilled,
PauseCircleOutlined
} from "@ant-design/icons";
import { Card, Col, Row, Space, Tooltip } from "antd";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import Dinero from "dinero.js";
import ProductionAlert from "../production-list-columns/production-list-columns.alert.component";
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
import dayjs from "../../utils/day";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
const cardColor = (ssbuckets, totalHrs) => {
const bucket = ssbuckets.find((bucket) => bucket.gte <= totalHrs && (!bucket.lt || bucket.lt > totalHrs));
return bucket && bucket.color ? bucket.color.rgb || bucket.color : { r: 255, g: 255, b: 255 };
};
const getContrastYIQ = (bgColor) =>
(bgColor.r * 299 + bgColor.g * 587 + bgColor.b * 114) / 1000 >= 128 ? "black" : "white";
const findEmployeeById = (employees, id) => employees.find((e) => e.id === id);
const EllipsesToolTip = React.memo(({ title, children, kiosk }) => {
if (kiosk || !title) {
return <div className="ellipses no-select">{children}</div>;
}
return (
<Tooltip title={title}>
<div className="ellipses">{children}</div>
</Tooltip>
);
});
const OwnerNameToolTip = ({ metadata, cardSettings }) =>
cardSettings?.ownr_nm && (
<Col span={24}>
<EllipsesToolTip
title={metadata.ownr_ln || metadata.ownr_co_nm ? <OwnerNameDisplay ownerObject={metadata} /> : null}
kiosk={cardSettings.kiosk}
>
{metadata.ownr_ln || metadata.ownr_co_nm ? (
cardSettings.compact ? (
`${metadata.ownr_ln || ""} ${metadata.ownr_co_nm || ""}`
) : (
<OwnerNameDisplay ownerObject={metadata} />
)
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
);
const ModelInfoToolTip = ({ metadata, cardSettings }) =>
cardSettings?.model_info && (
<Col span={24}>
<EllipsesToolTip
title={
metadata.v_model_yr || metadata.v_make_desc || metadata.v_model_desc
? `${metadata.v_model_yr || ""} ${metadata.v_make_desc || ""} ${metadata.v_model_desc || ""}`
: null
}
kiosk={cardSettings.kiosk}
>
{metadata.v_model_yr || metadata.v_make_desc || metadata.v_model_desc ? (
`${metadata.v_model_yr || ""} ${metadata.v_make_desc || ""} ${metadata.v_model_desc || ""}`
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
);
const InsuranceCompanyToolTip = ({ metadata, cardSettings }) =>
cardSettings?.ins_co_nm && (
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip title={metadata.ins_co_nm || null} kiosk={cardSettings.kiosk}>
{metadata.ins_co_nm ? metadata.ins_co_nm : <span>&nbsp;</span>}
</EllipsesToolTip>
</Col>
);
const ClaimNumberToolTip = ({ metadata, cardSettings }) =>
cardSettings?.clm_no && (
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip title={metadata.clm_no || null} kiosk={cardSettings.kiosk}>
{metadata.clm_no ? metadata.clm_no : <span>&nbsp;</span>}
</EllipsesToolTip>
</Col>
);
const EmployeeAssignmentsToolTip = ({
metadata,
cardSettings,
employee_body,
employee_prep,
employee_refinish,
employee_csr
}) =>
cardSettings?.employeeassignments && (
<Col span={24}>
<Row>
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip
title={
employee_body || metadata.labhrs.aggregate.sum.mod_lb_hrs
? `B: ${employee_body ? `${employee_body.first_name.substring(0, 3)} ${employee_body.last_name.charAt(0)}` : ""} ${metadata.labhrs.aggregate.sum.mod_lb_hrs || "?"}h`
: null
}
kiosk={cardSettings.kiosk}
>
{employee_body || metadata.labhrs.aggregate.sum.mod_lb_hrs ? (
`B: ${employee_body ? `${employee_body.first_name.substring(0, 3)} ${employee_body.last_name.charAt(0)}` : ""} ${metadata.labhrs.aggregate.sum.mod_lb_hrs || "?"}h`
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip
title={
employee_prep
? `P: ${employee_prep ? `${employee_prep.first_name.substring(0, 3)} ${employee_prep.last_name.charAt(0)}` : ""}`
: null
}
kiosk={cardSettings.kiosk}
>
{employee_prep ? (
`P: ${employee_prep ? `${employee_prep.first_name.substring(0, 3)} ${employee_prep.last_name.charAt(0)}` : ""}`
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip
title={
employee_refinish || metadata.larhrs.aggregate.sum.mod_lb_hrs
? `R: ${employee_refinish ? `${employee_refinish.first_name.substring(0, 3)} ${employee_refinish.last_name.charAt(0)}` : ""} ${metadata.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`
: null
}
kiosk={cardSettings.kiosk}
>
{employee_refinish || metadata.larhrs.aggregate.sum.mod_lb_hrs ? (
`R: ${employee_refinish ? `${employee_refinish.first_name.substring(0, 3)} ${employee_refinish.last_name.charAt(0)}` : ""} ${metadata.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip
title={
employee_csr ? `C: ${employee_csr ? `${employee_csr.first_name} ${employee_csr.last_name}` : ""}` : null
}
kiosk={cardSettings.kiosk}
>
{employee_csr ? (
`C: ${employee_csr ? `${employee_csr.first_name} ${employee_csr.last_name}` : ""}`
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
</Row>
</Col>
);
const ActualInToolTip = ({ metadata, cardSettings }) =>
cardSettings?.actual_in && (
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip title={metadata.actual_in || null} kiosk={cardSettings.kiosk}>
{metadata.actual_in ? (
<Space>
<DownloadOutlined />
<DateTimeFormatter format="MM/DD">{metadata.actual_in}</DateTimeFormatter>
</Space>
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
);
const EstimatorToolTip = ({ metadata, cardSettings }) => {
return (
cardSettings?.estimator && (
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip
title={metadata.est_ct_fn && metadata.est_ct_ln ? `${metadata.est_ct_fn} ${metadata.est_ct_ln}` : null}
kiosk={cardSettings.kiosk}
>
{metadata.est_ct_fn && metadata.est_ct_ln ? (
<span>E: {`${metadata.est_ct_fn} ${metadata.est_ct_ln}`}</span>
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
)
);
};
const SubtotalTooltip = ({ metadata, cardSettings, t }) => {
const amount = metadata?.job_totals?.totals?.subtotal?.amount;
const dineroAmount = amount ? Dinero({ amount: parseInt(amount * 100) }).toFormat("0,0.00") : null;
return (
cardSettings?.subtotal && (
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip
title={!!amount ? `${t("production.statistics.currency_symbol")}${dineroAmount}` : null}
kiosk={cardSettings.kiosk}
>
{!!amount ? (
<span>{`${t("production.statistics.currency_symbol")}${dineroAmount}`}</span>
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
)
);
};
const ScheduledCompletionToolTip = ({ metadata, cardSettings, pastDueAlert }) =>
cardSettings?.scheduled_completion && (
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip title={metadata.scheduled_completion || null} kiosk={cardSettings.kiosk}>
{metadata.scheduled_completion ? (
<Space className={pastDueAlert}>
<CalendarOutlined />
<DateTimeFormatter format="MM/DD">{metadata.scheduled_completion}</DateTimeFormatter>
</Space>
) : (
<span>&nbsp;</span>
)}
</EllipsesToolTip>
</Col>
);
const AltTransportToolTip = ({ metadata, cardSettings }) =>
cardSettings?.ats && (
<Col span={12}>
<EllipsesToolTip title={metadata.alt_transport || null} kiosk={cardSettings.kiosk}>
{metadata.alt_transport ? metadata.alt_transport : <span>&nbsp;</span>}
</EllipsesToolTip>
</Col>
);
const SubletsComponent = ({ metadata, cardSettings }) =>
cardSettings?.sublets && (
<Col span={12}>
{metadata.subletLines ? (
<ProductionSubletsManageComponent subletJobLines={metadata.subletLines} />
) : (
<span>&nbsp;</span>
)}
</Col>
);
const ProductionNoteComponent = ({ metadata, cardSettings, card }) =>
cardSettings?.production_note && (
<Col span={24} style={{ margin: "2px 0" }}>
<ProductionListColumnProductionNote
record={{
production_vars: metadata?.production_vars,
id: card?.id,
refetch: card?.refetch
}}
/>
</Col>
);
const PartsStatusComponent = ({ metadata, cardSettings }) =>
cardSettings?.partsstatus && (
<Col span={24} style={{ textAlign: "center" }}>
{metadata.joblines_status ? <JobPartsQueueCount parts={metadata.joblines_status} /> : <span>&nbsp;</span>}
</Col>
);
const TasksToolTip = ({ metadata, cardSettings, t }) =>
cardSettings?.tasks && (
<Col span={12}>
<EllipsesToolTip
title={`${t("production.labels.tasks")}: ${metadata.tasks_aggregate?.aggregate?.count || 0}`}
kiosk={cardSettings.kiosk}
>
{metadata.tasks_aggregate?.aggregate?.count ? (
`T: ${metadata.tasks_aggregate.aggregate.count}`
) : (
<span>T: 0</span>
)}
</EllipsesToolTip>
</Col>
);
export default function ProductionBoardCard({ technician, card, bodyshop, cardSettings, clone }) {
const { t } = useTranslation();
const { metadata } = card;
const employees = useMemo(() => bodyshop.employees, [bodyshop.employees]);
const { employee_body, employee_prep, employee_refinish, employee_csr } = useMemo(() => {
return {
employee_body: metadata?.employee_body && findEmployeeById(employees, metadata.employee_body),
employee_prep: metadata?.employee_prep && findEmployeeById(employees, metadata.employee_prep),
employee_refinish: metadata?.employee_refinish && findEmployeeById(employees, metadata.employee_refinish),
employee_csr: metadata?.employee_csr && findEmployeeById(employees, metadata.employee_csr)
};
}, [metadata, employees]);
const pastDueAlert = useMemo(() => {
if (!metadata?.scheduled_completion) return null;
const completionDate = dayjs(metadata.scheduled_completion);
if (dayjs().isSameOrAfter(completionDate, "day")) return "production-completion-past";
if (dayjs().add(1, "day").isSame(completionDate, "day")) return "production-completion-soon";
return null;
}, [metadata?.scheduled_completion]);
const totalHrs = useMemo(() => {
return metadata?.labhrs && metadata?.larhrs
? metadata.labhrs.aggregate.sum.mod_lb_hrs + metadata.larhrs.aggregate.sum.mod_lb_hrs
: 0;
}, [metadata?.labhrs, metadata?.larhrs]);
const bgColor = useMemo(() => cardColor(bodyshop.ssbuckets, totalHrs), [bodyshop.ssbuckets, totalHrs]);
const contrastYIQ = useMemo(() => getContrastYIQ(bgColor), [bgColor]);
const isBodyEmpty = useMemo(() => {
return !(
cardSettings?.ownr_nm ||
cardSettings?.model_info ||
cardSettings?.ins_co_nm ||
cardSettings?.clm_no ||
cardSettings?.employeeassignments ||
cardSettings?.actual_in ||
cardSettings?.scheduled_completion ||
cardSettings?.ats ||
cardSettings?.sublets ||
cardSettings?.production_note ||
cardSettings?.partsstatus ||
cardSettings?.estimator ||
cardSettings?.subtotal ||
cardSettings?.tasks
);
}, [cardSettings]);
const headerContent = (
<div className="header-content-container">
<div className="inner-container">
<ProductionAlert
record={{
id: card.id,
production_vars: card?.metadata.production_vars,
refetch: card?.refetch
}}
key="alert"
/>
{metadata?.suspended && <PauseCircleOutlined className="circle-outline" key="suspended" />}
{metadata?.iouparent && (
<EllipsesToolTip
title={t("jobs.labels.iou")}
key="iouparent"
className="iouparent"
kiosk={cardSettings.kiosk}
>
<BranchesOutlined className="branches-outlined" />
</EllipsesToolTip>
)}
</div>
<span className="tech-container">
<Link to={technician ? `/tech/joblookup?selected=${card.id}` : `/manage/jobs/${card.id}`}>
{metadata?.ro_number || t("general.labels.na")}
</Link>
</span>
{isBodyEmpty && (
<div className="body-empty-container">
<Link to={{ search: `?selected=${card.id}` }}>
<EyeFilled />
</Link>
</div>
)}
</div>
);
const bodyContent = (
<Row>
<OwnerNameToolTip metadata={metadata} cardSettings={cardSettings} />
<ModelInfoToolTip metadata={metadata} cardSettings={cardSettings} />
<InsuranceCompanyToolTip metadata={metadata} cardSettings={cardSettings} />
<ClaimNumberToolTip metadata={metadata} cardSettings={cardSettings} />
<EmployeeAssignmentsToolTip
metadata={metadata}
cardSettings={cardSettings}
employee_body={employee_body}
employee_prep={employee_prep}
employee_refinish={employee_refinish}
employee_csr={employee_csr}
/>
<EstimatorToolTip metadata={metadata} cardSettings={cardSettings} />
<TasksToolTip metadata={metadata} cardSettings={cardSettings} t={t} />
<SubtotalTooltip metadata={metadata} cardSettings={cardSettings} t={t} />
<ActualInToolTip metadata={metadata} cardSettings={cardSettings} />
<ScheduledCompletionToolTip metadata={metadata} cardSettings={cardSettings} pastDueAlert={pastDueAlert} />
<AltTransportToolTip metadata={metadata} cardSettings={cardSettings} />
<SubletsComponent metadata={metadata} cardSettings={cardSettings} />
<ProductionNoteComponent metadata={metadata} cardSettings={cardSettings} card={card} />
<PartsStatusComponent metadata={metadata} cardSettings={cardSettings} />
</Row>
);
return (
<Card
className={`react-trello-card ${cardSettings.kiosk ? "kiosk-mode" : ""}`}
size="small"
style={{
backgroundColor: cardSettings?.cardcolor && `rgba(${bgColor.r},${bgColor.g},${bgColor.b},${bgColor.a})`,
color: cardSettings?.cardcolor && contrastYIQ
}}
title={!isBodyEmpty ? headerContent : null}
extra={
!isBodyEmpty && (
<Link to={{ search: `?selected=${card.id}` }}>
<EyeFilled />
</Link>
)
}
>
{isBodyEmpty ? headerContent : bodyContent}
</Card>
);
}

View File

@@ -15,13 +15,13 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import IndefiniteLoading from "../indefinite-loading/indefinite-loading.component";
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
import ProductionListDetailComponent from "../production-list-detail/production-list-detail.component";
import CardColorLegend from "../production-board-kanban-card/production-board-kanban-card-color-legend.component";
import CardColorLegend from "./production-board-kanban-card-color-legend.component.jsx";
import "./production-board-kanban.styles.scss";
import { createBoardData } from "./production-board-kanban.utils.js";
import ProductionBoardKanbanSettings from "./settings/production-board-kanban.settings.component.jsx";
import cloneDeep from "lodash/cloneDeep";
import isEqual from "lodash/isEqual";
import { defaultKanbanSettings } from "./settings/defaultKanbanSettings.js";
import { mergeWithDefaults } from "./settings/defaultKanbanSettings.js";
import NoteUpsertModal from "../../components/note-upsert-modal/note-upsert-modal.container";
const mapStateToProps = createStructuredSelector({
@@ -182,13 +182,10 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr
[boardLanes, client, getCardByID, isMoving, t, insertAuditTrail]
);
const cardSettings = useMemo(
() =>
associationSettings?.kanban_settings && Object.keys(associationSettings.kanban_settings).length > 0
? associationSettings.kanban_settings
: defaultKanbanSettings,
[associationSettings]
);
const cardSettings = useMemo(() => {
const kanbanSettings = associationSettings?.kanban_settings;
return mergeWithDefaults(kanbanSettings);
}, [associationSettings]);
const handleSettingsChange = useCallback((newSettings) => {
setLoading(true);

View File

@@ -2,11 +2,13 @@ import React, { useMemo } from "react";
import { Card, Statistic } from "antd";
import { useTranslation } from "react-i18next";
import PropTypes from "prop-types";
import { statisticsItems, defaultKanbanSettings } from "./settings/defaultKanbanSettings.js";
import { defaultKanbanSettings, statisticsItems } from "./settings/defaultKanbanSettings.js";
export const StatisticType = {
HOURS: "hours",
AMOUNT: "amount",
JOBS: "jobs"
JOBS: "jobs",
TASKS: "tasks"
};
const mergeStatistics = (items, values) => {
@@ -122,6 +124,20 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
return parseFloat(total.toFixed(2));
}, [reducerData, cardSettings.totalAmountOnBoard]);
const tasksInProduction = useMemo(() => {
if (!data || !cardSettings.tasksInProduction) return null;
return data.reduce((acc, item) => acc + (item.tasks_aggregate?.aggregate?.count || 0), 0);
}, [data, cardSettings.tasksInProduction]);
const tasksOnBoard = useMemo(() => {
if (!reducerData || !cardSettings.tasksOnBoard) return null;
return reducerData.lanes.reduce((acc, lane) => {
return (
acc + lane.cards.reduce((laneAcc, card) => laneAcc + (card.metadata.tasks_aggregate?.aggregate?.count || 0), 0)
);
}, 0);
}, [reducerData, cardSettings.tasksOnBoard]);
const statistics = useMemo(
() =>
mergeStatistics(statisticsItems, [
@@ -134,7 +150,9 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
{ id: 6, value: totalAmountOnBoard, type: StatisticType.AMOUNT },
{ id: 7, value: totalLABOnBoard, type: StatisticType.HOURS },
{ id: 8, value: totalLAROnBoard, type: StatisticType.HOURS },
{ id: 9, value: jobsOnBoard, type: StatisticType.JOBS }
{ id: 9, value: jobsOnBoard, type: StatisticType.JOBS },
{ id: 10, value: tasksOnBoard, type: StatisticType.TASKS },
{ id: 11, value: tasksInProduction, type: StatisticType.TASKS }
]),
[
totalHrs,
@@ -146,7 +164,9 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
totalAmountOnBoard,
totalLABOnBoard,
totalLAROnBoard,
jobsOnBoard
jobsOnBoard,
tasksOnBoard,
tasksInProduction
]
);
@@ -187,37 +207,9 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
};
ProductionStatistics.propTypes = {
data: PropTypes.arrayOf(
PropTypes.shape({
labhrs: PropTypes.object,
larhrs: PropTypes.object,
job_totals: PropTypes.object
})
).isRequired,
cardSettings: PropTypes.shape({
totalHrs: PropTypes.bool,
totalLAB: PropTypes.bool,
totalLAR: PropTypes.bool,
jobsInProduction: PropTypes.bool,
totalAmountInProduction: PropTypes.bool,
totalHrsOnBoard: PropTypes.bool,
totalLABOnBoard: PropTypes.bool,
totalLAROnBoard: PropTypes.bool,
jobsOnBoard: PropTypes.bool,
totalAmountOnBoard: PropTypes.bool,
statisticsOrder: PropTypes.arrayOf(PropTypes.number)
}).isRequired,
reducerData: PropTypes.shape({
lanes: PropTypes.arrayOf(
PropTypes.shape({
cards: PropTypes.arrayOf(
PropTypes.shape({
metadata: PropTypes.object
})
).isRequired
})
).isRequired
})
data: PropTypes.array.isRequired,
cardSettings: PropTypes.object.isRequired,
reducerData: PropTypes.object
};
export default ProductionStatistics;

View File

@@ -18,7 +18,8 @@ const InformationSettings = ({ t }) => (
"sublets",
"partsstatus",
"estimator",
"subtotal"
"subtotal",
"tasks"
].map((item) => (
<Col span={4} key={item}>
<Form.Item name={item} valuePropName="checked">

View File

@@ -8,7 +8,9 @@ const statisticsItems = [
{ id: 6, name: "totalAmountOnBoard", label: "total_amount_on_board" },
{ id: 7, name: "totalLABOnBoard", label: "total_lab_on_board" },
{ id: 8, name: "totalLAROnBoard", label: "total_lar_on_board" },
{ id: 9, name: "jobsOnBoard", label: "total_jobs_on_board" }
{ id: 9, name: "jobsOnBoard", label: "total_jobs_on_board" },
{ id: 10, name: "tasksOnBoard", label: "tasks_on_board" },
{ id: 11, name: "tasksInProduction", label: "tasks_in_production" }
];
const defaultKanbanSettings = {
@@ -23,6 +25,7 @@ const defaultKanbanSettings = {
scheduled_completion: true,
cardcolor: false,
orientation: false,
tasks: false,
cardSize: "small",
model_info: true,
kiosk: false,
@@ -35,6 +38,8 @@ const defaultKanbanSettings = {
totalLABOnBoard: false,
totalLAROnBoard: false,
jobsOnBoard: false,
tasksOnBoard: false,
tasksInProduction: false,
totalAmountOnBoard: true,
estimator: false,
subtotal: false,
@@ -43,4 +48,20 @@ const defaultKanbanSettings = {
selectedEstimators: []
};
export { defaultKanbanSettings, statisticsItems };
const mergeWithDefaults = (settings) => {
// Create a new object that starts with the default settings
const mergedSettings = { ...defaultKanbanSettings };
// Override with the provided settings, if any
if (settings) {
for (const key in settings) {
if (settings.hasOwnProperty(key)) {
mergedSettings[key] = settings[key];
}
}
}
return mergedSettings;
};
export { defaultKanbanSettings, statisticsItems, mergeWithDefaults };

View File

@@ -3,13 +3,14 @@ import { Button, Card, Col, Form, notification, Popover, Row, Tabs } from "antd"
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_KANBAN_SETTINGS } from "../../../graphql/user.queries.js";
import { defaultKanbanSettings } from "./defaultKanbanSettings.js";
import { defaultKanbanSettings, mergeWithDefaults } from "./defaultKanbanSettings.js";
import LayoutSettings from "./LayoutSettings.jsx";
import InformationSettings from "./InformationSettings.jsx";
import StatisticsSettings from "./StatisticsSettings.jsx";
import FilterSettings from "./FilterSettings.jsx";
import PropTypes from "prop-types";
export default function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bodyshop, data }) {
function ProductionBoardKanbanSettings({ associationSettings, parentLoading, bodyshop, data }) {
const [form] = Form.useForm();
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
@@ -23,16 +24,11 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
useEffect(() => {
if (associationSettings?.kanban_settings) {
form.setFieldsValue(associationSettings.kanban_settings);
if (associationSettings.kanban_settings.statisticsOrder) {
setStatisticsOrder(associationSettings.kanban_settings.statisticsOrder);
}
if (associationSettings.kanban_settings.selectedMdInsCos) {
setSelectedMdInsCos(associationSettings.kanban_settings.selectedMdInsCos);
}
if (associationSettings.kanban_settings.selectedEstimators) {
setSelectedEstimators(associationSettings.kanban_settings.selectedEstimators);
}
const finalSettings = mergeWithDefaults(associationSettings.kanban_settings);
form.setFieldsValue(finalSettings);
setStatisticsOrder(finalSettings.statisticsOrder);
setSelectedMdInsCos(finalSettings.selectedMdInsCos);
setSelectedEstimators(finalSettings.selectedEstimators);
}
}, [form, associationSettings]);
@@ -155,3 +151,12 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
</Popover>
);
}
ProductionBoardKanbanSettings.propTypes = {
associationSettings: PropTypes.object,
parentLoading: PropTypes.func.isRequired,
bodyshop: PropTypes.object.isRequired,
data: PropTypes.array
};
export default ProductionBoardKanbanSettings;

View File

@@ -12,7 +12,7 @@ import { EyeInvisibleOutlined, EyeOutlined } from "@ant-design/icons";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../../../redux/user/user.selectors.js";
import { selectTechnician } from "../../../../redux/tech/tech.selectors.js";
import ProductionBoardCard from "../../../production-board-kanban-card/production-board-kanban-card.component.jsx";
import ProductionBoardCard from "../../production-board-kanban-card.component.jsx";
import HeightMemoryWrapper from "../components/HeightMemoryWrapper.jsx";
import SizeMemoryWrapper from "../components/SizeMemoryWrapper.jsx";
import ListComponent from "../components/ListComponent.jsx";