diff --git a/src/app/api/properties/route.ts b/src/app/api/properties/route.ts index 68c5fb7..c0fd513 100644 --- a/src/app/api/properties/route.ts +++ b/src/app/api/properties/route.ts @@ -22,4 +22,4 @@ export async function POST(req: NextRequest) { filters ); return NextResponse.json(properties); -} +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index c00cf15..c42a622 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -222,3 +222,8 @@ display: none !important; } } + +@keyframes loading { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(300%); } +} diff --git a/src/app/portfolio/[slug]/components/PropertyFilters.tsx b/src/app/portfolio/[slug]/components/PropertyFilters.tsx index f06106e..00cd41a 100644 --- a/src/app/portfolio/[slug]/components/PropertyFilters.tsx +++ b/src/app/portfolio/[slug]/components/PropertyFilters.tsx @@ -31,7 +31,6 @@ export default function PropertyFilters({ ----------------------------------------- */ useEffect(() => { if (currentEpc && expectedEpc) { - // expected must be BETTER than current if (epcIndex(expectedEpc) >= epcIndex(currentEpc)) { setExpectedEpc(""); } @@ -69,83 +68,118 @@ export default function PropertyFilters({ }); } + function handleKeyDown(e: React.KeyboardEvent) { + if (e.key === "Enter") { + e.preventDefault(); + apply(); + } + } + return ( -
- {/* Address */} - setAddress(e.target.value)} - /> - - {/* Postcode */} - setPostcode(e.target.value)} - /> - - {/* Current EPC */} - setAddress(e.target.value)} + /> +
+ + {/* Postcode */} +
+ + setPostcode(e.target.value)} + /> +
+ + {/* Current EPC */} +
+ + + + {["C", "D", "E", "F", "G"].map((epc) => ( + + ))} + +
- {/* Expected EPC */} - setExpectedEpc(e.target.value as any)} > - {epc} or above - - ))} - + + {["A", "B", "C", "D"].map((epc) => ( + + ))} + + - - - + {/* Actions */} +
+ + +
+ ); } diff --git a/src/app/portfolio/[slug]/components/PropertyTable.tsx b/src/app/portfolio/[slug]/components/PropertyTable.tsx index 885c214..1bc88c0 100644 --- a/src/app/portfolio/[slug]/components/PropertyTable.tsx +++ b/src/app/portfolio/[slug]/components/PropertyTable.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useState, useMemo } from "react"; import { useProperties } from "./useProperties"; import DataTable from "./dataTable"; import PropertyFilters, { @@ -9,8 +9,11 @@ import PropertyFilters, { import { PropertyFilter } from "@/app/utils/propertyFilters"; import { HomeIcon } from "@heroicons/react/24/outline"; import { columns } from "@/app/portfolio/[slug]/components/propertyTableColumns"; -import { useMemo } from "react"; +import clsx from "clsx"; +/* ---------------------------------------- + Filter parsing +----------------------------------------- */ export function parsePropertyFilters( filters: PropertyFilterValues ): PropertyFilter[] { @@ -47,26 +50,35 @@ export function parsePropertyFilters( value: filters.expected_epc_at_least, }); } - console.log(parsed) + return parsed; } - +/* ---------------------------------------- + Empty portfolio state +----------------------------------------- */ function EmptyPropertyState() { return ( -
-
-

- Hover over "New Property" to start adding properties to your - Portfolio - +

+
+ +

+ Hover over “New Property” to start adding properties + to your portfolio.

); } -export default function PropertyTable({ portfolioId }: { portfolioId: string }) { +/* ---------------------------------------- + Main table +----------------------------------------- */ +export default function PropertyTable({ + portfolioId, +}: { + portfolioId: string; +}) { const [filters, setFilters] = useState({ address: "", postcode: "", @@ -79,31 +91,74 @@ export default function PropertyTable({ portfolioId }: { portfolioId: string }) [filters] ); - const { data = [], isLoading, isFetching, isError } = useProperties({ + const hasActiveFilters = parsedFilters.length > 0; + + const { + data = [], + isLoading, + isFetching, + isError, + } = useProperties({ portfolioId, - filters: parsedFilters, + filters: parsedFilters, }); return (
-
+
+ + {/* Filters */} + {/* Loading bar (HubSpot-style) */} + {isFetching && ( +
+
+
+ )} + + + {/* Filter info */} + {hasActiveFilters && !isFetching && ( +
+ Filters applied ({parsedFilters.length}) +
+ )} + + {/* Content */} {isLoading ? ( -
Loading properties…
- ) : isError || data.length === 0 ? ( +
+ Loading properties… +
+ ) : isError ? ( +
+ Failed to load properties. +
+ ) : data.length === 0 && hasActiveFilters ? ( +
+

No properties match your filters.

+ +
+ ) : data.length === 0 ? ( ) : ( - <> - {isFetching && ( -
Updating…
- )} - - + )}
); -} \ No newline at end of file +} diff --git a/src/app/portfolio/[slug]/components/useProperties.ts b/src/app/portfolio/[slug]/components/useProperties.ts index 38e3ae6..3a56cd5 100644 --- a/src/app/portfolio/[slug]/components/useProperties.ts +++ b/src/app/portfolio/[slug]/components/useProperties.ts @@ -25,6 +25,12 @@ export function useProperties({ portfolioId, filters }: Params) { if (!res.ok) throw new Error("Failed to fetch properties"); return res.json(); }, + staleTime: 1000 * 60 * 5, // 5 minutes + cacheTime: 1000 * 60 * 30, // 30 minutes + + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, keepPreviousData: true, }); }