148 lines
4.3 KiB
TypeScript
148 lines
4.3 KiB
TypeScript
import { useState, useCallback, useMemo } from 'react';
|
|
import pb from '../lib/pocketbase';
|
|
import { trackEvent } from '../lib/analytics';
|
|
import type { Property } from '../types';
|
|
import { getNum } from '../lib/property-fields';
|
|
|
|
export interface SavedPropertyData {
|
|
propertyType?: string;
|
|
propertySubType?: string;
|
|
builtForm?: string;
|
|
duration?: string;
|
|
energyRating?: string;
|
|
price?: number;
|
|
estimatedPrice?: number;
|
|
askingPrice?: number;
|
|
askingRent?: number;
|
|
bedrooms?: number;
|
|
floorArea?: number;
|
|
listingUrl?: string;
|
|
}
|
|
|
|
export interface SavedProperty {
|
|
id: string;
|
|
address: string;
|
|
postcode: string;
|
|
data: SavedPropertyData;
|
|
created: string;
|
|
}
|
|
|
|
export function useSavedProperties(userId: string | null) {
|
|
const [properties, setProperties] = useState<SavedProperty[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const fetchProperties = useCallback(async () => {
|
|
if (!userId) return;
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
const records = await pb.collection('saved_properties').getFullList({
|
|
sort: '-created',
|
|
filter: `user = "${userId}"`,
|
|
});
|
|
setProperties(
|
|
records.map((r) => {
|
|
const raw = r as Record<string, unknown>;
|
|
let data: SavedPropertyData = {};
|
|
try {
|
|
data =
|
|
typeof raw.data === 'string'
|
|
? JSON.parse(raw.data)
|
|
: (raw.data as SavedPropertyData) || {};
|
|
} catch {
|
|
// Invalid JSON — use empty data
|
|
}
|
|
return {
|
|
id: r.id,
|
|
address: raw.address as string,
|
|
postcode: raw.postcode as string,
|
|
data,
|
|
created: r.created,
|
|
};
|
|
})
|
|
);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to load saved properties');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [userId]);
|
|
|
|
const saveProperty = useCallback(
|
|
async (property: Property) => {
|
|
if (!userId) return;
|
|
setError(null);
|
|
try {
|
|
const data: SavedPropertyData = {
|
|
propertyType: property.property_type,
|
|
propertySubType: property.property_sub_type,
|
|
builtForm: property.built_form,
|
|
duration: property.duration,
|
|
energyRating: property.current_energy_rating,
|
|
price: getNum(property, 'Last known price'),
|
|
estimatedPrice: getNum(property, 'Estimated current price'),
|
|
askingPrice: getNum(property, 'Asking price'),
|
|
askingRent: getNum(property, 'Asking rent (monthly)'),
|
|
bedrooms: getNum(property, 'Bedrooms'),
|
|
floorArea: getNum(property, 'Total floor area (sqm)'),
|
|
listingUrl: property.listing_url || undefined,
|
|
};
|
|
|
|
await pb.collection('saved_properties').create({
|
|
user: userId,
|
|
address: property.address || 'Unknown',
|
|
postcode: property.postcode || '',
|
|
data: JSON.stringify(data),
|
|
});
|
|
trackEvent('Property Save');
|
|
await fetchProperties();
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'Failed to save property';
|
|
setError(msg);
|
|
}
|
|
},
|
|
[userId, fetchProperties]
|
|
);
|
|
|
|
const deleteProperty = useCallback(async (id: string) => {
|
|
setError(null);
|
|
try {
|
|
await pb.collection('saved_properties').delete(id);
|
|
trackEvent('Property Delete');
|
|
setProperties((prev) => prev.filter((p) => p.id !== id));
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Failed to delete property');
|
|
}
|
|
}, []);
|
|
|
|
const savedPropertyKeys = useMemo(
|
|
() => new Set(properties.map((p) => `${p.address}|${p.postcode}`)),
|
|
[properties]
|
|
);
|
|
|
|
const isPropertySaved = useCallback(
|
|
(address?: string, postcode?: string) =>
|
|
savedPropertyKeys.has(`${address || ''}|${postcode || ''}`),
|
|
[savedPropertyKeys]
|
|
);
|
|
|
|
const getSavedPropertyId = useCallback(
|
|
(address?: string, postcode?: string) => {
|
|
const key = `${address || ''}|${postcode || ''}`;
|
|
return properties.find((p) => `${p.address}|${p.postcode}` === key)?.id;
|
|
},
|
|
[properties]
|
|
);
|
|
|
|
return {
|
|
properties,
|
|
loading,
|
|
error,
|
|
fetchProperties,
|
|
saveProperty,
|
|
deleteProperty,
|
|
isPropertySaved,
|
|
getSavedPropertyId,
|
|
};
|
|
}
|