Skip to content

Commit 945bd2e

Browse files
style: auto-fix lint and format [skip ci]
1 parent 3176447 commit 945bd2e

7 files changed

Lines changed: 129 additions & 89 deletions

File tree

client/src/components/CostBreakdownTable/CostBreakdownTable.test.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3462,7 +3462,6 @@ describe('Bug #586 — item expand state is independent per category', () => {
34623462
expect(screen.queryByText(/selected\)/i)).not.toBeInTheDocument();
34633463
});
34643464

3465-
34663465
// ── Live region announces source count (scenario 36) ─────────────────────
34673466

34683467
it('role="status" contains source-count announcement when filter is active', () => {
@@ -4030,6 +4029,4 @@ describe('Server-driven render path (#1360)', () => {
40304029
// The table element must be in the DOM (as opposed to the empty-state path which omits it).
40314030
expect(container.querySelector('table')).toBeInTheDocument();
40324031
});
4033-
40344032
});
4035-

client/src/components/CostBreakdownTable/CostBreakdownTable.tsx

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ function resolveLineCost(line: BreakdownBudgetLine, perspective: CostPerspective
8989
);
9090
}
9191

92-
9392
/**
9493
* Formats cost with explicit minus sign.
9594
* Must be called with the formatCurrency function from useFormatterContext or useFormatters.
@@ -393,11 +392,7 @@ function WorkItemAreaSection({
393392
const areaKey = `${sectionKey}-area-${area.areaId ?? 'unassigned'}`;
394393
const isExpanded = expandedKeys.has(areaKey);
395394

396-
const resolvedRawCost = resolveProjected(
397-
area.rawProjectedMin,
398-
area.rawProjectedMax,
399-
perspective,
400-
);
395+
const resolvedRawCost = resolveProjected(area.rawProjectedMin, area.rawProjectedMax, perspective);
401396
const resolvedPayback = resolveProjected(
402397
area.minSubsidyPayback,
403398
area.subsidyPayback,
@@ -584,11 +579,7 @@ function HouseholdItemAreaSection({
584579
const areaKey = `${sectionKey}-area-${area.areaId ?? 'unassigned'}`;
585580
const isExpanded = expandedKeys.has(areaKey);
586581

587-
const resolvedRawCost = resolveProjected(
588-
area.rawProjectedMin,
589-
area.rawProjectedMax,
590-
perspective,
591-
);
582+
const resolvedRawCost = resolveProjected(area.rawProjectedMin, area.rawProjectedMax, perspective);
592583
const resolvedPayback = resolveProjected(
593584
area.minSubsidyPayback,
594585
area.subsidyPayback,
@@ -798,7 +789,6 @@ export function CostBreakdownTable({
798789
*/
799790
const sum = overview.availableFunds - totalRawProjected + adjustedTotalPayback;
800791

801-
802792
// Empty state: only show early-return empty state if there are NO sources configured AND no items.
803793
// If sources are configured (even if all deselected, which prunes items), render the full table
804794
// so users can re-enable sources.
@@ -1105,12 +1095,7 @@ export function CostBreakdownTable({
11051095
)}
11061096
</td>
11071097
<td className={styles.colRemaining}>
1108-
{renderNet(
1109-
totalRawProjected,
1110-
adjustedTotalPayback,
1111-
styles,
1112-
formatCurrency,
1113-
)}
1098+
{renderNet(totalRawProjected, adjustedTotalPayback, styles, formatCurrency)}
11141099
</td>
11151100
</tr>
11161101

