mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-30 12:55:02 +00:00
ordnance survey working
This commit is contained in:
parent
f034fb2735
commit
1e5ccac346
8 changed files with 3923 additions and 47 deletions
|
|
@ -5,11 +5,6 @@ import { eq } from "drizzle-orm";
|
|||
import { lookupOsPlaces } from "@/app/api/postcode/LookupOsPlaces";
|
||||
import { mapOsPlacesToFlat } from "@/app/api/postcode/OsPlacesToFlat";
|
||||
|
||||
// Utility to normalize the postcode format
|
||||
function normalizePostcode(postcode: string) {
|
||||
return postcode.toUpperCase().replace(/\s+/g, "");
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
_req: Request,
|
||||
{ params }: { params: { postcode: string } }
|
||||
|
|
@ -22,17 +17,16 @@ export async function GET(
|
|||
);
|
||||
}
|
||||
|
||||
const normalized = normalizePostcode(postcode);
|
||||
|
||||
try {
|
||||
// Step 1: check cache
|
||||
const cached = await db
|
||||
.select()
|
||||
.from(postcodeSearch)
|
||||
.where(eq(postcodeSearch.postcode, normalized))
|
||||
.where(eq(postcodeSearch.postcode, postcode))
|
||||
.limit(1);
|
||||
|
||||
if (cached.length > 0) {
|
||||
console.log("Using cached OS Places data for postcode:", postcode);
|
||||
const record = cached[0];
|
||||
const addresses = mapOsPlacesToFlat(record.resultData?.results);
|
||||
return NextResponse.json({
|
||||
|
|
@ -44,7 +38,7 @@ export async function GET(
|
|||
}
|
||||
|
||||
// Step 2: if no cache, query OS Places API
|
||||
const result = await lookupOsPlaces(normalized);
|
||||
const result = await lookupOsPlaces(postcode);
|
||||
if (result.error || result.status !== 200 || !result.data) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
|
@ -55,14 +49,12 @@ export async function GET(
|
|||
);
|
||||
}
|
||||
|
||||
console.log("Fetched OS Places data:", result);
|
||||
|
||||
// Step 3: flatten and cache the result
|
||||
const addresses = mapOsPlacesToFlat(result.results);
|
||||
const total = result.data.header?.totalresults ?? addresses.length;
|
||||
|
||||
await db.insert(postcodeSearch).values({
|
||||
postcode: normalized,
|
||||
postcode: postcode,
|
||||
resultData: result.data,
|
||||
});
|
||||
|
||||
|
|
|
|||
7
src/app/db/migrations/0121_chunky_tony_stark.sql
Normal file
7
src/app/db/migrations/0121_chunky_tony_stark.sql
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE "postcode_search" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"postcode" text NOT NULL,
|
||||
"result_data" jsonb NOT NULL,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "postcode_search_postcode_unique" UNIQUE("postcode")
|
||||
);
|
||||
3875
src/app/db/migrations/meta/0121_snapshot.json
Normal file
3875
src/app/db/migrations/meta/0121_snapshot.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -848,6 +848,13 @@
|
|||
"when": 1761146299937,
|
||||
"tag": "0120_flashy_puck",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 121,
|
||||
"version": "7",
|
||||
"when": 1761218186670,
|
||||
"tag": "0121_chunky_tony_stark",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -24,12 +24,14 @@ export default function AddressSearch({
|
|||
onPostcodeSelect,
|
||||
postcode,
|
||||
}: {
|
||||
onAddressSelect: (address: string | null) => void;
|
||||
onAddressSelect: (address: AddressItem | null) => void; // ✅ Fix type
|
||||
onPostcodeSelect: (postcode: string) => void;
|
||||
postcode: string;
|
||||
}) {
|
||||
const [addresses, setAddresses] = useState<AddressItem[]>([]);
|
||||
const [selectedAddress, setSelectedAddress] = useState<string | null>(null);
|
||||
const [selectedAddress, setSelectedAddress] = useState<AddressItem | null>(
|
||||
null
|
||||
);
|
||||
const [showDropdown, setShowDropdown] = useState(false);
|
||||
const [triggerSearch, setTriggerSearch] = useState(false);
|
||||
const [loadingAddresses, setLoadingAddresses] = useState(false);
|
||||
|
|
@ -46,12 +48,11 @@ export default function AddressSearch({
|
|||
const validation = await refetch();
|
||||
setTriggerSearch(false);
|
||||
|
||||
// Only continue if postcode is valid
|
||||
if (!validation.data || validation.data.status !== 200) return;
|
||||
|
||||
// Fetch addresses from backend
|
||||
setLoadingAddresses(true);
|
||||
setAddressError(null);
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/postcode/${encodeURIComponent(postcode)}/addresses`
|
||||
|
|
@ -62,12 +63,16 @@ export default function AddressSearch({
|
|||
setAddressError(json.error || "Unable to retrieve addresses");
|
||||
setShowDropdown(false);
|
||||
} else if (json.results?.length) {
|
||||
setAddresses(json.results);
|
||||
const mapped = json.results.map((r: any) => ({
|
||||
address: r.address,
|
||||
uprn: r.uprn,
|
||||
}));
|
||||
setAddresses(mapped);
|
||||
setShowDropdown(true);
|
||||
} else {
|
||||
setAddressError("No addresses found for this postcode");
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch {
|
||||
setAddressError("There was an issue contacting the address service.");
|
||||
} finally {
|
||||
setLoadingAddresses(false);
|
||||
|
|
@ -75,9 +80,10 @@ export default function AddressSearch({
|
|||
}
|
||||
|
||||
function handleSelectAddress(value: string) {
|
||||
setSelectedAddress(value);
|
||||
const selected = addresses.find((a) => a.address === value) || null;
|
||||
setSelectedAddress(selected);
|
||||
setShowDropdown(false);
|
||||
onAddressSelect(value);
|
||||
onAddressSelect(selected); // ✅ This now matches the correct type
|
||||
}
|
||||
|
||||
function handleChangeAddress() {
|
||||
|
|
@ -88,7 +94,6 @@ export default function AddressSearch({
|
|||
|
||||
const showInvalid = data && data.status === 404;
|
||||
const showServerError = data && data.status === 500;
|
||||
|
||||
const isLoading = isFetching || loadingAddresses;
|
||||
|
||||
return (
|
||||
|
|
@ -115,7 +120,7 @@ export default function AddressSearch({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Validation or server errors */}
|
||||
{/* Validation and errors */}
|
||||
{showInvalid && (
|
||||
<p className="text-sm text-red-600 mb-3">
|
||||
Invalid postcode — please check and try again.
|
||||
|
|
@ -138,7 +143,7 @@ export default function AddressSearch({
|
|||
</label>
|
||||
<Select
|
||||
onValueChange={handleSelectAddress}
|
||||
value={selectedAddress || undefined}
|
||||
value={selectedAddress?.address || undefined} // ✅ fix value binding
|
||||
>
|
||||
<SelectTrigger className="w-full border-gray-300">
|
||||
<SelectValue placeholder="Choose an address" />
|
||||
|
|
@ -160,7 +165,7 @@ export default function AddressSearch({
|
|||
<h3 className="text-lg font-semibold text-brandblue mb-2">
|
||||
Selected Address
|
||||
</h3>
|
||||
<p className="text-gray-700">{selectedAddress}</p>
|
||||
<p className="text-gray-700">{selectedAddress.address}</p>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
|
|
|
|||
|
|
@ -15,14 +15,17 @@ export default function RemoteAssessmentClient({
|
|||
portfolioId: string;
|
||||
scenarios: ScenarioSelect[];
|
||||
}) {
|
||||
const [selectedAddress, setSelectedAddress] = useState<string | null>(null);
|
||||
const [selectedAddress, setSelectedAddress] = useState<{
|
||||
address: string;
|
||||
uprn: string;
|
||||
} | null>(null);
|
||||
const [selectedPostcode, setSelectedPostcode] = useState<string>("");
|
||||
|
||||
const { handleSubmit: submitAssessment, isUploading } =
|
||||
useCreateRemoteAssessment({
|
||||
portfolioId,
|
||||
uprn: 1,
|
||||
addressLineOne: selectedAddress || "",
|
||||
uprn: selectedAddress?.uprn ? parseInt(selectedAddress.uprn) : null,
|
||||
addressLineOne: selectedAddress?.address || "",
|
||||
postcode: selectedPostcode,
|
||||
valuation: null,
|
||||
propertyType: null,
|
||||
|
|
@ -31,7 +34,6 @@ export default function RemoteAssessmentClient({
|
|||
});
|
||||
|
||||
async function onSubmitRemoteAssessment(values: RemoteAssessmentFormValues) {
|
||||
console.log("🚀 Submitting remote assessment:", values);
|
||||
await submitAssessment(values);
|
||||
}
|
||||
|
||||
|
|
@ -58,20 +60,13 @@ export default function RemoteAssessmentClient({
|
|||
portfolioId={portfolioId}
|
||||
scenarios={scenarios}
|
||||
disabled={!selectedAddress}
|
||||
selectedAddress={selectedAddress}
|
||||
selectedAddress={selectedAddress?.address ?? ""}
|
||||
selectedPostcode={selectedPostcode}
|
||||
selectedUprn={Number(selectedAddress?.uprn) ?? null}
|
||||
isSubmitting={isUploading}
|
||||
onSubmitRemoteAssessment={onSubmitRemoteAssessment}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`transition-all duration-300 ${
|
||||
selectedAddress
|
||||
? "opacity-100 pointer-events-auto cursor-default"
|
||||
: "opacity-50 pointer-events-none cursor-not-allowed"
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export default function ScenarioSetup({
|
|||
disabled = false,
|
||||
selectedAddress,
|
||||
selectedPostcode,
|
||||
selectedUprn,
|
||||
isSubmitting,
|
||||
onSubmitRemoteAssessment,
|
||||
}: {
|
||||
|
|
@ -51,6 +52,7 @@ export default function ScenarioSetup({
|
|||
disabled?: boolean;
|
||||
selectedAddress: string | null;
|
||||
selectedPostcode: string;
|
||||
selectedUprn: number | null;
|
||||
isSubmitting: boolean;
|
||||
onSubmitRemoteAssessment: (values: RemoteAssessmentFormValues) => void;
|
||||
}) {
|
||||
|
|
@ -103,7 +105,7 @@ export default function ScenarioSetup({
|
|||
measures: measuresList,
|
||||
addressLineOne: selectedAddress || "",
|
||||
postcode: selectedPostcode || "",
|
||||
uprn: 1, // TODO: Replace with real UPRN
|
||||
uprn: selectedUprn || undefined,
|
||||
});
|
||||
} else {
|
||||
form.reset({
|
||||
|
|
@ -115,7 +117,7 @@ export default function ScenarioSetup({
|
|||
measures: measuresList,
|
||||
addressLineOne: selectedAddress || "",
|
||||
postcode: selectedPostcode || "",
|
||||
uprn: 1,
|
||||
uprn: selectedUprn || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,8 +43,6 @@ async function uploadCsvToS3({
|
|||
console.error(response);
|
||||
throw new Error("Failed to upload CSV to S3");
|
||||
}
|
||||
|
||||
console.log("✅ File uploaded successfully:", presignedUrl);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
|
|
@ -188,8 +186,6 @@ function useCreateRemoteAssessment({
|
|||
event_type: "remote_assessment",
|
||||
};
|
||||
|
||||
console.log("🚀 Triggering engine with body:", triggerBody);
|
||||
|
||||
const response = await fetch("/api/plan/trigger", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
|
@ -197,12 +193,9 @@ function useCreateRemoteAssessment({
|
|||
});
|
||||
|
||||
if (!response.ok) throw new Error("Failed to trigger engine");
|
||||
console.log("✅ Engine triggered successfully");
|
||||
}
|
||||
|
||||
async function handleSubmit(formData: RemoteAssessmentFormValues) {
|
||||
console.log("Submitting Remote Assessment form:", formData);
|
||||
|
||||
await Promise.all([
|
||||
presignedMutation.mutateAsync({
|
||||
userId,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue