import React, { useState, useMemo } from 'react'; import { Map } from '../ui/map'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../ui/card'; import { Badge } from '../ui/badge'; import { Button } from '../ui/button'; import { Checkbox } from '../ui/checkbox'; import { Label } from '../ui/label'; import { Utensils, Hotel, MapPin, Landmark, ShoppingBag, Coffee, X, Filter, type LucideIcon, } from 'lucide-react'; import { cn } from '../shared/utils'; /** * Exemplo avançado de mapa com marcadores personalizados e filtros por grupos * Demonstra como criar marcadores com cores e ícones distintos que podem ser filtrados * IMPORTANTE: Usa ícones Lucide com cores sólidas para contraste com fundos coloridos */ // Helper para criar SVG string de ícones Lucide manualmente function createLucideIconSvg(iconName: string): string { // SVG paths para os ícones Lucide usados const iconPaths: Record = { utensils: '', hotel: '', landmark: '', shopping: '', coffee: '', }; const path = iconPaths[iconName] || ''; return `${path}`; } // Definir tipos de grupos com cores e ícones Lucide export const markerGroups = { restaurant: { id: 'restaurant', name: 'Restaurantes', color: 'var(--destructive)', // red iconColor: '#FFFFFF', // white para contraste iconName: 'utensils', icon: Utensils, }, hotel: { id: 'hotel', name: 'Hotéis', color: 'var(--info)', // blue iconColor: '#FFFFFF', iconName: 'hotel', icon: Hotel, }, landmark: { id: 'landmark', name: 'Pontos Turísticos', color: 'var(--success)', // green iconColor: '#FFFFFF', iconName: 'landmark', icon: Landmark, }, shopping: { id: 'shopping', name: 'Compras', color: 'var(--warning)', // orange iconColor: '#FFFFFF', iconName: 'shopping', icon: ShoppingBag, }, cafe: { id: 'cafe', name: 'Cafeterias', color: 'var(--primary)', // purple iconColor: '#FFFFFF', iconName: 'coffee', icon: Coffee, }, } as const; export type MarkerGroupId = keyof typeof markerGroups; // Dados de exemplo com diferentes grupos const sampleLocations = [ // Restaurantes { position: { lat: -23.5505, lng: -46.6333 }, title: 'Restaurante Italiano', info: 'Melhor pizza da cidade', group: 'restaurant' as MarkerGroupId, }, { position: { lat: -23.5485, lng: -46.635 }, title: 'Sushi Bar', info: 'Comida japonesa autêntica', group: 'restaurant' as MarkerGroupId, }, { position: { lat: -23.552, lng: -46.631 }, title: 'Churrascaria Premium', info: 'Rodízio tradicional brasileiro', group: 'restaurant' as MarkerGroupId, }, // Hotéis { position: { lat: -23.5475, lng: -46.6361 }, title: 'Hotel Luxo', info: '5 estrelas - Vista panorâmica', group: 'hotel' as MarkerGroupId, }, { position: { lat: -23.553, lng: -46.634 }, title: 'Hotel Boutique', info: 'Design moderno e aconchegante', group: 'hotel' as MarkerGroupId, }, // Pontos Turísticos { position: { lat: -23.5613, lng: -46.6563 }, title: 'Parque Ibirapuera', info: 'Maior parque urbano da cidade', group: 'landmark' as MarkerGroupId, }, { position: { lat: -23.5558, lng: -46.6396 }, title: 'MASP', info: 'Museu de Arte de São Paulo', group: 'landmark' as MarkerGroupId, }, { position: { lat: -23.5489, lng: -46.6388 }, title: 'Avenida Paulista', info: 'Centro financeiro e cultural', group: 'landmark' as MarkerGroupId, }, // Shopping { position: { lat: -23.5465, lng: -46.64 }, title: 'Shopping Center', info: 'Mais de 300 lojas', group: 'shopping' as MarkerGroupId, }, { position: { lat: -23.554, lng: -46.638 }, title: 'Galeria de Arte', info: 'Arte contemporânea e design', group: 'shopping' as MarkerGroupId, }, // Cafeterias { position: { lat: -23.5495, lng: -46.6345 }, title: 'Café Artesanal', info: 'Café especial e brunch', group: 'cafe' as MarkerGroupId, }, { position: { lat: -23.551, lng: -46.637 }, title: 'Coffee House', info: 'Café gourmet e wi-fi', group: 'cafe' as MarkerGroupId, }, { position: { lat: -23.5525, lng: -46.6355 }, title: 'Café Cultural', info: 'Livros e café', group: 'cafe' as MarkerGroupId, }, ]; /** * Componente de Mapa com Filtro de Grupos * Permite filtrar marcadores por categorias com interface integrada no mapa * COMPACTO: Usa checkboxes para economia de espaço */ export interface FilterableMapExampleProps { apiKey?: string; } export function FilterableMapExample({ apiKey }: FilterableMapExampleProps) { const [activeFilters, setActiveFilters] = useState>( new Set(Object.keys(markerGroups) as MarkerGroupId[]) ); const [showFilters, setShowFilters] = useState(true); // Filtrar locais baseado nos filtros ativos const filteredLocations = useMemo(() => { return sampleLocations.filter(location => activeFilters.has(location.group)); }, [activeFilters]); // Toggle de filtro individual const toggleFilter = (groupId: MarkerGroupId) => { setActiveFilters(prev => { const newFilters = new Set(prev); if (newFilters.has(groupId)) { newFilters.delete(groupId); } else { newFilters.add(groupId); } return newFilters; }); }; // Selecionar/Desselecionar todos const toggleAll = () => { if (activeFilters.size === Object.keys(markerGroups).length) { setActiveFilters(new Set()); } else { setActiveFilters(new Set(Object.keys(markerGroups) as MarkerGroupId[])); } }; // Preparar marcadores com cores personalizadas e ícones const markersWithCustomization = filteredLocations.map(location => ({ ...location, customColor: markerGroups[location.group].color, iconSvg: createLucideIconSvg(markerGroups[location.group].iconName), iconColor: markerGroups[location.group].iconColor, })); // Contar marcadores por grupo const getGroupCount = (groupId: MarkerGroupId) => { return sampleLocations.filter(loc => loc.group === groupId).length; }; return (

Filtro por Categorias

{filteredLocations.length} de {sampleLocations.length} locais

Use os filtros compactos no canto superior esquerdo do mapa para visualizar marcadores por categoria

{/* Container do Mapa com Filtros Integrados */}
{/* Painel de Filtros COMPACTO Integrado no Mapa */}
Filtros
{/* Checkbox Todos */}
{/* Checkboxes de Filtro por Grupo */} {Object.entries(markerGroups).map(([id, group]) => { const groupId = id as MarkerGroupId; const Icon = group.icon; const isActive = activeFilters.has(groupId); const count = getGroupCount(groupId); return (
toggleFilter(groupId)} />
); })}
{/* Botão para mostrar filtros quando estão escondidos */} {!showFilters && ( )}
{/* Legenda Horizontal */}
{Object.entries(markerGroups).map(([id, group]) => { const Icon = group.icon; const count = getGroupCount(id as MarkerGroupId); return (
{group.name} {count}
); })}
{/* Estatísticas */}

Total de Locais

{sampleLocations.length}

Visíveis

{filteredLocations.length}

Categorias

{Object.keys(markerGroups).length}

Filtros Ativos

{activeFilters.size}

); }