@@ -1193,7 +1178,9 @@ export function CostBreakdownTable({
11931178
}
11941179
className={`${styles.rowSourceDetail} ${styles.rowSourceDetailToggle}`}
11951180
style={rowStyle}
1196-
onClick={() => onSourceToggle(source.id === 'unassigned' ? null : source.id)}
1181+
onClick={() =>
1182+
onSourceToggle(source.id === 'unassigned' ? null : source.id)
1183+
}
11971184
onKeyDown={(e) => {
11981185
if (e.key === 'Enter' || e.key === ' ') {
11991186
e.preventDefault();
@@ -1220,9 +1207,7 @@ export function CostBreakdownTable({
12201207
</span>
12211208
</td>
12221209
<td className={styles.colPayback}>
1223-
<span className={styles.valuePositive}>
1224-
{formatCurrency(payback)}
1225-
</span>
1210+
<span className={styles.valuePositive}>{formatCurrency(payback)}</span>
12261211
</td>
12271212
<td className={styles.colRemaining}>
12281213
<span

client/src/lib/budgetOverviewApi.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals';
22
import { fetchBudgetOverview, fetchBudgetBreakdown } from './budgetOverviewApi.js';
3-
import type { BudgetOverview, BudgetOverviewResponse, BudgetBreakdownResponse } from '@cornerstone/shared';
3+
import type {
4+
BudgetOverview,
5+
BudgetOverviewResponse,
6+
BudgetBreakdownResponse,
7+
} from '@cornerstone/shared';
48

59
describe('budgetOverviewApi', () => {
610
let mockFetch: jest.MockedFunction<typeof globalThis.fetch>;

client/src/lib/budgetOverviewApi.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ export async function fetchBudgetOverview(): Promise<BudgetOverview> {
1818
* Fetches the itemized budget breakdown with all nested work items, categories, and budget lines.
1919
* Pass deselectedSources to apply server-side source filtering.
2020
*/
21-
export async function fetchBudgetBreakdown(
22-
deselectedSources?: string[],
23-
): Promise<BudgetBreakdown> {
21+
export async function fetchBudgetBreakdown(deselectedSources?: string[]): Promise<BudgetBreakdown> {
2422
const path =
2523
deselectedSources && deselectedSources.length > 0
2624
? `/budget/breakdown?deselectedSources=${encodeURIComponent(deselectedSources.join(','))}`

e2e/tests/budget/budget-source-filter.spec.ts

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -932,8 +932,7 @@ test.describe('Deselect triggers server refetch', { tag: '@responsive' }, () =>
932932
// Register response waiter BEFORE the click (waitForResponse must precede the action)
933933
const refetchPromise = page.waitForResponse(
934934
(resp) =>
935-
resp.url().includes('/api/budget/breakdown') &&
936-
resp.url().includes('deselectedSources='),
935+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
937936
);
938937

939938
await row.click();
@@ -990,8 +989,7 @@ test.describe('Deselect triggers server refetch', { tag: '@responsive' }, () =>
990989
// Register response waiter BEFORE the click
991990
const refetchPromise = page.waitForResponse(
992991
(resp) =>
993-
resp.url().includes('/api/budget/breakdown') &&
994-
resp.url().includes('deselectedSources='),
992+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
995993
);
996994

997995
await overviewPage.sourceRow('Bank Loan').click();
@@ -1036,8 +1034,7 @@ test.describe('Deselect triggers server refetch', { tag: '@responsive' }, () =>
10361034
// Deselect Bank Loan — first refetch includes only SOURCE_A_ID
10371035
const refetchA = page.waitForResponse(
10381036
(resp) =>
1039-
resp.url().includes('/api/budget/breakdown') &&
1040-
resp.url().includes('deselectedSources='),
1037+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
10411038
);
10421039
await overviewPage.sourceRow('Bank Loan').click();
10431040
// aria-pressed updates from URL state change before the server response
@@ -1047,8 +1044,7 @@ test.describe('Deselect triggers server refetch', { tag: '@responsive' }, () =>
10471044
// Deselect Equity — new refetch should include both IDs
10481045
const refetchB = page.waitForResponse(
10491046
(resp) =>
1050-
resp.url().includes('/api/budget/breakdown') &&
1051-
resp.url().includes('deselectedSources='),
1047+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
10521048
);
10531049
await overviewPage.sourceRow('Equity').click();
10541050
// aria-pressed for Equity updates from URL state before server response
@@ -1192,8 +1188,7 @@ test.describe('Cascade hiding via server response', { tag: '@responsive' }, () =
11921188

11931189
const refetchPromise = page.waitForResponse(
11941190
(resp) =>
1195-
resp.url().includes('/api/budget/breakdown') &&
1196-
resp.url().includes('deselectedSources='),
1191+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
11971192
);
11981193
await overviewPage.sourceRow('Bank Loan').click();
11991194
await expect(overviewPage.sourceRow('Bank Loan')).toHaveAttribute('aria-pressed', 'false');
@@ -1243,8 +1238,7 @@ test.describe('Cascade hiding via server response', { tag: '@responsive' }, () =
12431238

12441239
const refetchPromise = page.waitForResponse(
12451240
(resp) =>
1246-
resp.url().includes('/api/budget/breakdown') &&
1247-
resp.url().includes('deselectedSources='),
1241+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
12481242
);
12491243
await overviewPage.sourceRow('Bank Loan').click();
12501244
// aria-pressed updates from URL state change before server response
@@ -1293,8 +1287,7 @@ test.describe('Subsidy oversubscription consistency', () => {
12931287
// Deselect Large Source → triggers server refetch with filtered data
12941288
const refetchPromise = page.waitForResponse(
12951289
(resp) =>
1296-
resp.url().includes('/api/budget/breakdown') &&
1297-
resp.url().includes('deselectedSources='),
1290+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
12981291
);
12991292

13001293
await largeRow.click();
@@ -1407,8 +1400,7 @@ test.describe('Stale-while-revalidate during refetch', { tag: '@responsive' }, (
14071400
// Register the delayed refetch promise BEFORE clicking
14081401
const refetchPromise = page.waitForResponse(
14091402
(resp) =>
1410-
resp.url().includes('/api/budget/breakdown') &&
1411-
resp.url().includes('deselectedSources='),
1403+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
14121404
);
14131405

14141406
// Deselect source — triggers debounced + delayed refetch
@@ -1454,8 +1446,7 @@ test.describe('URL on mount triggers initial filtered fetch', { tag: '@responsiv
14541446
// Register response waiter BEFORE navigation — must catch the initial mount fetch
14551447
const initialFetchPromise = page.waitForResponse(
14561448
(resp) =>
1457-
resp.url().includes('/api/budget/breakdown') &&
1458-
resp.url().includes('deselectedSources='),
1449+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
14591450
);
14601451

14611452
// Navigate directly with deselectedSources param
@@ -1578,8 +1569,7 @@ test.describe('Empty state when all sources deselected', { tag: '@responsive' },
15781569

15791570
const refetchPromise = page.waitForResponse(
15801571
(resp) =>
1581-
resp.url().includes('/api/budget/breakdown') &&
1582-
resp.url().includes('deselectedSources='),
1572+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
15831573
);
15841574

15851575
await overviewPage.sourceRow('Bank Loan').click();
@@ -1618,8 +1608,7 @@ test.describe('Empty state when all sources deselected', { tag: '@responsive' },
16181608
// Deselect to trigger empty state
16191609
const firstRefetch = page.waitForResponse(
16201610
(resp) =>
1621-
resp.url().includes('/api/budget/breakdown') &&
1622-
resp.url().includes('deselectedSources='),
1611+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
16231612
);
16241613
await overviewPage.sourceRow('Bank Loan').click();
16251614
await firstRefetch;
@@ -1702,8 +1691,7 @@ test.describe('Rapid debounce coalesces requests', () => {
17021691
// to let the single coalesced request complete.
17031692
const coalescedRefetch = page.waitForResponse(
17041693
(resp) =>
1705-
resp.url().includes('/api/budget/breakdown') &&
1706-
resp.url().includes('deselectedSources='),
1694+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
17071695
);
17081696
await coalescedRefetch;
17091697

@@ -1743,8 +1731,7 @@ test.describe('Available Funds caption', { tag: '@responsive' }, () => {
17431731
// Deselect Bank Loan — triggers refetch
17441732
const refetchPromise = page.waitForResponse(
17451733
(resp) =>
1746-
resp.url().includes('/api/budget/breakdown') &&
1747-
resp.url().includes('deselectedSources='),
1734+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
17481735
);
17491736
await overviewPage.sourceRow('Bank Loan').click();
17501737
await refetchPromise;
@@ -1825,8 +1812,7 @@ test.describe('Source detail row columns', { tag: '@responsive' }, () => {
18251812
// Deselect — triggers refetch; wait for it
18261813
const refetchPromise = page.waitForResponse(
18271814
(resp) =>
1828-
resp.url().includes('/api/budget/breakdown') &&
1829-
resp.url().includes('deselectedSources='),
1815+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
18301816
);
18311817
await row.click();
18321818
await expect(row).toHaveAttribute('aria-pressed', 'false');
@@ -1901,8 +1887,7 @@ test.describe('Keyboard navigation', () => {
19011887
// Space deselects (all sources start selected) — triggers refetch
19021888
const deselect = page.waitForResponse(
19031889
(resp) =>
1904-
resp.url().includes('/api/budget/breakdown') &&
1905-
resp.url().includes('deselectedSources='),
1890+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
19061891
);
19071892
await page.keyboard.press('Space');
19081893
await expect(row).toHaveAttribute('aria-pressed', 'false');
@@ -1942,8 +1927,7 @@ test.describe('Keyboard navigation', () => {
19421927

19431928
const refetchPromise = page.waitForResponse(
19441929
(resp) =>
1945-
resp.url().includes('/api/budget/breakdown') &&
1946-
resp.url().includes('deselectedSources='),
1930+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
19471931
);
19481932
await page.keyboard.press('Enter');
19491933
await expect(row).toHaveAttribute('aria-pressed', 'false');
@@ -1973,8 +1957,7 @@ test.describe('Keyboard navigation', () => {
19731957
// Deselect Bank Loan
19741958
const deselect = page.waitForResponse(
19751959
(resp) =>
1976-
resp.url().includes('/api/budget/breakdown') &&
1977-
resp.url().includes('deselectedSources='),
1960+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
19781961
);
19791962
const row = overviewPage.sourceRow('Bank Loan');
19801963
await row.click();
@@ -2053,8 +2036,7 @@ test.describe('Live region', { tag: '@responsive' }, () => {
20532036

20542037
const refetchPromise = page.waitForResponse(
20552038
(resp) =>
2056-
resp.url().includes('/api/budget/breakdown') &&
2057-
resp.url().includes('deselectedSources='),
2039+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
20582040
);
20592041
await overviewPage.sourceRow('Bank Loan').click();
20602042
await refetchPromise;
@@ -2091,8 +2073,7 @@ test.describe('No chip toolbar', { tag: '@responsive' }, () => {
20912073
// Select a source to make the UI "active" — toolbar must still not appear
20922074
const refetchPromise = page.waitForResponse(
20932075
(resp) =>
2094-
resp.url().includes('/api/budget/breakdown') &&
2095-
resp.url().includes('deselectedSources='),
2076+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
20962077
);
20972078
await overviewPage.sourceRow('Bank Loan').click();
20982079
await refetchPromise;
@@ -2179,8 +2160,7 @@ test.describe('Dark mode: badge color smoke check', { tag: '@responsive' }, () =
21792160

21802161
const refetchPromise = page.waitForResponse(
21812162
(resp) =>
2182-
resp.url().includes('/api/budget/breakdown') &&
2183-
resp.url().includes('deselectedSources='),
2163+
resp.url().includes('/api/budget/breakdown') && resp.url().includes('deselectedSources='),
21842164
);
21852165
await row.click();
21862166
await expect(row).toHaveAttribute('aria-pressed', 'false');

server/src/routes/budgetOverview.breakdown.test.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -598,8 +598,16 @@ describe('GET /api/budget/breakdown', () => {
598598

599599
const srcA = insertBudgetSource({ name: 'Source A S11', totalAmount: 80000 });
600600
const srcB = insertBudgetSource({ name: 'Source B S11', totalAmount: 50000 });
601-
insertWorkItemWithSource({ plannedAmount: 8000, confidence: 'own_estimate', budgetSourceId: srcA });
602-
insertWorkItemWithSource({ plannedAmount: 3000, confidence: 'own_estimate', budgetSourceId: srcB });
601+
insertWorkItemWithSource({
602+
plannedAmount: 8000,
603+
confidence: 'own_estimate',
604+
budgetSourceId: srcA,
605+
});
606+
insertWorkItemWithSource({
607+
plannedAmount: 3000,
608+
confidence: 'own_estimate',
609+
budgetSourceId: srcB,
610+
});
603611

604612
const response = await app.inject({
605613
method: 'GET',
@@ -629,7 +637,11 @@ describe('GET /api/budget/breakdown', () => {
629637
);
630638

631639
const srcA = insertBudgetSource({ name: 'Source A S12', totalAmount: 80000 });
632-
insertWorkItemWithSource({ plannedAmount: 5000, confidence: 'own_estimate', budgetSourceId: srcA });
640+
insertWorkItemWithSource({
641+
plannedAmount: 5000,
642+
confidence: 'own_estimate',
643+
budgetSourceId: srcA,
644+
});
633645

634646
const withEmpty = await app.inject({
635647
method: 'GET',
@@ -666,9 +678,21 @@ describe('GET /api/budget/breakdown', () => {
666678
const srcA = insertBudgetSource({ name: 'Source A S13', totalAmount: 80000 });
667679
const srcB = insertBudgetSource({ name: 'Source B S13', totalAmount: 50000 });
668680
const srcC = insertBudgetSource({ name: 'Source C S13', totalAmount: 30000 });
669-
insertWorkItemWithSource({ plannedAmount: 8000, confidence: 'own_estimate', budgetSourceId: srcA });
670-
insertWorkItemWithSource({ plannedAmount: 4000, confidence: 'own_estimate', budgetSourceId: srcB });
671-
insertWorkItemWithSource({ plannedAmount: 2000, confidence: 'own_estimate', budgetSourceId: srcC });
681+
insertWorkItemWithSource({
682+
plannedAmount: 8000,
683+
confidence: 'own_estimate',
684+
budgetSourceId: srcA,
685+
});
686+
insertWorkItemWithSource({
687+
plannedAmount: 4000,
688+
confidence: 'own_estimate',
689+
budgetSourceId: srcB,
690+
});
691+
insertWorkItemWithSource({
692+
plannedAmount: 2000,
693+
confidence: 'own_estimate',
694+
budgetSourceId: srcC,
695+
});
672696

673697
const response = await app.inject({
674698
method: 'GET',
@@ -696,15 +720,19 @@ describe('GET /api/budget/breakdown', () => {
696720
});
697721

698722
// Scenario 15: 'unassigned' literal in deselectedSources param (AC #30)
699-
it("excludes null-source lines when ?deselectedSources=unassigned is passed (Scenario 15)", async () => {
723+
it('excludes null-source lines when ?deselectedSources=unassigned is passed (Scenario 15)', async () => {
700724
const { cookie } = await createUserWithSession(
701725
'filter-s15@example.com',
702726
'Filter S15',
703727
'password',
704728
);
705729

706730
const srcA = insertBudgetSource({ name: 'Source A S15', totalAmount: 80000 });
707-
insertWorkItemWithSource({ plannedAmount: 5000, confidence: 'own_estimate', budgetSourceId: srcA });
731+
insertWorkItemWithSource({
732+
plannedAmount: 5000,
733+
confidence: 'own_estimate',
734+
budgetSourceId: srcA,
735+
});
708736
// Insert an unassigned WI (budgetSourceId=null) using the standard insertWorkItem helper
709737
insertWorkItem({ plannedAmount: 2000, confidence: 'own_estimate' });
710738

0 commit comments

Comments
 (0)