Support ticket #396994 (Jannick): pull prices from
haandvaerkpriser.dk for tasks we don't already price, as a starting
reference. robots.txt allows crawling /alle-priser/ and no terms of
use restrict reuse (checked before building this).
Each imported task becomes a verified 'component' Smart Package with
a single task carrying the price, filtered to only the 8 trades
Jannick asked for (Maler, Gulv, VVS & Blikkenslager, Tømrer &
Snedker, Murer, Tagrenovering, Isolering & Energi, Nedrivning &
Affald - the site has 16 categories total). This lands in the same
place as today's earlier smart-package fix, so these are immediately
selectable via the "Enkelte opgaver" picker and visible in the admin
Smart Packages list, unlike the existing price_database table which
is dead code (0 rows, its only consumer queries a column that no
longer exists, and its route isn't mounted).
Re-running the import is idempotent: a task already imported from
this source gets its price averaged with the new pull instead of
duplicating, per Jannick's request ("har vi en pris i forvejen kan
den lave et gennemsnit"). Cross-source matching against Jannick's own
manually-curated components is intentionally out of scope - automating
that risks merging unrelated tasks.
New endpoints (admin-only): GET /api/smart-packages/haandvaerkpriser-preview
(dry run, no writes) and POST /api/smart-packages/haandvaerkpriser-import.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
deploy-prod.yml targeted a remote SSH deploy server via
PROD_SERVER_HOST/PROD_SERVER_USER/PROD_SERVER_PATH/PROD_SSH_PRIVATE_KEY,
but none of those secrets were ever configured (repo and production
environment secret lists are both empty) and every historical run of
the workflow failed as a result.
The actual production server is this machine: unified-server.js runs
directly out of this working directory via PM2. Removed the dead
pipeline and documented the real manual deploy steps (pull, rebuild
frontend, sync backend deps, pm2 restart, verify) in CLAUDE.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The prod deploy just failed at Unit Tests even though all 226 tests
passed - the job died on `Upload coverage report` hitting the GitHub
Actions artifact storage quota. ci.yml already treats artifact uploads
as best-effort for this exact reason (comment: "Artifact upload must
never fail the job (storage quota can be full)"), but deploy-prod.yml
and deploy-test.yml were missing the same continue-on-error on their
diagnostic report/screenshot/coverage uploads.
Leaves the actual deployable build artifact uploads (deploy-package-*)
blocking, since a failed deploy really should stop there.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The blocking carpenter smoke test (introduced in this branch) exposed
a pre-existing gap: playwright.config.js hardcoded
executablePath: '/snap/bin/chromium' plus channel: 'chromium' for the
maintainer's local CachyOS machine. CI has no /snap/bin/chromium (it
installs its own Chromium via `playwright install --with-deps
chromium`), so every CI run was silently failing to launch the browser
- previously masked because the whole E2E step had
continue-on-error: true.
Gate both overrides behind `!process.env.CI` so local runs keep using
system Chromium and CI falls back to its own installed browser.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements the plan from the carpenter walkthrough audit: make the
project flow -> final review -> PDF path measurable and safe to gate
CI on.
- projectHydrationService loads a project's geometry/labor/materials/
rentals/packages/calculation in parallel and reports per-resource
timing plus the slowest resource, so hydration SLA breaches are
visible instead of silent
- projectReadiness centralizes the blocking checks final review must
pass before a quote can be sent (name, customer, description,
materials/labor present, zero-price lines)
- pdfSourceSignature detects when a generated PDF is stale relative to
the current project data, so a carpenter doesn't send an outdated
quote
- MaterialLinkReview + new customer-projects match-preview/link
endpoints let a user review and confirm suggested master-material
matches instead of silently auto-linking
- ProjectFlow refactored around the new hydration/readiness utilities;
FinalReview surfaces readiness checks and unlinked material counts
- CI: the carpenter smoke test (full-roof-quote-flow.spec.js) is now
a blocking gate on main, with the rest of the Playwright suite
running best-effort alongside it
- docs/qa/CARPENTER_WALKTHROUGH_RELEASE_GATE.md documents the gate and
the monthly production audit procedure
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Root cause (from support tickets #333382 and #775903): a one-off
integrity migration archived 40 Excel-imported carpenter task packages
(is_active=0, blocked) because their material lines couldn't be
auto-linked to the material database, and even a verified component
package had nowhere to appear since the quote flow only fetched
packageType=complete_offer.
- verifyPackage now reactivates a package (is_active=1) once its
materials/tasks pass integrity checks
- getPackageDetails/updatePackage can reach blocked-but-inactive
packages so admins can repair them, without exposing genuinely
deleted packages; fixes a latent bug where updatePackage's final
UPDATE still hardcoded is_active=1 and would have silently no-op'd
- new GET /api/smart-packages/review-queue + SmartPackageReviewQueue
admin panel to relink materials (with fuzzy-match suggestions) and
verify archived packages in one step
- InlineSmartPackage gains an "Enkelte opgaver" à la carte picker
(search-by-word, additive multi-select) for verified component
packages, alongside the existing single complete-offer picker;
geometry-based quantity/hours math extracted into
utils/smartPackageGeometryCalculations.js so both flows share it
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The last CI run failed only because GitHub artifact storage quota was
exhausted: backend tests and frontend build were green but their
upload-artifact steps failed the jobs.
- Mark all artifact uploads continue-on-error and cut retention to 3
days (deploy workflows kept artifacts up to 30 days, which is what
filled the quota)
- E2E job builds the frontend itself instead of downloading the
frontend-build artifact, so it no longer depends on artifact storage
- Bump actions/checkout v4->v7, setup-node v4->v6, upload-artifact
v4->v7, download-artifact v4->v8 (Node 20 runtime deprecation) and
CI Node 18 (EOL) -> 22 to match production
- Fix all frontend lint warnings: remove unused imports/state and the
dead Stepper-based form UI in SmartPackageForm (renderStepContent,
parseAndAddMaterialsFromDescription were unreferenced), disable
exhaustive-deps where the effect is intentionally scoped
Both steps relied on a debounced 5-second autosave as the only path
that wrote to the database. Continuing to the next step within those
5 seconds unmounted the component, cancelled the pending timer and
silently lost the data: Final Review then showed 0 m dimensions and
'Tagtype: unknown', and package materials/tasks were missing after a
project reload.
- EnhancedGeometry: handleNext now awaits handleGeometryCalculation
and blocks navigation if the save fails; the continue button shows
'Gemmer geometri...' while saving
- InlineSmartPackage: 'Gem Smart Pakke' and 'Naeste: Final Review' now
await autoSavePackageData (which syncs materials/rentals/status)
instead of only updating parent session state; autoSavePackageData
reports success; selecting a package marks the step as interacted so
the periodic autosave arms correctly
- InlineSmartPackage: rentals read and similar-experience lookup use
the correct /api/customer-projects/projects/{id}/... paths
Found by driving five different quote flows end to end: three runs
happened to win the autosave race, two lost it and persisted nothing.
GET rentals used /api/customer-projects/{id}/rentals but the backend
route lives at /api/customer-projects/projects/{id}/rentals, so saved
rental lines were never loaded back into the flow. The
similar-experience lookup used /api/customer/projects/... and always
404'ed, so historical experience data never appeared in the smart
package step.
- Scope global .materials-header rule to used-materials so the
Materialer page header stacks correctly on mobile
- Collapse system status indicator to a compact dot so it no longer
covers content
- Remove redundant current-view chip from header navigation
- Scope FinalReview button styles and switch submit gradient from red
to green so the danger color no longer leaks into project creation
- Remove duplicate title and category chip on smart package cards
- Stop absolute positioning of logout button that clipped the logo