import { useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { ts } from '../../i18n/server'; import { useCollapsibleGroups } from '../../hooks/useCollapsibleGroups'; import { trackEvent } from '../../lib/analytics'; import { POI_CATEGORY_LOGOS } from '../../lib/consts'; import type { POICategoryGroup } from '../../types'; import InfoPopup from '../ui/InfoPopup'; import { SearchInput } from '../ui/SearchInput'; import { PillToggle } from '../ui/PillToggle'; import { PillGroup } from '../ui/PillGroup'; import { InfoIcon, ChevronIcon, CloseIcon } from '../ui/icons'; import { IconButton } from '../ui/IconButton'; interface POIPaneProps { groups: POICategoryGroup[]; selectedCategories: Set; onCategoriesChange: (categories: Set) => void; poiCount: number; onNavigateToSource?: (slug: string) => void; onClose?: () => void; } export default function POIPane({ groups, selectedCategories, onCategoriesChange, poiCount: _poiCount, onNavigateToSource, onClose, }: POIPaneProps) { const { t } = useTranslation(); const [searchTerm, setSearchTerm] = useState(''); const [isGroupExpanded, toggleCollapse] = useCollapsibleGroups(); const [showInfo, setShowInfo] = useState(false); const allCategories = groups.flatMap((g) => g.categories); const toggleCategory = (category: string) => { const newSet = new Set(selectedCategories); const wasSelected = newSet.has(category); if (wasSelected) { newSet.delete(category); } else { newSet.add(category); } trackEvent('POI Toggle', { category, selected: String(!wasSelected) }); onCategoriesChange(newSet); }; const selectAll = () => { trackEvent('POI Select All'); onCategoriesChange(new Set(allCategories)); }; const selectNone = () => { trackEvent('POI Select None'); onCategoriesChange(new Set()); }; const toggleGroup = useCallback( (groupName: string) => { const group = groups.find((g) => g.name === groupName); if (!group) return; const allSelected = group.categories.every((c) => selectedCategories.has(c)); const newSet = new Set(selectedCategories); if (allSelected) { group.categories.forEach((c) => newSet.delete(c)); } else { group.categories.forEach((c) => newSet.add(c)); } onCategoriesChange(newSet); }, [groups, selectedCategories, onCategoriesChange] ); const lowerSearch = searchTerm.toLowerCase(); const filteredGroups = groups .map((group) => { if (!searchTerm) return group; const matchingCats = group.categories.filter((c) => c.toLowerCase().includes(lowerSearch)); const groupMatches = group.name.toLowerCase().includes(lowerSearch); if (groupMatches) return group; if (matchingCats.length === 0) return null; return { ...group, categories: matchingCats }; }) .filter(Boolean) as POICategoryGroup[]; const selectedCount = selectedCategories.size; return (
{t('poiPane.pois')} {selectedCount}/{allCategories.length} setShowInfo(true)} title={t('poiPane.dataSourceInfo')}>
{onClose && ( )}
{showInfo && ( setShowInfo(false)} sourceLink={ onNavigateToSource ? { label: t('common.viewDataSource'), onClick: () => { onNavigateToSource('osm-pois'); setShowInfo(false); }, } : undefined } >

{t('poiPane.poiDescription')}

)}
{filteredGroups.map((group) => { const groupSelected = group.categories.filter((c) => selectedCategories.has(c)).length; const allInGroupSelected = groupSelected === group.categories.length; const someInGroupSelected = groupSelected > 0 && !allInGroupSelected; const isCollapsed = !isGroupExpanded(group.name) && !searchTerm; return (
toggleGroup(group.name)} size="xs" /> {groupSelected}/{group.categories.length}
{!isCollapsed && (
{group.categories.map((category) => { const logo = POI_CATEGORY_LOGOS[category]; return (
)}
); })}
); }