← back to Stayclaim

src/app/map/MapWithLayers.tsx

86 lines

'use client';
import { useMemo, useState } from 'react';
import { InteractiveMapMount, type MapPin } from '@/components/InteractiveMapMount';

type FilmingPin = {
  slug: string;
  title: string;
  lat: number;
  lng: number;
  productions: { slug: string; title: string; year: number | null }[];
};

export function MapWithLayers({
  archivePins,
  filmingPins,
}: {
  archivePins: MapPin[];
  filmingPins: FilmingPin[];
}) {
  const [showArchive, setShowArchive] = useState(true);
  const [showFilming, setShowFilming] = useState(true);

  const pins: MapPin[] = useMemo(() => {
    // Dedupe by slug across layers; filming overlay wins when both layers
    // have a pin for the same address (coral overrides moss).
    const bySlug = new Map<string, MapPin>();
    const slugless: MapPin[] = [];
    if (showArchive) {
      for (const p of archivePins) {
        if (p.slug) bySlug.set(p.slug, { ...p, featured: false });
        else slugless.push({ ...p, featured: false });
      }
    }
    if (showFilming) {
      for (const f of filmingPins) {
        const titles = f.productions.slice(0, 3).map(p => `${p.title}${p.year != null ? ` (${p.year})` : ''}`).join(' · ');
        const more = f.productions.length > 3 ? ` +${f.productions.length - 3} more` : '';
        bySlug.set(f.slug, {
          slug: f.slug,
          title: f.title,
          subtitle: `Filmed: ${titles}${more}`,
          lat: f.lat,
          lng: f.lng,
          featured: true,
        });
      }
    }
    return [...bySlug.values(), ...slugless];
  }, [archivePins, filmingPins, showArchive, showFilming]);

  return (
    <div className="space-y-4">
      <div className="flex items-center gap-3 flex-wrap">
        <span className="text-[10px] uppercase tracking-[0.15em] text-ink/45">Layers</span>
        <button
          type="button"
          aria-pressed={showArchive}
          onClick={() => setShowArchive(s => !s)}
          className={`inline-flex items-center gap-2 px-3 py-1 text-[11px] uppercase tracking-[0.15em] border transition ${
            showArchive
              ? 'bg-moss text-sand border-moss'
              : 'border-ink/20 text-ink/55 hover:border-ink/45'
          }`}
        >
          <span className="inline-block w-2 h-2 rounded-full bg-moss" />
          Archive ({archivePins.length})
        </button>
        <button
          type="button"
          aria-pressed={showFilming}
          onClick={() => setShowFilming(s => !s)}
          className={`inline-flex items-center gap-2 px-3 py-1 text-[11px] uppercase tracking-[0.15em] border transition ${
            showFilming
              ? 'bg-ink text-sand border-ink'
              : 'border-ink/20 text-ink/55 hover:border-ink/45'
          }`}
        >
          <span className="inline-block w-2 h-2 rounded-full bg-coral" />
          Filming ({filmingPins.length})
        </button>
      </div>
      <InteractiveMapMount pins={pins} height={680} variant="voyager" />
    </div>
  );
}