feature/IO-1113-Online-Dark-Mode - Initial Commit

This commit is contained in:
Dave Richer
2025-08-08 10:23:09 -04:00
parent 3737fe457f
commit 93e9e20f6f
35 changed files with 540 additions and 367 deletions

View File

@@ -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>

View File

@@ -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={

View File

@@ -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;
}

View File

@@ -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;
`;