Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .claude/agent-memory/e2e-test-engineer/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
> Detailed notes live in topic files. This index links to them.
> See: `e2e-pom-patterns.md`, `e2e-parallel-isolation.md`, `story-epic08-e2e.md`, `story-933-dav-vendor-contacts.md`, `milestones-e2e.md`, `story-1248-mass-move.md`

## Budget Overview Hero Card Removed (Issues #1389/#1390, 2026-04-29)

- `<section aria-label="Budget overview">` (heroCard) is **gone** from BudgetOverviewPage.tsx after #1389.
- `BudgetOverviewPage.POM.waitForLoaded()` now races on `costBreakdownCard` instead of `heroCard`.
- `heroCard` locator kept in POM for historical reference but never matches on-page elements.
- Tests that asserted `heroCard.toBeVisible()` were removed from: `budget-overview.spec.ts`, `budget-overview-print.spec.ts`, `budget-source-filter.spec.ts`.
- New spec: `e2e/tests/budget/budget-overview-no-hero-card.spec.ts` (smoke, @smoke tag).
- Source badge (`aria-label="Budget source: {name}"`) is on Level 3 rows only — must expand Work Items → area → item to reveal budget lines.
- `BreakdownBudgetLine` fields: `id`, `description`, `plannedAmount`, `confidence`, `actualCost`, `hasInvoice`, `isQuotation`, `budgetSourceId` (NOT `sourceId`/`sourceName`).
- BudgetSources API mock response: `{ budgetSources: [{ id, name, ... }] }` — component only reads `s.id` and `s.name`.

## Budget Source Filter E2E (Story #1360, 2026-04-25 — server-side filter)

- **Story #1360** rewrote filter from client-side to server-side. `BudgetSourceSummaryBreakdown` now has `subsidyPaybackMin/Max` NOT `subsidyPayback`.
Expand Down
1 change: 1 addition & 0 deletions .claude/agent-memory/product-owner/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ All 12 stories merged. Paperless-ngx links for invoices are EPIC-08; budget repo

- **2026-02-27** — 11 issues #328-#338 (EPIC-06 + EPIC-05 sub-issues, 6 bugs / 5 stories)
- **2026-04-28** — 5 standalone UI bugs #1369-#1373 (no active parent epic; all related epics closed): #1369 hide-linked filter on Paperless picker, #1370 disable scroll-wheel on numeric inputs, #1371 "Includes VAT" parity for direct-amount budget lines, #1372 vendor in invoice picker, #1373 "Claimed" total on Budget Invoices summary. All Todo. Only EPIC-16 (Floor Plans) is currently open and is unrelated to these.
- **2026-04-29** — 2 standalone Budget Overview bugs #1389-#1390 (no parent epic): #1389 remove Budget Health hero card from `/budget/overview` (full deletion incl. helpers, state, CSS classes, i18n keys, hero-card-only tests), #1390 source-name badge missing from print preview (mobile media query hides label on print-width pages). Both Todo.

## Patterns and Conventions

Expand Down
4 changes: 4 additions & 0 deletions .claude/agent-memory/qa-integration-tester/MEMORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
> Detailed notes live in topic files. This index links to them.
> See: `budget-categories-story-142.md`, `e2e-pom-patterns.md`, `e2e-parallel-isolation.md`, `story-358-document-linking.md`, `story-360-document-a11y.md`, `story-epic08-e2e.md`, `story-509-manage-page.md`, `story-471-dashboard.md`

## Systemic jest.unstable_mockModule Issue in This Worktree (2026-04-29)

ALL client tests using `jest.unstable_mockModule('../../lib/formatters.js', ...)` fail locally in this worktree with `useLocale must be used within a LocaleProvider`. This is a pre-existing environment issue — tests pass in CI. **Do not attempt to fix by changing mocks or adding LocaleProvider** — the tests are structurally correct and the mock works in CI. Just commit and let CI validate. The issue is specific to this worktree's Jest module resolution environment.

## Story #1360 — Server-Side Source Filter Tests (2026-04-25)

**CostBreakdownTable.test.tsx**: Replaced the 12-test `describe('Source filter — aggregate consistency (#1358)')` block with 4-test `describe('Server-driven render path (#1360)')`. The 12 old tests tested deleted client-side helpers (`computePerSourcePayback`, `computeFilteredAggregates`, `visibleLineIds`). Removal strategy: Python `content.replace()` on large block — incremental Edit tool calls left orphaned code. The `buildBreakdownWithTwoSources()` helper was replaced by `buildServerFilteredBreakdown()`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,24 @@
.rowSourceDetailToggle[aria-pressed='false'] {
display: none !important;
}

/* Issue #1390: Print viewports map to ~600-720px which triggers the mobile
breakpoint and hides .sourceBadgeLabel. Force the label visible and the
dot hidden in print regardless of viewport width. */
.sourceBadgeLabel {
display: inline-flex !important;
}

.sourceBadgeDot {
display: none !important;
}

/* Make source badge legible without background-color in print using a border. */
.sourceBadgeLabel > * {
background: transparent !important;
border: 1pt solid var(--color-border-strong) !important;
print-color-adjust: exact;
}
}

