Commit graph

1720 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
2f3e4b4c8d feat(scenarios): surface and set fabric-first across scenario pages
Adds the fabric-first constraint to the app-authored scenario flow:

- Create: a "Fabric first" toggle in the Measures step (Step 2) with an
  explanation, mirrored in the Step 3 review and carried through the
  "Use as template" (?from=) flow.
- List card: a "Fabric first" chip in the constraints box, shown only
  when the scenario is fabric-first.
- Detail page: an always-shown "Fabric first" config row (chip +
  explanation, or "Off").

Domain layer (TDD): validateScenarioConfig carries the flag and defaults
it to false; scenarioToInsertRow persists it; findExactDuplicate treats
it as part of the immutable config identity, so two scenarios differing
only in fabric-first are distinct. Threaded through ScenarioListItem,
both query mappers, the create route, and the new-scenario loader.

Backend already reads scenario.fabric_first for id-based modelling runs,
so no dispatch changes are needed.

Docs: adds a "Fabric first" glossary entry to CONTEXT.md, softens the
stale "only measure-constraint" line, and pins the distinction from the
"Fabric only" quick-start.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 13:51:55 +00:00
KhalimCK
e48a5f4ae2
Merge pull request #381 from Hestia-Homes/feature/scenario-fabric-first
feat(db): add fabric_first flag to scenario
2026-07-09 12:37:01 +01:00
Khalim Conn-Kowlessar
9e57596b64 feat(db): add fabric_first flag to scenario
Adds `fabric_first boolean NOT NULL DEFAULT false` to the scenario table
via the drizzle model, with the generated migration (0268).

Migration to be run via `npm run migration:migrate` once merged to main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 11:32:58 +00:00
KhalimCK
9fa11b0b6d
Merge pull request #379 from Hestia-Homes/fix/hmo-classification-code
fix(postcode-search): classify RH (HMO) family as addable residential
2026-07-09 11:54:12 +01:00
Khalim Conn-Kowlessar
fac425762e fix(postcode-search): classify RH (HMO) family as addable residential
7 Leyton Avenue (UPRN 100031252876) came back from OS Places as RH03
("HMO Not Further Divided") and rendered as "Non-residential", so it
couldn't be added from the postcode-search "add properties" journey.

classifyOsCode decided "residential?" purely on an RD prefix, dropping the
whole RH (HMO) family — even though RD07 (the RD-family HMO code) was
already mapped to House. Extend the residential check to RH and map
RH01/RH02/RH03 to House (built form unknowable), consistent with RD07.

Updates ADR-0007's enumerated mapping table to stay truthful.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 10:33:07 +00:00
KhalimCK
8bbcd77974
Merge pull request #376 from Hestia-Homes/feature/bulk-document-download
feat(live-reporting): bulk document download (Documents tab)
2026-07-08 17:40:53 +01:00
Khalim Conn-Kowlessar
098a5ccffc fix(live-reporting): bulk download poller no longer hangs after completion
The status poll only declared "ready" when it could parse presigned_url out
of sub_task.outputs, so a finished job whose link used a different key (or
whose completion was only reflected in status) sat on "preparing" forever.

- Derive terminal state from completion/failure *status* (task or sub_task,
  incl. jobCompleted), not just the parsed link.
- Tolerate link-key variants (presigned_url / presignedUrl / download_url /
  url; package_s3_key / s3_key).
- When completed but the link can't be surfaced in-app, show a "ready — check
  your email" card instead of hanging.
- Ease the poll cadence to 8s (assembly takes minutes; email is the real
  channel).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 16:34:00 +00:00
Khalim Conn-Kowlessar
7a7f7e157e feat(live-reporting): select page vs all pages in bulk download
The header tick now selects the current page (its intuitive meaning). When
the whole page is ticked and more filtered rows exist, a Gmail-style banner
offers "Select all N across pages" (and "Clear selection" once everything
is selected) — so users can grab a page-sized chunk without selecting the
entire project.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 16:04:53 +00:00
Jun-te Kim
e9b03fef1a
Merge pull request #377 from Hestia-Homes/feature/hubspot-deal-planning-columns
feat(crm): add planning/address-profiling columns to hubspot_deal_data
2026-07-08 15:50:34 +01:00
Jun-te Kim
177ffd0b83 feat(crm): add planning/address-profiling columns to hubspot_deal_data
Add eight nullable text columns to the hubspot_deal_data table to hold
planning and address-profiling attributes synced from HubSpot:

  design_constraints, planning_comments, planning_status,
  planning_suggested_approach, planning_authority, designated_area,
  article_4_pd_rights, listed_building

