Prechádzať zdrojové kódy

fix: ghost visual only for all markers, keep interactive for auth users

- createIcon() always uses radial gradient (same visual for all)
- isGuest controls interactivity (title/onClick/ghost flag) only
- restore PlaceCardModal for auth users
neyrogovnarik 1 mesiac pred
rodič
commit
2377646823
2 zmenil súbory, kde vykonal 38 pridanie a 20 odobranie
  1. 32 4
      frontend/src/components/MapView.tsx
  2. 6 16
      frontend/src/lib/map.ts

+ 32 - 4
frontend/src/components/MapView.tsx

@@ -13,6 +13,7 @@ export default function MapView() {
   const mapRef = useRef<MapProvider | null>(null)
   const { user, isLoading } = useAuth()
   const [places, setPlaces] = useState<Place[]>([])
+  const [selectedPlace, setSelectedPlace] = useState<Place | null>(null)
   const [mapError, setMapError] = useState(false)
 
   const fetchPlaces = useCallback(async (bounds?: MapBounds) => {
@@ -63,17 +64,19 @@ export default function MapView() {
     const provider = mapRef.current
     if (!provider || !provider.isReady()) return
 
+    const isGuest = !user
     places.forEach((place) => {
       provider.addMarker(place.id, place.lat, place.lng, {
-        type: place.type as 'place' | 'studio',
-        ghost: true,
+        title: isGuest ? undefined : place.title,
+        onClick: isGuest ? undefined : () => setSelectedPlace(place),
+        ghost: isGuest,
       })
     })
 
     return () => {
       places.forEach((place) => provider.removeMarker(place.id))
     }
-  }, [places])
+  }, [places, user])
 
   return (
     <div className="relative h-full w-full">
@@ -93,8 +96,33 @@ export default function MapView() {
           <p className="text-sm">Зарегистрируйтесь, чтобы исследовать места для фотосъёмок</p>
         </div>
       )}
+
+      {selectedPlace && (
+        <PlaceCardModal
+          place={selectedPlace}
+          onClose={() => setSelectedPlace(null)}
+        />
+      )}
     </div>
   )
 }
 
-
+function PlaceCardModal({ place, onClose }: { place: Place; onClose: () => void }) {
+  return (
+    <div className="absolute bottom-0 left-0 right-0 z-20 max-h-[60vh] overflow-y-auto rounded-t-2xl bg-[#1e293b] p-6 shadow-xl">
+      <button onClick={onClose} className="mb-4 text-white/60 hover:text-white">✕</button>
+      <h2 className="mb-2 text-xl font-bold text-white">{place.title}</h2>
+      {place.address && <p className="mb-2 text-sm text-white/60">{place.address}</p>}
+      {place.description && <p className="mb-4 text-sm text-white/80">{place.description}</p>}
+      <div className="mb-4 flex flex-wrap gap-2">
+        {place.tags?.map((tag) => (
+          <span key={tag.id} className="rounded-full bg-white/10 px-3 py-1 text-xs text-white/80">{tag.name}</span>
+        ))}
+      </div>
+      <div className="flex items-center gap-4 text-sm text-white/60">
+        <span>★ {place.rating?.toFixed(1) || 'Нет оценок'}</span>
+        {place.type === 'studio' && place.hourly_rate && <span>{place.hourly_rate} ₽/час</span>}
+      </div>
+    </div>
+  )
+}

+ 6 - 16
frontend/src/lib/map.ts

@@ -11,30 +11,23 @@ export interface MapBounds {
 }
 
 export interface MarkerOptions {
-  type?: 'place' | 'studio'
   title?: string
   onClick?: () => void
   ghost?: boolean
-  color?: string
 }
 
 const TILE_URL = 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png'
-const MARKER_COLORS: Record<string, string> = {
-  place: '#FFF82A',
-  studio: '#FF80DF',
-}
 
 const MARKER_SIZE = 7
 
-function createIcon(color: string, ghost?: boolean): L.DivIcon {
-  const bg = ghost
-    ? 'radial-gradient(circle, rgba(255, 238, 0, 1) 0%, rgba(255, 238, 0, 0) 75%)'
-    : color
+const GRADIENT = 'radial-gradient(circle, rgba(255, 238, 0, 1) 0%, rgba(255, 238, 0, 0) 75%)'
+
+function createIcon(): L.DivIcon {
   return L.divIcon({
     className: '',
     iconSize: [MARKER_SIZE, MARKER_SIZE],
     iconAnchor: [MARKER_SIZE / 2, MARKER_SIZE / 2],
-    html: `<div style="width:${MARKER_SIZE}px;height:${MARKER_SIZE}px;background:${bg};border-radius:50%"></div>`,
+    html: `<div style="width:${MARKER_SIZE}px;height:${MARKER_SIZE}px;background:${GRADIENT};border-radius:50%"></div>`,
   })
 }
 
@@ -104,11 +97,8 @@ export function createLeafletMapProvider(): MapProvider {
       const existing = markers.get(id)
       if (existing) existing.remove()
       const ghost = options?.ghost
-      const type = options?.type || 'place'
-      const color = options?.color || MARKER_COLORS[type] || '#FFF82A'
-      const icon = createIcon(color, ghost)
-      const interactive = !ghost
-      const marker = L.marker([lat, lng], { icon, interactive }).addTo(map)
+      const icon = createIcon()
+      const marker = L.marker([lat, lng], { icon, interactive: !ghost }).addTo(map)
 
       if (options?.title && !ghost) {
         marker.bindTooltip(options.title, { direction: 'top' })