/* ============================================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4473,3 +4473,40 @@ describe('filteredAvailableFunds — Available Funds row and Remaining Budget ro
expect(remainingCostCell!.textContent?.replace(/\s+/g, '')).toContain('€200,000.00');
});
});

// ── Source badge print visibility (#1390) ─────────────────────────────────────

describe('source badge print visibility (#1390)', () => {
// Verifies the DOM structure that the print-mode CSS in CostBreakdownTable.module.css depends on.
// The CSS toggles .sourceBadgeDot off and .sourceBadgeLabel on inside @media print;
// jsdom does not evaluate @media, so this test asserts both elements are present in the DOM,
// which is the contract the CSS relies on.
it('renders both .sourceBadgeDot and .sourceBadgeLabel with the badge inside the label for each budget line', () => {
const sourceId = 'src-1';
const sourceName = 'Bank Loan';
const breakdown = buildBreakdownWithSourcedWI({
budgetSourceId: sourceId,
budgetSources: [buildSourceSummary({ id: sourceId, name: sourceName })],
});

const { container } = renderWithRouter(breakdown, buildOverview());

// Expand WI section → area → item so BudgetLineRow renders in the DOM
fireEvent.click(getButtonByControls(container, 'wi-section-categories'));
fireEvent.click(getButtonByControls(container, 'area:No Area'));
fireEvent.click(getButtonByLabel('Expand Sourced Work Item'));

// .sourceBadgeDot — screen-visible colored dot, hidden in print via CSS
const dot = container.querySelector('[class*="sourceBadgeDot"]');
expect(dot).not.toBeNull();
expect(dot!.getAttribute('aria-hidden')).toBe('true');

// .sourceBadgeLabel — wraps the Badge; hidden on screen, shown in print via CSS
const label = container.querySelector('[class*="sourceBadgeLabel"]');
expect(label).not.toBeNull();

// The Badge inside has aria-label set by the source-badge code path.
const badgeChild = label!.querySelector('[aria-label]');
expect(badgeChild).not.toBeNull();
});
});
29 changes: 0 additions & 29 deletions client/src/i18n/de/budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,7 @@
"addInvoice": "Rechnung Hinzufügen",
"addVendor": "Auftragnehmer Hinzufügen"
},
"availableFunds": "Verfügbare Mittel",
"projectedCostRange": "Geplanter Kostenbereich",
"expectedPayback": "Erwartete Rückzahlung",
"paybackCapped": "Einige Förderprogramme sind überzeichnet – Rückzahlungswerte sind begrenzt",
"remaining": "Verbleibend",
"remainingDetail": "Verbleibendes Budget – tippen Sie für Details",
"categories": "Kategorien",
"allCategories": "Alle Kategorien",
"noCategories": "Keine Kategorien",
"selectAll": "Alle auswählen",
"clearAll": "Alle deaktivieren",
"bars": {
"claimedInvoices": "Eingereichte Rechnungen",
"paidInvoices": "Bezahlte Rechnungen",
"pendingInvoices": "Ausstehende Rechnungen",
"projectedOptimistic": "Projiziert (optimistisch)",
"projectedPessimistic": "Projiziert (pessimistisch)",
"overflow": "Überlauf"
},
"remainingPerspectives": {
"vsMinPlanned": "Verbleibend vs. Min. Geplant",
"vsMaxPlanned": "Verbleibend vs. Max. Geplant",
"vsProjectedMin": "Verbleibend vs. Projiz. Min.",
"vsProjectedMax": "Verbleibend vs. Projiz. Max.",
"vsActualCost": "Verbleibend vs. Tatsächliche Kosten",
"vsActualPaid": "Verbleibend vs. Tatsächlich Bezahlt",
"vsMinPlannedWithPayback": "Verbleibend vs. Min. Geplant (inkl. Rückzahlung)",
"vsMaxPlannedWithPayback": "Verbleibend vs. Max. Geplant (inkl. Rückzahlung)"
},
"ofAvailableFunds": "der verfügbaren Mittel",
"costBreakdown": {
"tableCaption": "Kostenaufschlüsselung nach Bereich und Element",
"loading": "Kostenaufschlüsselung wird geladen…",
Expand Down
29 changes: 0 additions & 29 deletions client/src/i18n/en/budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,7 @@
"addInvoice": "Add Invoice",
"addVendor": "Add Vendor"
},
"availableFunds": "Available Funds",
"projectedCostRange": "Projected Cost Range",
"expectedPayback": "Expected Payback",
"paybackCapped": "Some subsidies are oversubscribed — payback values are capped",
"remaining": "Remaining",
"remainingDetail": "Remaining budget — tap for details",
"categories": "Categories",
"allCategories": "All categories",
"noCategories": "No categories",
"selectAll": "Select All",
"clearAll": "Clear All",
"bars": {
"claimedInvoices": "Claimed Invoices",
"paidInvoices": "Paid Invoices",
"pendingInvoices": "Pending Invoices",
"projectedOptimistic": "Projected (optimistic)",
"projectedPessimistic": "Projected (pessimistic)",
"overflow": "Overflow"
},
"remainingPerspectives": {
"vsMinPlanned": "Remaining vs Min Planned",
"vsMaxPlanned": "Remaining vs Max Planned",
"vsProjectedMin": "Remaining vs Projected Min",
"vsProjectedMax": "Remaining vs Projected Max",
"vsActualCost": "Remaining vs Actual Cost",
"vsActualPaid": "Remaining vs Actual Paid",
"vsMinPlannedWithPayback": "Remaining vs Min Planned (incl. payback)",
"vsMaxPlannedWithPayback": "Remaining vs Max Planned (incl. payback)"
},
"ofAvailableFunds": "of available funds",
"costBreakdown": {
"tableCaption": "Budget cost breakdown by area and item",
"loading": "Loading cost breakdown…",
Expand Down
Loading