feat: add SavedView, integrate decisions into App with review mode and client-side filtering

This commit is contained in:
Viktor Barzin 2026-02-21 13:55:05 +00:00
parent 341de89004
commit 43084ef19a
No known key found for this signature in database
GPG key ID: 0EB088298288D958
3 changed files with 124 additions and 6 deletions

View file

@ -0,0 +1,61 @@
import { useMemo } from 'react';
import { Virtuoso } from 'react-virtuoso';
import { Heart } from 'lucide-react';
import { PropertyCard } from './PropertyCard';
import type { GeoJSONFeatureCollection, PropertyFeature, DecisionType } from '@/types';
interface SavedViewProps {
listingData: GeoJSONFeatureCollection;
getDecision: (listingId: number, listingType?: string) => DecisionType | undefined;
}
function getListingId(feature: PropertyFeature): number {
const parts = feature.properties.url.split('/');
return parseInt(parts[parts.length - 1], 10);
}
export function SavedView({ listingData, getDecision }: SavedViewProps) {
const savedFeatures = useMemo(() => {
return listingData.features.filter((f) => {
const id = getListingId(f);
const type = f.properties.listing_type === 'BUY' ? 'BUY' : 'RENT';
return getDecision(id, type) === 'liked';
});
}, [listingData, getDecision]);
if (savedFeatures.length === 0) {
return (
<div className="flex-1 flex items-center justify-center">
<div className="text-center p-8">
<Heart className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
<p className="text-xl font-semibold mb-2">No saved properties yet</p>
<p className="text-muted-foreground">
Use the Review mode to swipe through properties and save ones you like.
</p>
</div>
</div>
);
}
return (
<div className="h-full flex flex-col bg-background">
<div className="px-3 py-2 text-sm text-muted-foreground border-b">
{savedFeatures.length} saved {savedFeatures.length === 1 ? 'property' : 'properties'}
</div>
<Virtuoso
className="flex-1"
data={savedFeatures}
overscan={200}
itemContent={(_index, feature) => (
<div className="px-3 pb-2 first:pt-3">
<PropertyCard
key={feature.properties.url}
property={feature.properties}
variant="compact"
/>
</div>
)}
/>
</div>
);
}

View file

@ -1,13 +1,14 @@
import { BarChart3, MapPin, PoundSterling, Maximize2, List, Map as MapIcon } from 'lucide-react';
import { BarChart3, MapPin, PoundSterling, Maximize2, List, Map as MapIcon, Heart } from 'lucide-react';
import { Button } from './ui/button';
import type { GeoJSONFeatureCollection, PropertyFeature } from '@/types';
export type ViewMode = 'map' | 'list' | 'split';
export type ViewMode = 'map' | 'list' | 'split' | 'saved';
interface StatsBarProps {
listingData: GeoJSONFeatureCollection | null;
viewMode: ViewMode;
onViewModeChange: (mode: ViewMode) => void;
likedCount?: number;
}
interface ListingStats {
@ -59,7 +60,7 @@ function formatCurrency(value: number): string {
return `£${Math.round(value)}`;
}
export function StatsBar({ listingData, viewMode, onViewModeChange }: StatsBarProps) {
export function StatsBar({ listingData, viewMode, onViewModeChange, likedCount = 0 }: StatsBarProps) {
const stats = calculateStats(listingData);
return (
@ -122,6 +123,15 @@ export function StatsBar({ listingData, viewMode, onViewModeChange }: StatsBarPr
</div>
<span className="hidden sm:inline ml-1">Split</span>
</Button>
<Button
variant={viewMode === 'saved' ? 'secondary' : 'ghost'}
size="sm"
className="h-7 px-2"
onClick={() => onViewModeChange('saved')}
>
<Heart className="h-4 w-4" />
<span className="hidden sm:inline ml-1">Saved{likedCount > 0 ? ` (${likedCount})` : ''}</span>
</Button>
</div>
</div>
);