import type { LabelView } from '@dorfteich/shared'; import { labelDepth } from '@dorfteich/shared'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { apiDelete, apiGet, apiPost } from '../lib/api'; import { usePondLabels } from './use-pond-labels'; /** * Page label picker (issue #44): a searchable, hierarchy-aware multi-select of * the pond's labels. Toggling a label assigns/unassigns it immediately and * refreshes both the page's labels and the sidebar (chips + filter). Shown only * to users who may edit the page; the api enforces the permission. */ export function LabelPicker({ pageId, pondId, pondSlug, onClose, }: { pageId: string; pondId: string; pondSlug: string; onClose: () => void; }): React.JSX.Element { const { t } = useTranslation('labels'); const queryClient = useQueryClient(); const { flat, isLoading } = usePondLabels(pondId); const [search, setSearch] = useState(''); const [busy, setBusy] = useState(null); const assigned = useQuery({ queryKey: ['page-labels', pageId], queryFn: () => apiGet(`/pages/${pageId}/labels`), }); const assignedIds = new Set((assigned.data ?? []).map((l) => l.id)); async function toggle(label: LabelView, checked: boolean): Promise { setBusy(label.id); try { if (checked) { await apiPost(`/pages/${pageId}/labels`, { labelId: label.id }); } else { await apiDelete(`/pages/${pageId}/labels/${label.id}`); } // Refresh the page's own labels and the sidebar list (chips + filter). await queryClient.invalidateQueries({ queryKey: ['page-labels', pageId] }); await queryClient.invalidateQueries({ queryKey: ['pages', pondId] }); } finally { setBusy(null); } } const term = search.trim().toLowerCase(); const shown = term ? flat.filter((l) => l.name.toLowerCase().includes(term)) : flat; return ( ); }