Typed as text to match how the table already stores HubSpot
dropdown/enum-style properties (e.g. dampmould_growth, domna_survey_type).

Migration: 0267_add_hubspot_deal_planning_columns.sql

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 14:43:58 +00:00
Khalim Conn-Kowlessar
34dd3fe7ae feat(live-reporting): bulk document download from the Documents tab
Add bulk document download as a row-selection flow on the Live Reporting
Documents tab: a "Bulk download" button flips the existing DocumentTable
into selection mode (per-row tickers + select-all across all filtered
pages); "Download selected" zips those properties' documents and emails a
link (shown in-app while the page stays open). Scoped by the tab's
existing Project/group selector.

Two-step trigger (ADR-0008 convention): insert an app-owned tasks row
(task_source "app:bulk_document_download", selection config JSON in
tasks.inputs, source_id = portfolio) then dispatch only { task_id } to
POST /v1/documents/bulk-download. Documents are matched by
landlord_property_id, so the ticked ids ARE the selection — a property
with no landlord id has no documents and is un-tickable.

Resilience: the client now parses responses defensively (a platform 504's
HTML body no longer surfaces as "Unexpected token 'A'…"), and the POST
route sets maxDuration=60 so the synchronous backend resolution has
headroom before the browser gets a 504.

Docs: CONTEXT.md gains Project + Bulk document download; ADR-0011 records
why the marker lives in task_source (worker re-reads the task) vs service.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 14:11:42 +00:00
KhalimCK
153c64195b
Merge pull request #374 from Hestia-Homes/perf/measures-plan-default-index
perf(db): covering partial index for the reporting measures aggregate
2026-07-08 13:57:19 +01:00
Khalim Conn-Kowlessar
4fd1e00e63 perf(db): covering partial index for the reporting measures aggregate
The reporting 'where the money goes' panel joins the latest plan per
property to its recommendations ON plan_id, filters to the default active
set, and sums estimated_cost / counts property_id per measure_type. The
only plan_id index (idx_recommendation_plan_id) is plain, so the aggregate
heap-fetches every recommendation to apply the filter and read those
columns — hundreds of thousands of fetches on large portfolios (434),
which times the panel out.

idx_recommendation_plan_default carries plan_id + measure_type +
property_id + estimated_cost as key columns (drizzle-orm has no INCLUDE),
partial on default = true AND already_installed = false, so the aggregate
runs index-only. Added to the schema and generated via drizzle-kit.

OPS: build CONCURRENTLY out-of-band on prod before the migration runs so it
no-ops there (0258 pattern); the migration uses IF NOT EXISTS for fresh
environments. See the migration file header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 11:52:35 +00:00
Jun-te Kim
4c65bca0c3
Merge pull request #367 from Hestia-Homes/feature/uprn-confirmation-onboarding
Confirm UPRNs before finalise: two-tab bulk-upload review + retire Unmatched tab (ADR-0057)
2026-07-08 10:28:09 +01:00
Jun-te Kim
72c6d9dd5a Merge remote-tracking branch 'origin/main' into feature/uprn-confirmation-onboarding
# Conflicts:
#	src/app/portfolio/[slug]/components/PropertyTable.tsx
2026-07-08 08:59:17 +00:00
KhalimCK
1344d1a77d
Merge pull request #369 from Hestia-Homes/feature/home-portfolio-redesign
feat(home): portfolio home redesign — folders, starring, drag-to-file
2026-07-07 22:18:38 +01:00
Khalim Conn-Kowlessar
8d56283df8 docs(adr): 0009 — portfolio organisation is per-user, not shared
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 21:11:21 +00:00
KhalimCK
326e191b79
Merge pull request #365 from Hestia-Homes/fix/scenario-exclusions-backend-vocab
fix(scenarios): exclusion vocabulary = backend MeasureType exactly
2026-07-07 18:55:37 +01:00
KhalimCK
4a437ec526
Merge pull request #360 from Hestia-Homes/feature/bulk-trigger-modelling
feat: bulk-trigger modelling runs with property filters
2026-07-07 18:55:20 +01:00
Jun-te Kim
3a1f9146b7 Merge branch 'main' of https://github.com/Hestia-Homes/assessment-model into feature/uprn-confirmation-page
# Conflicts:
#	src/app/db/migrations/meta/0261_snapshot.json
#	src/app/db/migrations/meta/_journal.json
2026-07-07 17:53:42 +00:00
Khalim Conn-Kowlessar
2caac6fa6c fix(portfolio): actually refetch the property table after adding properties
invalidateQueries only refetches mounted queries; the property table isn't
mounted on the add page, and useProperties' refetchOnMount: false then
suppresses the refetch when it does mount — so the invalidation never
produced a fetch. Removing the cached query instead guarantees the initial
fetch on mount.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:50:47 +00:00
Khalim Conn-Kowlessar
0b475d8e03 fix(portfolio): refresh server-rendered data after adding properties or saving a scenario
Both mutations navigated with router.push alone, so the destination served
the pre-mutation render from the client router cache: new properties were
missing from the portfolio table (compounded by useProperties' 5-minute
staleTime, now invalidated too) and a first scenario still showed
"No scenarios yet". router.refresh() after push busts the cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:37:16 +00:00
Khalim Conn-Kowlessar
8f7f5b3a88 fix(modelling): set-and-forget trigger state, visible back link, filter clearing
Post-review UX feedback on the run journey:

