feature/IO-1113-Online-Dark-Mode - Initial Commit
This commit is contained in:
@@ -1,26 +1,29 @@
|
||||
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 };
|
||||
|
||||
let color = { r: 255, g: 255, b: 255, a: 1 }; // Default to white with full opacity
|
||||
if (bucket.color) {
|
||||
color = bucket.color;
|
||||
|
||||
if (bucket.color.rgb) {
|
||||
color = bucket.color.rgb;
|
||||
color = { ...bucket.color.rgb, a: bucket.color.a || 1 };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
label: bucket.label,
|
||||
color
|
||||
};
|
||||
});
|
||||
|
||||
const getBackgroundColor = (color) => {
|
||||
// Return dynamic color if valid, otherwise use fallback
|
||||
return color && color.r !== undefined && color.g !== undefined && color.b !== undefined
|
||||
? `rgba(${color.r},${color.g},${color.b},${color.a || 1})`
|
||||
: "var(--legend-bg-fallback)";
|
||||
};
|
||||
|
||||
return (
|
||||
<Col>
|
||||
<Typography>{t("production.labels.legend")}</Typography>
|
||||
@@ -36,7 +39,7 @@ const CardColorLegend = ({ bodyshop }) => {
|
||||
style={{
|
||||
width: "1.5rem",
|
||||
aspectRatio: "1/1",
|
||||
backgroundColor: `rgba(${item.color.r},${item.color.g},${item.color.b},${item.color.a})`
|
||||
backgroundColor: getBackgroundColor(item.color)
|
||||
}}
|
||||
></div>
|
||||
<div>{item.label}</div>
|
||||
|
||||
@@ -11,13 +11,10 @@ import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
|
||||
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 JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import ShareToTeamsButton from "../share-to-teams/share-to-teams.component.jsx";
|
||||
@@ -25,11 +22,25 @@ import { PiMicrosoftTeamsLogo } from "react-icons/pi";
|
||||
|
||||
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 };
|
||||
return bucket && bucket.color
|
||||
? bucket.color.rgb || bucket.color
|
||||
: {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255,
|
||||
a: 1,
|
||||
fallback: "var(--card-bg-fallback)"
|
||||
};
|
||||
};
|
||||
|
||||
const getContrastYIQ = (bgColor) =>
|
||||
(bgColor.r * 299 + bgColor.g * 587 + bgColor.b * 114) / 1000 >= 128 ? "black" : "white";
|
||||
const getContrastYIQ = (bgColor, isDarkMode = document.documentElement.getAttribute("data-theme") === "dark") => {
|
||||
// Use fallback if bgColor is invalid
|
||||
if (!bgColor || bgColor.fallback) return isDarkMode ? "var(--card-text-fallback)" : "black";
|
||||
// Calculate luminance for contrast
|
||||
const luminance = (bgColor.r * 299 + bgColor.g * 587 + bgColor.b * 114) / 1000;
|
||||
// Adjust threshold for dark mode to ensure readable text
|
||||
return luminance >= (isDarkMode ? 150 : 128) ? "black" : isDarkMode ? "var(--card-text-fallback)" : "white";
|
||||
};
|
||||
|
||||
const findEmployeeById = (employees, id) => employees.find((e) => e.id === id);
|
||||
|
||||
@@ -44,6 +55,8 @@ const EllipsesToolTip = React.memo(({ title, children, kiosk }) => {
|
||||
);
|
||||
});
|
||||
|
||||
EllipsesToolTip.displayName = "EllipsesToolTip";
|
||||
|
||||
const OwnerNameToolTip = ({ metadata, cardSettings }) =>
|
||||
cardSettings?.ownr_nm && (
|
||||
<Col span={24}>
|
||||
@@ -214,9 +227,8 @@ const EstimatorToolTip = ({ metadata, cardSettings }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const SubtotalTooltip = ({ metadata, cardSettings, t }) => {
|
||||
const SubtotalTooltip = ({ metadata, cardSettings }) => {
|
||||
const dineroAmount = Dinero(metadata?.job_totals?.totals?.subtotal ?? Dinero()).toFormat();
|
||||
|
||||
return (
|
||||
cardSettings?.subtotal && (
|
||||
<Col span={cardSettings.compact ? 24 : 12}>
|
||||
@@ -300,12 +312,10 @@ const TasksToolTip = ({ metadata, cardSettings, t }) =>
|
||||
</Col>
|
||||
);
|
||||
|
||||
export default function ProductionBoardCard({ technician, card, bodyshop, cardSettings, clone }) {
|
||||
export default function ProductionBoardCard({ technician, card, bodyshop, cardSettings }) {
|
||||
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),
|
||||
@@ -314,7 +324,6 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
|
||||
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);
|
||||
@@ -322,16 +331,13 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
|
||||
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 ||
|
||||
@@ -413,8 +419,10 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
|
||||
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
|
||||
backgroundColor: cardSettings?.cardcolor
|
||||
? bgColor.fallback || `rgba(${bgColor.r},${bgColor.g},${bgColor.b},${bgColor.a || 1})`
|
||||
: "var(--card-bg-fallback)",
|
||||
color: cardSettings?.cardcolor ? contrastYIQ : "var(--card-text-fallback)"
|
||||
}}
|
||||
title={!isBodyEmpty ? headerContent : null}
|
||||
extra={
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
|
||||
.share-to-teams-badge {
|
||||
background-color: #cccccc;
|
||||
background-color: var(--share-badge-bg);
|
||||
border-radius: 50%;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
@@ -23,7 +23,7 @@
|
||||
.react-trello-column-header {
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
background-color: #d0d0d0;
|
||||
background-color: var(--column-header-bg);
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
@@ -31,13 +31,14 @@
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.react-trello-footer {
|
||||
background-color: #d0d0d0;
|
||||
background-color: var(--footer-bg);
|
||||
border-radius: 0 0 5px 5px;
|
||||
}
|
||||
|
||||
.grid-item {
|
||||
margin: 1px; // TODO: (Note) THis is where we set the margin for vertical
|
||||
margin: 1px; // TODO: (Note) This is where we set the margin for vertical
|
||||
}
|
||||
|
||||
.lane-title {
|
||||
@@ -53,27 +54,33 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
.body-empty-container {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.tech-container {
|
||||
font-weight: bolder;
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
|
||||
.branches-outlined {
|
||||
color: orangered;
|
||||
color: var(--tech-icon-color);
|
||||
}
|
||||
}
|
||||
|
||||
.inner-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
||||
.circle-outline {
|
||||
color: orangered;
|
||||
color: var(--tech-icon-color);
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.iou-parent {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -81,6 +88,6 @@
|
||||
}
|
||||
|
||||
.clone.is-dragging .ant-card {
|
||||
border: #1890ff 2px solid !important;
|
||||
border: 2px solid var(--clone-border-color) !important;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export const StyleHorizontal = styled.div`
|
||||
height: 100%;
|
||||
min-height: 1px;
|
||||
overflow-y: visible;
|
||||
overflow-x: visible; // change this line
|
||||
overflow-x: visible;
|
||||
}
|
||||
|
||||
.react-trello-lane.lane-collapsed {
|
||||
@@ -89,13 +89,13 @@ export const StyleHorizontal = styled.div`
|
||||
}
|
||||
|
||||
.size-memory-wrapper {
|
||||
display: flex; /* This makes it a flex container */
|
||||
flex-direction: column; /* Aligns children vertically */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.size-memory-wrapper .ant-card {
|
||||
flex-grow: 1; /* Allows the card to expand to fill the available space */
|
||||
width: 100%; /* Ensures the card stretches to fill the width of its parent */
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -131,7 +131,7 @@ export const StyleVertical = styled.div`
|
||||
|
||||
.grid-item {
|
||||
display: flex;
|
||||
width: ${(props) => props.gridItemWidth}; /* Use props to set width */
|
||||
width: ${(props) => props.gridItemWidth};
|
||||
align-content: stretch;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -148,13 +148,13 @@ export const StyleVertical = styled.div`
|
||||
}
|
||||
|
||||
.size-memory-wrapper {
|
||||
display: flex; /* This makes it a flex container */
|
||||
flex-direction: column; /* Aligns children vertically */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.size-memory-wrapper .ant-card {
|
||||
flex-grow: 1; /* Allows the card to expand to fill the available space */
|
||||
width: 100%; /* Ensures the card stretches to fill the width of its parent */
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.react-trello-lane .lane-collapsed {
|
||||
@@ -163,7 +163,7 @@ export const StyleVertical = styled.div`
|
||||
`;
|
||||
|
||||
export const BoardWrapper = styled.div`
|
||||
color: #393939;
|
||||
color: var(--board-text-color);
|
||||
height: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
@@ -171,7 +171,7 @@ export const BoardWrapper = styled.div`
|
||||
`;
|
||||
|
||||
export const Section = styled.section`
|
||||
background-color: #e3e3e3;
|
||||
background-color: var(--section-bg);
|
||||
border-radius: 3px;
|
||||
margin: 2px 2px;
|
||||
height: 100%;
|
||||
@@ -197,6 +197,6 @@ export const ScrollableLane = styled.div`
|
||||
|
||||
export const Detail = styled.div`
|
||||
font-size: 12px;
|
||||
color: #4d4d4d;
|
||||
color: var(--detail-text-color);
|
||||
white-space: pre-wrap;
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user