release: promote v2.8.0 to main#1721
Merged
Merged
Conversation
…d duration to daily log (#1673) * feat(diary): add optional vendor and work start/end time with computed duration to daily log Extends daily_log diary entries with three new optional fields: vendor (via SearchPicker), work start time, and work end time (both HH:mm 24h inputs). The server resolves vendorName from vendorId for display. A computed duration (hours, 2 decimal places) is calculated client-side and shown inline between the time inputs. Cross-field validation rejects end ≤ start. DiaryMetadataSummary renders the new fields for saved entries. German translations and full unit + E2E test coverage included. Fixes #1672 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <[email protected]> Co-Authored-By: Claude backend-developer (Haiku 4.5) <[email protected]> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <[email protected]> Co-Authored-By: Claude translator (Sonnet 4.6) <[email protected]> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <[email protected]> * test(diary): add computeWorkDuration to formatters mock factories DiaryMetadataSummary now re-exports computeWorkDuration from formatters.js, so any test file that mocks formatters.js and transitively imports DiaryMetadataSummary must include the export or the suite fails with a SyntaxError. Add computeWorkDuration to the mock factories in DiaryPage.test.tsx and DiaryEntryDetailPage.area.test.tsx (matching the style of each file's existing computeActualDuration stub). Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]> --------- Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…items (#1678) Previously, when auto-itemizing an invoice, line items where `includesVat` is `false` (net prices) were summed at their raw stored value without applying the applicable VAT rate. This caused the computed auto-itemize total to be ~19% lower than the actual gross amount, producing a false variance on the invoice and triggering a spurious `TOTAL_MISMATCH` error during itemized-sum validation. The fix introduces a shared helper `effectiveLineAmount(line)` in `@cornerstone/shared` that returns the gross-equivalent amount for any line: `amount * (1 + vatRate)` for net lines and `amount` for gross (includesVat=true) lines. All total-computation paths in `invoiceAutoItemizeService` now use this helper. Storage is unchanged — line amounts are still persisted as net; the VAT gross-up is applied only at aggregation time. Fixes #1677 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
#1684) Hoisted 52 inline `import()` type annotations to top-of-file `import type` declarations across 23 test and page-object files (14 client, 4 server, 5 e2e). No runtime behavior change — purely mechanical lint compliance. Fixes #1458 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…ollision (#1685) Add an exact-version override (10.3.0) for the `konva` package in the root `package.json` so the entire workspace tree hoists a single Konva copy. Without this override the root package had no Konva entry while `@cornerstone/client` depended on it directly, allowing npm to silently resolve two different Konva versions under certain install conditions and causing TypeScript type-collision errors. Also adds `scripts/check-single-dep-version.sh`, a guardrail script that asserts exactly one version of a given package is present across the `node_modules` tree. The script is wired into the Static Analysis job in `ci.yml` so that any future Konva version drift fails CI immediately. Fixes #1646 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…rlay (#1680) Replaces the hidden context-menu unlink action with a visible overlay button on LinkedDocumentCard that appears on hover/focus. Also fixes a bug where the unlink-confirmation modal's Cancel button rendered a raw i18n key instead of the translated string (was using `t('button.cancel')` without the required `common:` namespace prefix). Fixes #1680 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…re usable (#1688) (#1689) Fixes #1688 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…arnings (#1469) (#1690) Adds justified inline eslint-disable directives (with -- reason comments) and renames stale `react-hooks/exhaustive-deps` directives to the real rule `@eslint-react/exhaustive-deps` across 45 client/src/ files. All changes are comment-only or behavior-neutral dependency-array reformats; no logic or rendering behavior is altered. The inline disables are explicitly permitted by the issue's acceptance criteria where refactoring would require non-trivial state-model changes. Verified result: zero remaining set-state-in-effect errors, zero @eslint-react/exhaustive-deps errors, zero stale-rule "Definition for rule not found" errors, zero unused-disable directives. Net ESLint error count drops by 20 vs beta HEAD with no new errors introduced. Fixes #1469 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…-pattern (#1568) (#1686) * test(infra): fix per-render useNavigate jest.fn() anti-pattern in LinkedDocumentsSection Replace `useNavigate: () => jest.fn()` with a stable module-scope mock (`const mockNavigate = jest.fn()`) that is cleared in beforeEach. The per-render pattern allocates a new function object on every component render/re-render within a test, which inflates Jest worker heap over time when the test suite has many renders. The stable module-scope mock reuses the same function across all renders in a test run. This is Phase 4 from issue #1568 (Jest memory audit). Phases 2-3 (shared formatters mock factory via static import) were investigated but cannot be delivered: adding a static `import` before `jest.unstable_mockModule` in Jest 30 ESM VM mode causes the mock to fail to intercept the real module in CI (shards 1, 2), even though the factory itself is sound. The inline factory pattern is required for reliable mock registration in Jest ESM. Fixes #1568 Co-Authored-By: Claude qa-integration-tester (Sonnet 4.6) <[email protected]> * chore(memory): document Jest ESM static-import mock constraint from issue #1568 investigation Co-Authored-By: Claude qa-integration-tester (Sonnet 4.6) <[email protected]> --------- Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude qa-integration-tester (Sonnet 4.6) <[email protected]>
…1463) (#1691) Replace `any` with `unknown` or precise types where the shape is known, and mark genuine boundary escape hatches (Fastify decorators, dynamic drizzle table rows, schema-erased DB handles, WebDAV inject verbs) with reasoned per-line eslint-disable comments. Type-only changes; no runtime or test-behavior changes. Fixes #1463 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude backend-developer (Haiku 4.5) <[email protected]>
Added a Node-scripts flat-config block in eslint.config.js (Node globals + no-console: off for scripts/**) ordered after the General rules block, clearing 171 no-undef/no-console problems in scripts/*.mjs. Real fixes in server/src (calendarIcal, diaryService: no-useless-assignment), shared/src (diary.ts: no-empty-object-type interface→type alias), and client/src (no-unused-vars, no-case-declarations, preserve-caught-error cause, IIFE-in-JSX extraction, useState setter renames). Justified inline disables in client/src where no stable id exists for keys, render-time `new Date()` is intentional, DOM-measurement useEffect/setState patterns are correct, and naming collisions exist in LocaleContext/ThemeContext. Test file disables cover error-boundaries/rules-of-hooks edge cases. Dead-variable removals in e2e/tests (no-useless-assignment, no-unused-vars, no-self-assign) with no behavior or assertion changes. Full-project `eslint .` now reports 0 errors and 0 warnings (down from 857 at the start of the #1455 tracking issue; all 16 child issues already closed). Fixes #1455 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…h, jsx-no-children-prop) (#1696) - Rename local `async function fetch()` in useTimeline.ts to `loadTimeline` to eliminate the false-positive @eslint-react/web-api-no-leaked-fetch warning (the function is not the Web Fetch API, but shares the name) - Add justified eslint-disable-next-line for LinkedDocumentsSection.tsx where `systemLinkedIds.fetch()` is a custom hook method, not the Web Fetch API — the rule cannot distinguish call sites by type - Move `children` from the React.createElement props object to the 3rd positional argument at two call sites in PhotoMetadataSidepanel.test.tsx to fix jsx-no-children-prop; behavior-identical refactor All changes are behavior-preserving. `eslint .` now reports 0 errors and 0 warnings across the full client workspace, completing the clean-lint baseline established by #1455. Ref #1455 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
… orientation metadata (#1674) Mobile-first photo upload: Take photo / Upload photo split on touch devices, per-photo metadata capture modal (description, hierarchical area, orientation) with non-blocking background upload queue, new user-configurable Orientation entity (settings CRUD + nullable photos.orientation_id FK, SET NULL on delete), OrientationPicker showing name + description, orientation field in the photo metadata sidepanel, and the orientation-selector empty-state hint. Fixes #1674. Fixes #1675.
#1681) Adds a Paperless-first invoice creation flow on Budget → Invoices: when Paperless + LLM are configured, "New Invoice" opens a document picker (searchable correspondent filter, hide-already-linked default ON, open-in-Paperless link, manual-entry escape). Selecting a document runs a stateless LLM auto-itemize preview (LLM picks the vendor from the app vendor list), and the invoice + document link + budget-line itemizations are created atomically only after human review. New endpoints: GET /api/paperless/correspondents, POST /api/invoices/auto-itemize/preview, POST /api/invoices/auto-itemize/commit (ADR-032). No DB schema changes. Fixes #1679
…1693) Bumps the github-actions group with 1 update: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 4.36.1 to 4.36.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@87557b9...8aad20d) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.36.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the dev-dependencies group with 7 updates: | Package | From | To | | --- | --- | --- | | [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/plugins/eslint-plugin) | `5.8.12` | `5.8.19` | | [concurrently](https://github.com/open-cli-tools/concurrently) | `10.0.1` | `10.0.3` | | [prettier](https://github.com/prettier/prettier) | `3.8.3` | `3.8.4` | | [semantic-release](https://github.com/semantic-release/semantic-release) | `25.0.3` | `25.0.5` | | [stylelint](https://github.com/stylelint/stylelint) | `17.12.0` | `17.13.0` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.60.1` | `8.61.0` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.9.1` | `25.9.3` | Updates `@eslint-react/eslint-plugin` from 5.8.12 to 5.8.19 - [Release notes](https://github.com/Rel1cx/eslint-react/releases) - [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md) - [Commits](https://github.com/Rel1cx/eslint-react/commits/v5.8.19/plugins/eslint-plugin) Updates `concurrently` from 10.0.1 to 10.0.3 - [Release notes](https://github.com/open-cli-tools/concurrently/releases) - [Commits](open-cli-tools/concurrently@v10.0.1...v10.0.3) Updates `prettier` from 3.8.3 to 3.8.4 - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](prettier/prettier@3.8.3...3.8.4) Updates `semantic-release` from 25.0.3 to 25.0.5 - [Release notes](https://github.com/semantic-release/semantic-release/releases) - [Commits](semantic-release/semantic-release@v25.0.3...v25.0.5) Updates `stylelint` from 17.12.0 to 17.13.0 - [Release notes](https://github.com/stylelint/stylelint/releases) - [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md) - [Commits](stylelint/stylelint@17.12.0...17.13.0) Updates `typescript-eslint` from 8.60.1 to 8.61.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.0/packages/typescript-eslint) Updates `@types/node` from 25.9.1 to 25.9.3 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@eslint-react/eslint-plugin" dependency-version: 5.8.19 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: concurrently dependency-version: 10.0.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: prettier dependency-version: 3.8.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: semantic-release dependency-version: 25.0.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: stylelint dependency-version: 17.13.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: typescript-eslint dependency-version: 8.61.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: "@types/node" dependency-version: 25.9.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… 8 updates (#1695) * chore(deps): bump the prod-dependencies group with 8 updates Bumps the prod-dependencies group with 8 updates: | Package | From | To | | --- | --- | --- | | [ical-generator](https://github.com/sebbo2002/ical-generator) | `10.2.0` | `11.0.0` | | [sharp](https://github.com/lovell/sharp) | `0.34.5` | `0.35.1` | | [tar](https://github.com/isaacs/node-tar) | `7.5.15` | `7.5.16` | | [i18next](https://github.com/i18next/i18next) | `26.2.0` | `26.3.1` | | [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.2.6` | `19.2.7` | | [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `19.2.6` | `19.2.7` | | [react-konva](https://github.com/konvajs/react-konva) | `19.2.4` | `19.2.5` | | [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) | `7.15.1` | `7.17.0` | Updates `ical-generator` from 10.2.0 to 11.0.0 - [Release notes](https://github.com/sebbo2002/ical-generator/releases) - [Changelog](https://github.com/sebbo2002/ical-generator/blob/develop/CHANGELOG.md) - [Commits](sebbo2002/ical-generator@v10.2.0...v11.0.0) Updates `sharp` from 0.34.5 to 0.35.1 - [Release notes](https://github.com/lovell/sharp/releases) - [Commits](lovell/sharp@v0.34.5...v0.35.1) Updates `tar` from 7.5.15 to 7.5.16 - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](isaacs/node-tar@v7.5.15...v7.5.16) Updates `i18next` from 26.2.0 to 26.3.1 - [Release notes](https://github.com/i18next/i18next/releases) - [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md) - [Commits](i18next/i18next@v26.2.0...v26.3.1) Updates `react` from 19.2.6 to 19.2.7 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react) Updates `react-dom` from 19.2.6 to 19.2.7 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react-dom) Updates `react-konva` from 19.2.4 to 19.2.5 - [Release notes](https://github.com/konvajs/react-konva/releases) - [Commits](https://github.com/konvajs/react-konva/commits) Updates `react-router-dom` from 7.15.1 to 7.17.0 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/[email protected]/packages/react-router-dom) --- updated-dependencies: - dependency-name: ical-generator dependency-version: 11.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: prod-dependencies - dependency-name: sharp dependency-version: 0.35.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-dependencies - dependency-name: tar dependency-version: 7.5.16 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: i18next dependency-version: 26.3.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-dependencies - dependency-name: react dependency-version: 19.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: react-dom dependency-version: 19.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: react-konva dependency-version: 19.2.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: react-router-dom dependency-version: 7.17.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: prod-dependencies ... Signed-off-by: dependabot[bot] <[email protected]> * fix(deps): repair react override mismatch in prod-dependencies bump Dependabot's prod-dependencies bump updated the react/react-dom edges in client/ and docs/ to 19.2.7 but left the root package.json `overrides` pinned at 19.2.6. The override forced the top-level react node to 19.2.6 while workspaces requested 19.2.7, leaving duplicate React copies in the tree. This crashed the client bundle at runtime ("Cannot read properties of null (reading 'useRef')") and failed all E2E, and corrupted the tree such that `npm audit signatures` mis-resolved the @docusaurus/react-loadable alias (ETARGET [email protected]) — failing Static Analysis. Bump the overrides to 19.2.7 to match the workspace edges and regenerate the lockfile with a clean `npm install`. react and react-dom now dedupe to a single 19.2.7 across all workspaces. Co-Authored-By: Claude backend-developer (Haiku 4.5) <[email protected]> --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude backend-developer (Haiku 4.5) <[email protected]>
…tTests (#1697) - Add `workerGracefulExitTimeout: 2000` to the top-level Jest config; the 500 ms default is too tight for jsdom/file-watcher cleanup in the resource-constrained sandbox and produces spurious force-kill warnings. - Add `test:collect` and `test:collect:json` npm scripts using Jest 30.4.0's `--collectTests` flag to enumerate all test suites and names without executing them. - Document `test:collect` in CLAUDE.md Common Commands table. Fixes #1573 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude qa-integration-tester (Sonnet 4.5) <[email protected]>
…) (#1698) OrientationsTab referenced undefined CSS module classes so orientation rows rendered completely unstyled. Mapped all class references to the existing defined classes used by TradesTab and other Manage tabs (itemsList/itemRow/itemInfo/itemDetails/itemSortOrder/itemActions/ editActions/saveButton) and restructured view-mode JSX to match TradesTab layout. Added 20 unit tests covering both view and edit modes of OrientationsTab, and a deep-link E2E test verifying the Orientations tab is selected when navigating directly to #orientations. Fixes #1687 Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <[email protected]>
…-up (#1591) (#1699) The variance computation in AutoItemizePage sums effectiveLineAmount() which grosses up net (includesVat:false) amounts by 1.19. THREE_LINES has all three lines as net: 900+680+120 net = 1071+809.20+142.80 = 2023 gross. The test was using invoice amount=1700 (raw net sum), causing an immediate ~19% variance on page load and deterministic varianceMatch assertion failure. Fix: set invoice amount=2023 (gross total) so the initial state is a genuine 0% match, then editing line 0 to 500 net (=595 gross) produces a new gross total of 1547 vs 2023 = ~23.5% > 5% = varianceDanger as expected. Co-authored-by: Claude e2e-test-engineer (Sonnet 4.5) <[email protected]>
…ion (#1702) * fix(llm): disable model thinking/reasoning for budget invoice extraction Gemini 2.5 Flash has dynamic thinking enabled by default on its OpenAI-compat endpoint, causing 30s+ timeouts that surfaced as LLM_UNREACHABLE errors during budget invoice extraction. Fix: add per-provider reasoning suppression in buildRequestBody(): - Gemini: always send reasoning_effort:"none" (disables dynamic thinking) - Ollama: always send reasoning_effort:"none" (safely ignored on non-thinking models) - OpenAI: send reasoning_effort:"none" ONLY for reasoning models (o1/o3/o4/gpt-5 series, codex-mini); non-reasoning models such as gpt-4o reject the field with HTTP 400, so an isOpenAiReasoningModel() helper guards the injection with conservative pattern matching - Anthropic: no change (thinking already disabled by default) - Generic: no change (conservative — avoid breaking unknown providers) Fixes #1701 Co-Authored-By: Claude backend-developer (Haiku 4.5) <[email protected]> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]> Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <[email protected]> * fix(budgetExtraction): change OpenAI reasoning effort from 'none' to 'low' OpenAI reasoning models (o1/o3/gpt-5 series) do not support reasoning_effort='none' — they only accept 'low|medium|high'. The 'none' value caused HTTP 400 errors. Using 'low' is valid across all OpenAI reasoning model families and still minimizes reasoning cost for structured extraction. Gemini and Ollama continue to use 'none' (which they support) to fully disable thinking. Fixes #1701 Co-Authored-By: Claude backend-developer (Haiku 4.5) <[email protected]> * test(budgetExtraction): update OpenAI reasoning_effort expectation to 'low' Update o3-mini and gpt-5 test expectations to reflect that OpenAI reasoning models require 'low' (not 'none') as the minimum reasoning effort value — matching the production fix in f53a932. Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]> Co-Authored-By: Claude backend-developer (Haiku 4.5) <[email protected]> Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <[email protected]> --------- Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude backend-developer (Haiku 4.5) <[email protected]>
…1707) Add maximum-scale=1 and user-scalable=no to the viewport meta tag so iOS Safari no longer auto-zooms when an input is focused and pinch/double-tap zoom is disabled, preserving the native-app feel on mobile. Fixes #1707 Co-Authored-By: Claude frontend-developer (Haiku) <[email protected]>
…ll (#1712) Track the SearchPicker dropdown to its input field during mobile scroll via a requestAnimationFrame loop, with a close-on-out-of-view guard and an equality guard to avoid per-frame re-renders. Portal + position:fixed anti-clipping (#1601) preserved. Fixes #1708 Co-Authored-By: Claude frontend-developer (Haiku 4.5) <[email protected]> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <[email protected]>
…view and fix silent create failure (#1703, #1704) Extract shared auto-itemize components (line card, line list, budget-line picker modal) consumed by both the new-invoice and existing-invoice flows. Give the new-invoice page the two-column side-by-side PDF review layout and surface previously-silent create errors (inline vendor field error + visible banner). Fixes #1703 Fixes #1704
… annotation (#1705) * fix(photo-annotator): responsive scaling and touch support for mobile annotation Fix photo annotation editor to be responsive on mobile/tablet and enable touch drawing. The Stage is now fit-scaled within its container using ResizeObserver, keeping all shape coordinates in intrinsic photo-pixel space (no serialisation format changes). Stage width/height are scaled by fitScale, Stage.scaleX/Y are set to fitScale, and KonvaImage uses intrinsic dimensions. All drawing tools now use pointer events (onPointerDown/Move/Up) instead of mouse events. Pointer capture is enabled for reliable multi-touch drawing. The 1.0 fitScale cap prevents excessive zoom on small photos. Inline text input positioning already correctly computes scale from stageRect.width / photo.width, which equals fitScale. Changes: - konvaInit.ts: Add Konva.hitOnDragEnabled = true for better touch support - PhotoAnnotator.module.css: Add touch-action: none to .canvasArea - PhotoAnnotator.tsx: * Add canvasAreaRef and containerSize state * Add ResizeObserver effect to measure container dimensions * Add pointer capture effect for multi-touch drawing * Compute fitScale = min(containerW/photoW, containerH/photoH, 1.0) * Replace stageWidth/Height with scaled values * Keep KonvaImage dimensions as intrinsic (not scaled) * Rename all Stage handlers: onMouseDown/Up/Move → onPointerDown/Up/Move * Add scaleX={fitScale}, scaleY={fitScale} to Stage * Add ref={canvasAreaRef} to canvas container div Fixes #1705 Co-Authored-By: Claude frontend-developer (Haiku 4.5) <[email protected]> * fix(photo-annotator): add unit/E2E tests and fix ResizeObserver deps Add comprehensive unit tests for PhotoAnnotator covering responsive scaling, touch support, and annotation CRUD on mobile viewports. Extend react-konva Jest manual mock to support shape refs/methods used by the annotator. Add E2E scenarios 24-26 validating annotation creation on tablet/mobile, pinch-to-zoom interaction, and annotation persistence across device breakpoints. Also fix ResizeObserver effect dependency array ([] → [imageLoaded]) so the canvas re-sizes correctly when the image finishes loading. Fixes #1705 Co-Authored-By: Claude frontend-developer (Haiku 4.5) <[email protected]> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <[email protected]> * fix(photo-annotator): support mouse+touch drawing and fit-to-container scaling Binds both mouse (onMouseDown/Move/Up) and touch (onTouchStart/Move/End) events to the same shared handlers on the Konva Stage, so desktop mouse drawing and mobile touch drawing both work through a single code path. Handlers accept MouseEvent | TouchEvent and call preventDefault() on touch events to suppress scroll interference. The pointer-capture effect (which only fires on mouse) is removed, eliminating the regression that broke desktop drawing after the previous responsive-scaling commit. Unit tests updated to assert both mouse and touch handler props are forwarded; stale pointer-capture test removed. react-konva mock updated to forward mouse/touch handler-presence flags and includes the earlier ESM fix (no jest.fn() at module scope). Fixes #1705 Co-Authored-By: Claude frontend-developer (Haiku) <[email protected]> Co-Authored-By: Claude qa-integration-tester (Sonnet) <[email protected]> * fix(photo-annotator): use relative pointer position so shapes map to intrinsic coords Drawing handlers previously called `getPointerPosition()` which returns coordinates in Stage/screen space. When `fitScale < 1` (large photos or small viewports), the Stage is scaled down, so screen-space coords are compressed relative to the intrinsic image dimensions. Shapes therefore landed in the top-left quadrant instead of where the user drew. Switch to `getRelativePointerPosition()` on the layer, which automatically applies the inverse of the Stage transform and returns coordinates in intrinsic image space — matching the coordinate system used when the annotation is rendered back over the full-resolution image. New unit test asserts the round-trip under fitScale=0.5: a pointer event at screen (100,80) must produce a shape at intrinsic (200,160). The react-konva mock now exposes `getRelativePointerPosition()` as a distinct method (returning x*2, y*2 for the 0.5 scale fixture) separate from `getPointerPosition()`. Fixes #1705 Co-Authored-By: Claude frontend-developer (Haiku) <[email protected]> Co-Authored-By: Claude qa-integration-tester (Sonnet) <[email protected]> --------- Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude frontend-developer (Haiku 4.5) <[email protected]>
Cherry-picks the #1706 mobile lightbox metadata-toggle fix onto beta (originally merged to main as v2.7.1, leaving beta behind). Reconciles beta with main. Co-Authored-By: Claude frontend-developer (Haiku 4.5) <[email protected]> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <[email protected]>
…1714) Adopt @floating-ui/[email protected] (ADR-033) for SearchPicker dropdown positioning, replacing the hand-rolled portal/getBoundingClientRect/rAF approach. useFloating fixed-strategy + offset/flip/shift/size + autoUpdate + FloatingPortal keeps the dropdown anchored to its input during mobile momentum scroll and soft-keyboard/visualViewport shifts — the cases the prior rAF fix (#1712) could not handle. FloatingPortal preserves the #1601 anti-clipping; an isPositioned gate avoids the first-frame flash. Drops the hide() middleware (it blanked the dropdown inside modals). Fixes #1708 Co-Authored-By: Claude frontend-developer (Haiku 4.5) <[email protected]> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <[email protected]> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <[email protected]> Co-Authored-By: Claude product-architect (Sonnet 4.5) <[email protected]>
chore: reconcile main into beta (recover from mis-targeted #1710 squash)
Adds a required-status-check workflow that fails any pull request targeting 'main' whose head branch is not 'beta'. Mark the 'guard' job as a required check on the main ruleset to block out-of-band promotions (the #1710 class of incident, where a beta-based fix branch was squash-merged directly into main). Co-Authored-By: Claude Opus 4.8 <[email protected]>
ci: enforce that PRs into main originate from beta
…in CI (#1718) * fix(e2e): harden Scenario 14 against slow getDiaryEntry response in CI Register a page.waitForResponse for GET /api/diary-entries/:id BEFORE clicking the draft card, then await it after waitForURL completes. This ensures the heading/draftBadge assertions only fire after the API response has arrived, not after an arbitrary 15s wall-clock timeout. Also align both toBeVisible() timeouts to 45_000 (matching test.slow()'s 3× budget) so CI load spikes cannot exhaust the deadline. The previous { timeout: 15_000 } overrides bypassed test.slow()'s tripled expect.timeout and caused intermittent failures when the server response exceeded 15s under 8-worker CI load. Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <[email protected]> * fix(ci): install chrome-headless-shell for Playwright 1.60 and bump browser cache key Since Playwright 1.49+, `chrome-headless-shell` is a SEPARATE browser binary from `chromium` and must be explicitly installed. The previous install command (`npx playwright install chromium webkit`) omitted it, so every E2E test that uses the headless-shell channel failed with: browserType.launch: Executable doesn't exist at .../chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell Fix: add `chromium-headless-shell` to the warmup install step. The browser cache key is bumped from `playwright-v3` to `playwright-v4` (all four restore/save references) so any stale cached browser set that is missing the headless-shell binary is bypassed and a correct, complete set is re-seeded. The apt system-deps cache key (`apt-v3-playwright-*`) is intentionally left unchanged — only the `~/.cache/ms-playwright` browser cache key changes. Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <[email protected]> * fix(ci): add Playwright browser install-on-cache-miss fallback to smoke and shard jobs The E2E Cache Warmup job installs browsers and saves them under the `playwright-v4-{version}-{os}` cache key. However, the saved cache is not reliably visible to downstream jobs (e2e-smoke, e2e) — they receive "Cache not found for input keys: playwright-v4-..." and subsequently fail with "browserType.launch: Executable doesn't exist at .../chromium_headless_shell-1223/...". Changes: - Add `id: browser-cache` to the "Restore browser cache" step in both the smoke job and the shard matrix job (was already present in the warmup job). - After the "Install Playwright system dependencies" step in each of those two jobs, add a new conditional step that runs `npx playwright install chromium chromium-headless-shell webkit` only when `steps.browser-cache.outputs.cache-hit != 'true'`. This guarantees browsers are present regardless of cache visibility, eliminating the "Executable doesn't exist" failure class that causes entire shards to fail when the warmup cache is not populated or not accessible on the runner. Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <[email protected]> --------- Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude e2e-test-engineer (Sonnet 4.6) <[email protected]>
* docs: update documentation for v2.8.0 release Document Paperless-first invoice creation with auto-itemize, mobile-first photo capture with orientation tagging, diary daily-log vendor/work-hours, and the relocated document unlink action. Refresh README, RELEASE_SUMMARY, and add a new Capturing Photos guide. Co-Authored-By: Claude docs-writer (Sonnet 4.6) <[email protected]> * chore: add E2E test.slow() timeout-override lesson to implementation checklist Captures the v2.8.0 release lesson: test.slow() triples the project expect.timeout but an explicit per-assertion { timeout } override negates the tripling, causing intermittent CI failures (diary Scenario 14). Co-Authored-By: Claude Opus 4.8 <[email protected]> --------- Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude docs-writer (Sonnet 4.6) <[email protected]>
* chore: retrigger promotion CI (post-docs auto-fix skip-ci) The auto-fix bot pushed a [skip ci] commit to beta after the docs merge, advancing the v2.8.0 promotion PR #1700 HEAD to a commit with no CI runs. This clean-titled commit advances beta with a real (markdown-only) diff so CI fires and pull_request:synchronize re-runs on #1700. Co-Authored-By: Claude Opus 4.8 <[email protected]> * chore: nudge promotion CI synchronize Co-Authored-By: Claude Opus 4.8 <[email protected]> --------- Co-authored-by: Frank Steiler <[email protected]> Co-authored-by: Claude Opus 4.8 <[email protected]>
7 tasks
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Contributor
|
🎉 This PR is included in version 2.8.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release Summary — v2.8.0
Supersedes #1700 (whose required CI checks would not re-fire on the
betahead branch after an auto-fix[skip ci]push — a webhook-delivery issue). This PR promotes the identical content from a pushable head branch (promote/v2.8.0, byte-identical tobetaHEAD9d4f3b78) so CI fires reliably. Merge with a merge commit to preserve individual commits for semantic-release (→ minor v2.8.0).Features
Fixes
Chores
any, consistent type imports; Jest 30.4 test-infra; dependency bumpschrome-headless-shellinstall + browser cache-key bump + install-on-miss fallback; diary Scenario 14 flake hardening (fix(e2e): harden Scenario 14 against slow getDiaryEntry response in CI #1718)Incident recovery included
Reconciled
main→beta(#1716) after PR #1710 was mistakenly squash-merged intomain;mainis now a clean ancestor ofbeta.Testing