- The post-trigger card computed its ETA from the just-cleared scenario
  selection, so it always promised "~5 minutes" whatever the run size.
  ETA now comes from the captured run (estimateRunMinutes, TDD'd) and the
  done state is a compact status banner above a still-visible Recent runs
  table, which polls every 5s while a run is moving (historyRefetchInterval,
  TDD'd) — trigger, glance, leave.
- The only way back was a text-xs gray-400 breadcrumb (~2.5:1, sub-AA);
  replaced with an accessible "← Back to {portfolio}" link.
- "Remove all filters" button on Step 2.
- Scenario rows were a checkbox nested inside a button (state invisible to
  screen readers); now a label wrapping a real checkbox.
- Large-run confirm lists the scenario names and selection being committed,
  is labelled, closes on Escape, and starts focus on "Go back".
- Postcode filter no longer fails silently: loading and error-with-retry
  states.
- Run status "Dispatched" reads "Starting up"; scenarios page gains a
  "Run modelling" entry point.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:37:16 +00:00
Khalim Conn-Kowlessar
7e35f3c0a2 feat(home): Headless UI menus, drag-to-file onto rail, folder view in URL
Card and folder menus move to Headless UI v2 (Menu / Popover with
anchor positioning): panels portal to the top layer so the list view
can't clip them, and Esc, outside-click close, arrow-key nav, and menu
semantics come from the primitive — replaces the details/summary menus
and the fixed-position workaround. The folder popover hosts the
two-step remove confirm. Cards and table rows are draggable onto rail
targets (folder = file, Starred = star, All portfolios = unfile) with
drop highlighting; folder-reorder drag is type-guarded from card drags.
Selected rail view persists in the URL via replaceState (?folder=id /
?view=starred), read by the server component; unknown folder ids fall
back to All, and deleting the folder being viewed returns to All.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:20:47 +00:00
Khalim Conn-Kowlessar
ade33195cb fix(home): inline delete confirm, visible mutation errors, unclipped list menu
Impeccable critique (25/40) fixes: window.confirm replaced by a
two-step confirm inside the folder menu (states the unfile consequence,
red confirm takes focus, Escape cancels); mutation failures now render
a dismissible notice as well as the live region, and folder-create
errors are no longer swallowed; list-view card menu uses fixed
positioning measured on open so the table's overflow container can't
clip it; Escape closes card/folder menus with focus returned. Also:
search placeholder and rail-count contrast to >=4.5:1, folder-menu and
New-folder hit targets to >=24px, th scope=col, decorative ticks
aria-hidden, rename input regains a visible focus state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:01:16 +00:00
Khalim Conn-Kowlessar
1940f02cf3 feat(home): delete-folder confirm, view preference cookie, show-more batching
Folder removal now confirms with copy stating portfolios are unfiled,
not deleted. Grid/list preference persists in a cookie read by the
server component, so the chosen view renders on first paint. Long lists
render the first 24 rows with a show-more button; the reveal count
resets when the view or search changes, and starred float keeps pinned
work on the first screen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:32:19 +00:00
Jun-te Kim
3f649755b4 feat(portfolio): retire the Unmatched tab; surface low_score (ADR-0057 Phase 4/5)
Phase 4: the portfolio landing page now renders the property table directly.
UPRN matching is confirmed during onboarding, so properties arrive matched and
the post-onboarding "Unmatched" tab is no longer needed (tab components left in
place pending a legacy-no-UPRN reconciliation follow-up).
Phase 5: AddressesPanel labels the new "low_score" status "Low-confidence match".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 16:29:19 +00:00
Jun-te Kim
8b99e7029c feat(bulk-upload): two-tab review with pre-finalise Addresses/UPRN confirmation (ADR-0057)
At awaiting_review, when address2uprn flagged rows, OnboardingProgress now
shows two tabs: Classification (existing verify/unknown/ordering panels) and
Addresses. The Addresses tab (AddressesPanel) lists each flagged combiner row
with the OS Places matcher (reused search → residential candidates) and a
"No UPRN" action, saving corrections via useSaveUprnCorrection. Finalise is
gated on unresolvedAddresses === 0 with a matching disabled reason; the tab
carries a badge of the unresolved count.

tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 16:19:18 +00:00
Khalim Conn-Kowlessar
cfa181f3c7 feat(home): portfolio home redesign — folder rail, starred view, grid/list
Client page driven by useQuery on /api/me/portfolio-home with optimistic
mutations (star, move-to-folder, folder create/rename/delete, drag or
menu-based reorder). TDD'd view-model in portfolioHomeView.ts (status
pill mapping, search over display language, sort incl needs-attention,
money/date formatting, optimistic config upsert);
groupPortfoliosForHome generalised over id type so the client re-groups
with wire-format string ids. List view is a semantic table; replaces
CardTiles/Card/AddNewCard (NewPortfolioModal retained).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:18:44 +00:00
Jun-te Kim
ee22764e06 feat(bulk-upload): client hooks for address matches + corrections (ADR-0057)
useAddressMatches (flagged rows + unresolved count) and useSaveUprnCorrection
(assign UPRN / mark no-UPRN), matching the existing TanStack Query v4 conventions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 16:10:56 +00:00
Jun-te Kim
6743cd8bae feat(bulk-upload): gate finalise on unresolved addresses + overlay corrections (ADR-0057)
dispatchFinaliser now:
- blocks (before the CAS claim) when any flagged combiner row is still
  unresolved -> new "unresolved_addresses" outcome; finalize route returns 409.
- overlays the user's confirmed UPRNs onto the combiner CSV (buildConfirmedCombinerUri
  writes a sibling .confirmed.csv) and points the finaliser at it, so a confirmed
  UPRN drives identity + property_overrides in one pass. No corrections -> original
  URI unchanged.

tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 16:09:21 +00:00
Jun-te Kim
e960b76afc feat(bulk-upload): read flagged combiner rows + save UPRN corrections (ADR-0057)
Server module + two routes for the pre-finalise Addresses tab:
- getFlaggedAddresses: reads the combiner CSV from combinedOutputS3Uri,
  returns rows address2uprn could not confidently match (empty UPRN or a
  non-"matched" status), joined with any saved correction.
- upsertUprnCorrection: upserts a confirmed UPRN / no-UPRN per source_row_id.
- GET .../address-matches, POST .../address-corrections (thin, auth-guarded).

tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 16:06:23 +00:00
Khalim Conn-Kowlessar
6e30ca311d fix(scenarios): exclusion vocabulary = backend MeasureType exactly
Scenario 1275's modelling run crashed the engine: its exclusions carried
'room_roof_insulation', which isn't a backend MeasureType — the strict
_parse_exclusions raises for every property in the scenario. The authoring
vocabulary had seven such tokens (room_roof_insulation, boiler_upgrade,
secondary_heating, ventilation, fireplace, hot_water_tank_insulation,
cylinder_thermostat).

Scenario authoring now uses its own SCENARIO_MEASURES vocabulary — exactly
the backend MeasureType set (drifted names corrected, unimplemented
measures dropped, missing system_tune_up/_zoned + sloping_ceiling gained),
pinned by a test that fails if either side moves alone (the backend's
ADR-0056 shared-contract rule, applied app-side). room_roof_insulation
leaves DISRUPTIVE_MEASURES until the backend implements the measure;
legacy tokens on existing rows still display in constraint summaries.
The global measuresDisplayLabels vocabulary (recommendations display, old
file-based trigger) is untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:00:01 +00:00
Jun-te Kim
e0d2b9dbef feat(bulk-upload): add uprn corrections table for pre-finalise confirmation (ADR-0057)
Foundation for the "Addresses" review tab: captures the user's confirmed
UPRN per combiner row, keyed by source_row_id (no property.id exists
pre-finalise). At dispatchFinaliser these are overlaid onto the combiner
CSV so the finaliser builds identity + property_overrides from the
confirmed UPRN in one pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 15:46:58 +00:00
Khalim Conn-Kowlessar
633c127620 feat(home): per-user portfolio config planning layer + /api/me routes
TDD'd pure planning core (planFolderReorder with payload validation,
groupPortfoliosForHome with starred float + recency, planFolderDeletion
encoding unfile-then-delete, buildStarChange owning starred_at-as-flag),
applied by thin route handlers: folder create/rename/delete/reorder,
per-portfolio config upsert, and the grouped home read.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:12:07 +00:00
Khalim Conn-Kowlessar
31b7c28ad5 docs(design): home-page redesign context — Stitch refs, PRODUCT.md, impeccable trial
Design references and critique snapshot for the portfolio home redesign;
impeccable design skill added to devcontainer postCreateCommand on trial.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 15:12:07 +00:00
KhalimCK
ce6a6ffa2d
Merge pull request #363 from Hestia-Homes/feature/user-portfolio-config
feat(db): user portfolio config + folders (home-page redesign schema)
2026-07-07 14:53:41 +01:00
Khalim Conn-Kowlessar
4073671127 feat(db): add user_portfolio_config and user_portfolio_folders tables
Per-user portfolio workspace layer for the home-page redesign: starred
portfolios (starred_at, non-null = starred, orderable) and personal
folders (name + position, drag-reorderable). Composite FK
(folder_id, user_id) -> (id, user_id) makes cross-user folder
assignment impossible at the database level. Config rows are created
lazily via upsert on (user_id, portfolio_id); portfolioUsers remains
pure access control.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:23:15 +00:00
Jun-te Kim
4ab153ad59
Merge pull request #362 from Hestia-Homes/feat/property-certificate-number
Add certificate_number column to property table
2026-07-07 14:14:52 +01:00
Jun-te Kim
1ac9f0fdcf fix(portfolio): open the bulk-upload dialog from "Bulk excel import"
The BulkUploadComingSoonModal (drag/drop upload, template download, header
validation → creates the upload and routes into the map-columns onboarding
step) existed but was never wired to anything. The "Bulk excel import" menu
item routed to the uploads list instead. Point it at the dialog so the bulk
upload onboarding starts from there.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:53:26 +00:00
Jun-te Kim
114ccbba02 Add certificate_number column to property table
The address2uprn lookup already returns the EPC certificate number
alongside the UPRN, but only the UPRN is persisted today. This adds
a nullable column so a follow-up change can start saving it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 11:50:59 +00:00
Jun-te Kim
fe55a28622 fix(portfolio): truncate long match addresses; hide Needs attention tab when empty
- MatchAddress: long OS addresses overflowed the candidate row (text column
  wasn't width-constrained). Give it min-w-0 flex-1 and truncate both lines
  (full address on hover) so the dialog layout holds.
- PortfolioTabs: when nothing is unmatched, render just the properties table
  with no tab strip (the "Needs attention" tab only appears when count > 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:38:38 +00:00
Jun-te Kim
3f8920acd0 feat(portfolio): match unmatched properties to a residential UPRN
Turns the "Needs attention" tab into an actionable per-property match flow:
edit the address, confirm the postcode, search Ordnance Survey, and pick from
the RESIDENTIAL addresses in that postcode. Every candidate is a real OS
address, so picking one assigns a guaranteed valid, residential UPRN — the
property then leaves the tab and joins the main table.

- MatchAddress dialog: postcode search + residential-only candidate list
  (non-residential filtered out; already-in-portfolio shown but non-selectable,
  each row shows its UPRN). Replaces the edit-only EditAddress.
- PATCH /properties/[propertyId]: assigns uprn + address + postcode (user input
  kept as the fallback); (portfolio_id, uprn) clash → friendly 409.
- client.ts: searchAddresses() + useAssignUprn().

Reuses the existing /properties/search endpoint, which read-through caches OS
Places results per postcode in postcode_search (30-day TTL) — repeat searches
of the same postcode hit our DB, not the OS API (surfaced as a "from cache"
hint in the dialog). No backend trigger needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:33:01 +00:00
Khalim Conn-Kowlessar
06ad4eec0d Merge remote-tracking branch 'origin/main' into feature/bulk-trigger-modelling 2026-07-07 11:28:34 +00:00
KhalimCK
668588d3ce
Merge pull request #361 from Hestia-Homes/chore/tasks-inputs-column
feat(db): add inputs column to tasks
2026-07-07 12:27:40 +01:00
Jun-te Kim
aab3984b5c copy: reword unmatched-properties help text to AraAddressMatcher
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:21:44 +00:00
Jun-te Kim
7ac632a7aa feat(portfolio): move unmatched properties into a "Needs attention" tab
Reworks the separation from a card stacked above the table into a proper
second tab, and takes unmatched properties out of the main list entirely:

- PortfolioTabs: "Properties" | "Needs attention" tab shell with a count badge
  on the second tab. Both panels stay mounted (hidden with CSS) so the main
  table keeps its filter/pagination state across tab switches.
- getProperties / getPropertiesCount now exclude uprn IS NULL, so unmatched
  properties no longer appear (or count) in the main table until resolved.
  (Both functions are only used by the main table's /api/properties route.)
- UnmatchedProperties: lives in the tab; shows an "all matched" empty state
  instead of rendering nothing, and copy updated for the edit-only scope.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:17:48 +00:00
Jun-te Kim
f1cef2e643 feat(portfolio): enable the "Bulk excel import" entry point
The Add-properties menu had "Bulk excel import" disabled behind a "Soon" badge.
The bulk-upload flow exists (/portfolio/[id]/bulk-upload), so make the item
clickable and route to it, matching the "Search by postcode" item.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:05:40 +00:00
Jun-te Kim
c0087e08c3
Merge pull request #356 from Hestia-Homes/feat/landlord-overrides-nine-categories
feat(landlord-overrides): wire all 9 override categories through the app layer
2026-07-07 12:04:26 +01:00
Khalim Conn-Kowlessar
83117e90c5 feat(modelling-runs): run config on tasks.inputs; backend contract updates
- The run's config now lives on the task row's new `inputs` column
  (migration PR #361) instead of a config subtask — sub_tasks belong
  entirely to the distributor's execution work.
- Distributor path corrected to /v1/modelling/trigger-run.
- Sub-task granularity is batches, not properties: history shows progress
  as a percentage under the status chip; the Plans column always shows the
  plan count (batch counts are never comparable to property/plan numbers).
- ADR-0008 amended accordingly.

Live verification of the task write waits for PR #361 to be applied.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:46:29 +00:00