+ );
+}
+
+function TabButton({
+ active,
+ onClick,
+ children,
+}: {
+ active: boolean;
+ onClick: () => void;
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
diff --git a/src/app/portfolio/[slug]/components/UnmatchedProperties.tsx b/src/app/portfolio/[slug]/components/UnmatchedProperties.tsx
index 60a1dcdf..adaf4e15 100644
--- a/src/app/portfolio/[slug]/components/UnmatchedProperties.tsx
+++ b/src/app/portfolio/[slug]/components/UnmatchedProperties.tsx
@@ -1,11 +1,13 @@
-import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
+import {
+ ExclamationTriangleIcon,
+ CheckCircleIcon,
+} from "@heroicons/react/24/outline";
import type { UnmatchedProperty } from "@/lib/properties/unmatched";
import EditAddress from "./EditAddress";
-// The "needs attention" workspace that separates properties with no UPRN from
-// the matched portfolio table. Each row lets the user correct the address /
-// postcode (UPRN matching is a later step). Renders nothing when every property
-// is matched, so a healthy portfolio stays uncluttered.
+// The "Needs attention" tab: properties with no UPRN, separated out of the main
+// table until they're matched. Each row lets the user correct the address /
+// postcode (UPRN matching is a later step).
export default function UnmatchedProperties({
properties,
portfolioId,
@@ -13,14 +15,26 @@ export default function UnmatchedProperties({
properties: UnmatchedProperty[];
portfolioId: string;
}) {
- if (properties.length === 0) return null;
-
const count = properties.length;
+ if (count === 0) {
+ return (
+
+
+
+ All properties are matched
+
+
+ Every property has a UPRN — nothing needs attention.
+
+
+ );
+ }
+
return (
@@ -35,8 +49,9 @@ export default function UnmatchedProperties({
{count === 1 ? "This property" : "These properties"} couldn't be
- matched to an Ordnance Survey address automatically. Check the address
- and postcode, then run an advanced search to find the right match.
+ matched to an Ordnance Survey address automatically. Correct the
+ address and postcode so {count === 1 ? "it's" : "they're"} ready to
+ match — they stay out of the main list until resolved.
diff --git a/src/app/portfolio/[slug]/utils.ts b/src/app/portfolio/[slug]/utils.ts
index 6014cc25..0eb1b88e 100644
--- a/src/app/portfolio/[slug]/utils.ts
+++ b/src/app/portfolio/[slug]/utils.ts
@@ -739,6 +739,9 @@ export async function getPropertiesCount(
${epcJoins}
${planJoin}
WHERE p.portfolio_id = ${portfolioId}
+ -- Unmatched (no-UPRN) properties live in the "Needs attention" tab until
+ -- resolved, so they're excluded from the main table + its count.
+ AND p.uprn IS NOT NULL
${combinedWhere}
`);
@@ -831,6 +834,9 @@ export async function getProperties(
ON epc.property_id = p.id
${newApproachJoins}
WHERE p.portfolio_id = ${portfolioId}
+ -- Unmatched (no-UPRN) properties live in the "Needs attention" tab until
+ -- resolved, so they're excluded from the main table.
+ AND p.uprn IS NOT NULL
${combinedWhere}
LIMIT ${limit} OFFSET ${offset};
`);