- Optimize Production Board Card Component,

- Fix issue with production note
- Refactor shared Global styles into their own global style.

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-05-23 15:12:49 -04:00
parent 2f8f058c5c
commit 296afdbeee
4 changed files with 110 additions and 119 deletions

View File

@@ -6,7 +6,7 @@ import {
PauseCircleOutlined
} from "@ant-design/icons";
import { Card, Col, Row, Space, Tooltip } from "antd";
import React from "react";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { DateTimeFormatter } from "../../utils/DateFormatter";
@@ -18,60 +18,78 @@ 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";
/**
* Get the color of the card based on the total hours
* @param ssbuckets
* @param totalHrs
* @returns {{r: number, b: number, g: number}}
*/
const cardColor = (ssbuckets, totalHrs) => {
const bucket = ssbuckets.filter((bucket) => bucket.gte <= totalHrs && (!!bucket.lt ? bucket.lt > totalHrs : true))[0];
const bucket = ssbuckets.find((bucket) => bucket.gte <= totalHrs && (!bucket.lt || bucket.lt > totalHrs));
let color = { r: 255, g: 255, b: 255 };
if (bucket && bucket.color) {
color = bucket.color;
if (bucket.color.rgb) {
color = bucket.color.rgb;
}
color = bucket.color.rgb || bucket.color;
}
return color;
};
function getContrastYIQ(bgColor) {
const yiq = (bgColor.r * 299 + bgColor.g * 587 + bgColor.b * 114) / 1000;
return yiq >= 128 ? "black" : "white";
}
/**
* Get the contrast color based on the background color
* @param bgColor
* @returns {string}
*/
const getContrastYIQ = (bgColor) =>
(bgColor.r * 299 + bgColor.g * 587 + bgColor.b * 114) / 1000 >= 128 ? "black" : "white";
/**
* Production Board Card component
* @param technician
* @param card
* @param bodyshop
* @param cardSettings
* @returns {Element}
* @constructor
*/
export default function ProductionBoardCard({ technician, card, bodyshop, cardSettings }) {
const { t } = useTranslation();
let employee_body, employee_prep, employee_refinish, employee_csr;
if (card && card.metadata && card.metadata.employee_body) {
employee_body = bodyshop.employees.find((e) => e.id === card.metadata.employee_body);
// Destructure metadata
const { metadata } = card;
if (metadata?.employee_body) {
employee_body = bodyshop.employees.find((e) => e.id === metadata.employee_body);
}
if (card && card.metadata && card.metadata.employee_prep) {
employee_prep = bodyshop.employees.find((e) => e.id === card.metadata.employee_prep);
if (metadata?.employee_prep) {
employee_prep = bodyshop.employees.find((e) => e.id === metadata.employee_prep);
}
if (card && card.metadata && card.metadata.employee_refinish) {
employee_refinish = bodyshop.employees.find((e) => e.id === card.metadata.employee_refinish);
if (metadata?.employee_refinish) {
employee_refinish = bodyshop.employees.find((e) => e.id === metadata.employee_refinish);
}
if (card && card.metadata && card.metadata.employee_csr) {
employee_csr = bodyshop.employees.find((e) => e.id === card.metadata.employee_csr);
if (metadata?.employee_csr) {
employee_csr = bodyshop.employees.find((e) => e.id === metadata.employee_csr);
}
// if (card && card.metadata && card.metadata.employee_csr) {
// employee_csr = bodyshop.employees.find((e) => e.id === card.metadata.employee_csr);
// if (metadata.?employee_csr) {
// employee_csr = bodyshop.employees.find((e) => e.id === metadata.employee_csr);
// }
const pastDueAlert =
!!card?.metadata?.scheduled_completion &&
((dayjs().isSameOrAfter(dayjs(card.metadata.scheduled_completion), "day") && "production-completion-past") ||
(dayjs().add(1, "day").isSame(dayjs(card.metadata.scheduled_completion), "day") && "production-completion-soon"));
!!metadata?.scheduled_completion &&
((dayjs().isSameOrAfter(dayjs(metadata.scheduled_completion), "day") && "production-completion-past") ||
(dayjs().add(1, "day").isSame(dayjs(metadata.scheduled_completion), "day") && "production-completion-soon"));
const totalHrs =
card && card?.metadata?.labhrs && card?.metadata?.larhrs
? card.metadata.labhrs.aggregate.sum.mod_lb_hrs + card.metadata.larhrs.aggregate.sum.mod_lb_hrs
const totalHrs = useMemo(() => {
return metadata?.labhrs && metadata?.larhrs
? metadata.labhrs.aggregate.sum.mod_lb_hrs + metadata.larhrs.aggregate.sum.mod_lb_hrs
: 0;
}, [metadata]);
const bgColor = cardColor(bodyshop.ssbuckets, totalHrs);
const bgColor = useMemo(() => cardColor(bodyshop.ssbuckets, totalHrs), [bodyshop.ssbuckets, totalHrs]);
const contrastYIQ = useMemo(() => getContrastYIQ(bgColor), [bgColor]);
return (
<Card
@@ -80,22 +98,22 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
style={{
backgroundColor:
cardSettings && cardSettings.cardcolor && `rgba(${bgColor.r},${bgColor.g},${bgColor.b},${bgColor.a})`,
color: cardSettings && cardSettings.cardcolor && getContrastYIQ(bgColor),
color: cardSettings && cardSettings.cardcolor && contrastYIQ,
maxWidth: "250px",
margin: "5px"
}}
title={
<Space>
<ProductionAlert record={card} key="alert" />
{card.metadata.suspended && <PauseCircleOutlined style={{ color: "orangered" }} />}
{card.metadata.iouparent && (
{metadata?.suspended && <PauseCircleOutlined style={{ color: "orangered" }} />}
{metadata?.iouparent && (
<Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} />
</Tooltip>
)}
<span style={{ fontWeight: "bolder" }}>
<Link to={technician ? `/tech/joblookup?selected=${card.id}` : `/manage/jobs/${card.id}`}>
{card.metadata.ro_number || t("general.labels.na")}
{metadata?.ro_number || t("general.labels.na")}
</Link>
</span>
</Space>
@@ -110,7 +128,7 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
{cardSettings && cardSettings.ownr_nm && (
<Col span={24}>
{cardSettings && cardSettings.compact ? (
<div className="ellipses">{`${card.metadata.ownr_ln || ""} ${card.metadata.ownr_co_nm || ""}`}</div>
<div className="ellipses">{`${metadata.ownr_ln || ""} ${metadata.ownr_co_nm || ""}`}</div>
) : (
<div className="ellipses">
<OwnerNameDisplay ownerObject={card} />
@@ -119,18 +137,18 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
</Col>
)}
<Col span={24}>
<div className="ellipses">{`${card.metadata.v_model_yr || ""} ${
card.metadata.v_make_desc || ""
} ${card.metadata.v_model_desc || ""}`}</div>
<div className="ellipses">{`${metadata.v_model_yr || ""} ${
metadata.v_make_desc || ""
} ${metadata.v_model_desc || ""}`}</div>
</Col>
{cardSettings && cardSettings.ins_co_nm && card.metadata.ins_co_nm && (
{cardSettings && cardSettings.ins_co_nm && metadata.ins_co_nm && (
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
<div className="ellipses">{card.metadata.ins_co_nm || ""}</div>
<div className="ellipses">{metadata.ins_co_nm || ""}</div>
</Col>
)}
{cardSettings && cardSettings.clm_no && card.metadata.clm_no && (
{cardSettings && cardSettings.clm_no && metadata.clm_no && (
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
<div className="ellipses">{card.metadata.clm_no || ""}</div>
<div className="ellipses">{metadata.clm_no || ""}</div>
</Col>
)}
@@ -139,7 +157,7 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
<Row>
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`B: ${
employee_body ? `${employee_body.first_name.substr(0, 3)} ${employee_body.last_name.charAt(0)}` : ""
} ${card.metadata.labhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
} ${metadata.labhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`P: ${
employee_prep ? `${employee_prep.first_name.substr(0, 3)} ${employee_prep.last_name.charAt(0)}` : ""
}`}</Col>
@@ -147,7 +165,7 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
employee_refinish
? `${employee_refinish.first_name.substr(0, 3)} ${employee_refinish.last_name.charAt(0)}`
: ""
} ${card.metadata.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
} ${metadata.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
employee_csr ? `${employee_csr.first_name} ${employee_csr.last_name}` : ""
}`}</Col>
@@ -158,48 +176,56 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
<Col span={24}>
<Row>
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`B: ${
card.metadata.labhrs.aggregate.sum.mod_lb_hrs || "?"
metadata.labhrs.aggregate.sum.mod_lb_hrs || "?"
} hrs`}</Col>
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`R: ${
card.metadata.larhrs.aggregate.sum.mod_lb_hrs || "?"
metadata.larhrs.aggregate.sum.mod_lb_hrs || "?"
} hrs`}</Col>
</Row>
</Col>
)} */}
{cardSettings && cardSettings.actual_in && card.metadata.actual_in && (
{cardSettings && cardSettings.actual_in && metadata.actual_in && (
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
<Space>
<DownloadOutlined />
<DateTimeFormatter format="MM/DD">{card.metadata.actual_in}</DateTimeFormatter>
<DateTimeFormatter format="MM/DD">{metadata.actual_in}</DateTimeFormatter>
</Space>
</Col>
)}
{cardSettings && cardSettings.scheduled_completion && card.metadata.scheduled_completion && (
{cardSettings && cardSettings.scheduled_completion && metadata.scheduled_completion && (
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
<Space className={pastDueAlert}>
<CalendarOutlined />
<DateTimeFormatter format="MM/DD">{card.metadata.scheduled_completion}</DateTimeFormatter>
<DateTimeFormatter format="MM/DD">{metadata.scheduled_completion}</DateTimeFormatter>
</Space>
</Col>
)}
{cardSettings && cardSettings.ats && card.metadata.alt_transport && (
{cardSettings && cardSettings.ats && metadata.alt_transport && (
<Col span={12}>
<div>{card.metadata.alt_transport || ""}</div>
<div>{metadata.alt_transport || ""}</div>
</Col>
)}
{cardSettings && cardSettings.sublets && (
<Col span={12}>
<ProductionSubletsManageComponent subletJobLines={card.metadata.subletLines} />
<ProductionSubletsManageComponent subletJobLines={metadata.subletLines} />
</Col>
)}
{cardSettings && cardSettings.production_note && (
<Col span={24}>
{cardSettings && cardSettings.production_note && <ProductionListColumnProductionNote record={card} />}
{cardSettings && cardSettings.production_note && (
<ProductionListColumnProductionNote
record={{
production_vars: card?.metadata.production_vars,
id: card?.id,
refetch: card?.refetch
}}
/>
)}
</Col>
)}
{cardSettings && cardSettings.partsstatus && (
<Col span={24}>
<JobPartsQueueCount parts={card.metadata.joblines_status} />
<JobPartsQueueCount parts={metadata.joblines_status} />
</Col>
)}
</Row>

View File

@@ -6,11 +6,12 @@ import NewLaneForm from "./NewLaneForm.jsx";
import NewCardForm from "./NewCardForm.jsx";
import AddCardLink from "./AddCardLink";
import NewLaneSection from "./NewLaneSection.jsx";
import { BoardWrapper, GlobalStyleHorizontal, GlobalStyleVertical, ScrollableLane, Section } from "../styles/Base";
import { BoardWrapper, StyleHorizontal, GlobalStyle, StyleVertical, ScrollableLane, Section } from "../styles/Base";
const exports = {
GlobalStyleHorizontal,
GlobalStyleVertical,
StyleHorizontal,
StyleVertical,
GlobalStyle,
BoardWrapper,
Loader,
ScrollableLane,

View File

@@ -7,19 +7,21 @@ const Board = ({ id, className, components, orientation, ...additionalProps }) =
const [storeId] = useState(id || v1());
const allClassNames = classNames("react-trello-board", className || "");
const GlobalStyle = orientation === "horizontal" ? components.GlobalStyleHorizontal : components.GlobalStyleVertical;
const OrientationStyle = orientation === "horizontal" ? components.StyleHorizontal : components.StyleVertical;
return (
<GlobalStyle>
<BoardContainer
components={components}
orientation={orientation}
{...additionalProps}
id={storeId}
className={allClassNames}
/>
</GlobalStyle>
<>
<components.GlobalStyle />
<OrientationStyle>
<BoardContainer
components={components}
orientation={orientation}
{...additionalProps}
id={storeId}
className={allClassNames}
/>
</OrientationStyle>
</>
);
};

View File

@@ -24,48 +24,12 @@ const getSectionStyles = (props) => {
return "";
};
export const GlobalStyleHorizontal = styled.div`
export const GlobalStyle = createGlobalStyle`
.comPlainTextContentEditable {
-webkit-user-modify: read-write-plaintext-only;
cursor: text;
}
.comPlainTextContentEditable--has-placeholder::before {
content: attr(placeholder);
opacity: 0.5;
color: inherit;
cursor: text;
}
.react_trello_dragClass {
transform: rotate(3deg);
}
.react_trello_dragLaneClass {
transform: rotate(3deg);
}
.icon-overflow-menu-horizontal:before {
content: "\\E91F";
}
.icon-lg,
.icon-sm {
color: #798d99;
}
.icon-lg {
height: 32px;
font-size: 16px;
line-height: 32px;
width: 32px;
}
`;
export const GlobalStyleVertical = styled.div`
.comPlainTextContentEditable {
-webkit-user-modify: read-write-plaintext-only;
cursor: text;
}
.smooth-dnd-container.horizontal {
}
@@ -100,23 +64,21 @@ export const GlobalStyleVertical = styled.div`
width: 32px;
}
.react-trello-column-header {
border-radius: 5px;
}
`;
export const StyleHorizontal = styled.div``;
export const StyleVertical = styled.div`
.react-trello-column-header {
text-align: left;
}
.smooth-dnd-container {
// TODO ? This is the question. We need the same drag-zone we get in horizontal mode
min-height: 50px; // Not needed, just for extra landing space
}
.react-trello-lane {
border: 1px solid #ccc;
border-radius: 5px;
}
.react-trello-column-header {
border: 1px solid #ccc;
border-radius: 5px;
padding: 5px;
text-align: left;
}
.react-trello-board {
overflow-y: hidden !important;
}
@@ -225,7 +187,7 @@ export const ScrollableLane = styled.div`
align-self: center;
// TODO: This was commented out to match existing board style
//max-height: 90vh;
margin-top: 10px;
//margin-top: 10px;
flex-direction: column;
justify-content: space-between;
`;