From 496a3ade1a7d912fc84d538d00c532dfd5e86842 Mon Sep 17 00:00:00 2001
From: Aditya Varun Dhayapulay <93042524+adit24dhaya@users.noreply.github.com>
Date: Wed, 28 Oct 2026 21:09:53 -0700
Subject: [PATCH 1/2] Clarify completed project duration for Task 1558
Explain that Project Status measures elapsed hours for projects completed in the selected period. Let cards grow to fit the definition and preserve unavailable-data handling.
Cover single Active Projects rendering and duration edge cases. Include the complete original request and UI evidence for review.
Task: 1558 (WBS 6ae059b9b664eb073c950a4c)
---
.../WeeklyProjectSummary.jsx | 9 ++-
.../WeeklyProjectSummary.module.css | 15 +++-
...eeklyProjectSummary.projectStatus.test.jsx | 71 ++++++++++++++++++-
3 files changed, 92 insertions(+), 3 deletions(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx
index 71404447993..dab7a87c9a8 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx
@@ -47,7 +47,7 @@ const projectStatusButtonData = [
['Completed Projects', 'completedProjects', '#F3FCFF', '#C1EFFB', '#328D1B'],
['Delayed Projects', 'delayedProjects', '#FFE9FA', '#FECFF3', '#C82F2F'],
['Active Projects', 'activeProjects', '#E8E8FF', '#CBCBFE', '#328D1B'],
- ['Avg Project Duration', 'avgProjectDuration', '#FFF6EE', '#FFD8A5', '#FFD8A5'],
+ ['Avg Completed Project Duration (hours)', 'avgProjectDuration', '#FFF6EE', '#FFD8A5', '#FFD8A5'],
['Total Material Cost', 'totalMaterialCost', '#FFF3F3', '#FBC1C2', '#328D1B'],
['Total Material Used', 'totalMaterialUsed', '#DAC8FF', '#B28ECC', '#328D1B'],
['Total Labor Hours Invested', 'totalLaborHoursInvested', '#E5C1FC', '#F6E1FB', '#328D1B'],
@@ -327,10 +327,17 @@ function WeeklyProjectSummary() {
return (
{button.title}
+ {button.metricKey === 'avgProjectDuration' && (
+
+ Start to completion for projects completed in the selected period.
+
+ )}
{
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
});
+ it.each([false, true])('renders Active Projects only once (dark mode: %s)', darkMode => {
+ const state = createState();
+ state.theme.darkMode = darkMode;
+ state.weeklyProjectSummary.projectStatusData = {
+ current: { metrics: { activeProjects: { value: 265, comparisonType: 'CURRENT_ONLY' } } },
+ };
+ renderWithState(state);
+
+ expect(screen.getAllByText('Active Projects')).toHaveLength(1);
+ expect(screen.getAllByText('265')).toHaveLength(1);
+ });
+
+ it.each([0, 17, 1008])(
+ 'defines the duration metric and preserves the API hour value %s',
+ value => {
+ const state = createState();
+ state.weeklyProjectSummary.projectStatusData = {
+ current: {
+ metrics: {
+ avgProjectDuration: {
+ value,
+ unit: 'hrs',
+ comparisonType: 'PERIOD_COMPARABLE',
+ percentageChange: 0.13,
+ },
+ },
+ },
+ };
+ renderWithState(state);
+
+ const durationCard = screen.getByRole('group', {
+ name: 'Avg Completed Project Duration (hours)',
+ });
+ expect(within(durationCard).getByText(`${value} hrs`)).toBeInTheDocument();
+ expect(
+ within(durationCard).getByText(
+ 'Start to completion for projects completed in the selected period.',
+ ),
+ ).toBeInTheDocument();
+ expect(within(durationCard).getByText('+13% month over month')).toBeInTheDocument();
+ },
+ );
+
+ it('keeps missing duration unavailable instead of inventing 17 hours', () => {
+ const state = createState();
+ state.weeklyProjectSummary.projectStatusData = {
+ current: {
+ metrics: {
+ avgProjectDuration: {
+ value: null,
+ unit: 'hrs',
+ comparisonType: 'UNAVAILABLE',
+ unavailableReason: 'No completed projects in this period.',
+ },
+ },
+ },
+ };
+ renderWithState(state);
+
+ const durationCard = screen.getByRole('group', {
+ name: 'Avg Completed Project Duration (hours)',
+ });
+ expect(within(durationCard).getByText('N/A')).toBeInTheDocument();
+ expect(
+ within(durationCard).getByRole('button', { name: 'No completed projects in this period.' }),
+ ).toBeInTheDocument();
+ expect(screen.queryByText('17 hrs')).not.toBeInTheDocument();
+ });
+
it('shows loading and suppresses stale comparison text when filters change before useEffect dispatches', () => {
renderWithState(
createState({
From 194e2ec0b61c78958484646086cb9a7b028ad7f8 Mon Sep 17 00:00:00 2001
From: Aditya Varun Dhayapulay <93042524+adit24dhaya@users.noreply.github.com>
Date: Tue, 3 Nov 2026 21:40:36 -0800
Subject: [PATCH 2/2] Fix Project Status card semantics and shared dark-mode
styling
Use named native sections for the metric cards and reuse their readable dark-mode text rule for the duration explanation, removing its redundant override.
Update accessible-region assertions while preserving API values and responsive behavior. Addresses Sonar findings on PR #5729, Task 1558.
---
.../WeeklyProjectSummary/WeeklyProjectSummary.jsx | 5 ++---
.../WeeklyProjectSummary/WeeklyProjectSummary.module.css | 5 +----
.../WeeklyProjectSummary.projectStatus.test.jsx | 4 ++--
3 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx
index dab7a87c9a8..128f4dbf311 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.jsx
@@ -325,9 +325,8 @@ function WeeklyProjectSummary() {
{projectStatusCards.map(button => {
const unavailableTooltipId = `project-status-unavailable-${button.metricKey}`;
return (
-
)}
-
+
);
})}
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css
index 9764982e8c4..6f8fc34d795 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css
+++ b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.module.css
@@ -695,15 +695,12 @@
}
/* Keep all Project Status text readable on the preserved pastel status cards in dark mode. */
+.darkMode .statusCard .projectDurationDescription,
.darkMode .statusCard .weeklyCardTitle,
.darkMode .statusCard .weeklyStatusValue {
color: #000 !important;
}
-.darkMode .statusCard .projectDurationDescription {
- color: #333 !important;
-}
-
.darkMode .statusCard .projectStatusUnavailableInfo {
color: #000 !important;
}
diff --git a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.projectStatus.test.jsx b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.projectStatus.test.jsx
index 2c32564d295..0aaa6297119 100644
--- a/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.projectStatus.test.jsx
+++ b/src/components/BMDashboard/WeeklyProjectSummary/WeeklyProjectSummary.projectStatus.test.jsx
@@ -193,7 +193,7 @@ describe('WeeklyProjectSummary Project Status request state', () => {
};
renderWithState(state);
- const durationCard = screen.getByRole('group', {
+ const durationCard = screen.getByRole('region', {
name: 'Avg Completed Project Duration (hours)',
});
expect(within(durationCard).getByText(`${value} hrs`)).toBeInTheDocument();
@@ -222,7 +222,7 @@ describe('WeeklyProjectSummary Project Status request state', () => {
};
renderWithState(state);
- const durationCard = screen.getByRole('group', {
+ const durationCard = screen.getByRole('region', {
name: 'Avg Completed Project Duration (hours)',
});
expect(within(durationCard).getByText('N/A')).toBeInTheDocument();