From eacdf246214a27fe465444b9e9272a4e593d5dee Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sat, 21 Feb 2026 21:13:32 +0000 Subject: [PATCH] Add tap-to-detail on swipe cards and fix color overlay alignment - Add onTap callback to SwipeCard using useDrag's tap detection - Wire through SwipeReviewMode to open ListingDetailSheet on tap - Fix color overlay misalignment: add relative to card container so the absolute overlay positions within the rounded card, not the full-width outer wrapper --- frontend/src/App.tsx | 1 + frontend/src/components/SwipeCard.tsx | 14 ++++++++++---- frontend/src/components/SwipeReviewMode.tsx | 3 +++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 017ab98..5731189 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -625,6 +625,7 @@ function App() { onDecide={decide} onClear={clear} onClose={() => setShowReviewMode(false)} + onSelectListing={(id) => setSelectedListingId(id)} getDecision={getDecision} /> )} diff --git a/frontend/src/components/SwipeCard.tsx b/frontend/src/components/SwipeCard.tsx index 5fe7248..74c4850 100644 --- a/frontend/src/components/SwipeCard.tsx +++ b/frontend/src/components/SwipeCard.tsx @@ -7,13 +7,14 @@ import type { PropertyFeature } from '@/types'; interface SwipeCardProps { feature: PropertyFeature; onSwipe: (direction: 'left' | 'right' | 'up') => void; + onTap?: () => void; isTop: boolean; stackIndex: number; } const SWIPE_THRESHOLD = 100; -export function SwipeCard({ feature, onSwipe, isTop, stackIndex }: SwipeCardProps) { +export function SwipeCard({ feature, onSwipe, onTap, isTop, stackIndex }: SwipeCardProps) { const hasSwiped = useRef(false); const p = feature.properties; @@ -27,9 +28,14 @@ export function SwipeCard({ feature, onSwipe, isTop, stackIndex }: SwipeCardProp })); const bind = useDrag( - ({ active, movement: [mx, my], velocity: [vx, vy], direction: [dx, dy] }) => { + ({ active, movement: [mx, my], velocity: [vx, vy], direction: [dx, dy], tap }) => { if (!isTop || hasSwiped.current) return; + if (tap) { + onTap?.(); + return; + } + if (!active) { const isSwipeRight = mx > SWIPE_THRESHOLD || (vx > 0.5 && dx > 0); const isSwipeLeft = mx < -SWIPE_THRESHOLD || (vx > 0.5 && dx < 0); @@ -81,11 +87,11 @@ export function SwipeCard({ feature, onSwipe, isTop, stackIndex }: SwipeCardProp }} className="cursor-grab active:cursor-grabbing" > -
+
{/* Color overlay */} {isTop && ( )} diff --git a/frontend/src/components/SwipeReviewMode.tsx b/frontend/src/components/SwipeReviewMode.tsx index d739f08..0339f0a 100644 --- a/frontend/src/components/SwipeReviewMode.tsx +++ b/frontend/src/components/SwipeReviewMode.tsx @@ -9,6 +9,7 @@ interface SwipeReviewModeProps { onDecide: (listingId: number, decision: DecisionType, listingType?: 'RENT' | 'BUY') => void; onClear: (listingId: number, listingType?: 'RENT' | 'BUY') => void; onClose: () => void; + onSelectListing?: (listingId: number) => void; getDecision: (listingId: number, listingType?: string) => DecisionType | undefined; } @@ -33,6 +34,7 @@ export function SwipeReviewMode({ onDecide, onClear, onClose, + onSelectListing, getDecision, }: SwipeReviewModeProps) { // Filter to only undecided features @@ -128,6 +130,7 @@ export function SwipeReviewMode({ key={feature.properties.url} feature={feature} onSwipe={handleSwipe} + onTap={() => onSelectListing?.(getListingId(feature))} isTop={i === 0} stackIndex={i} />