import { useEffect, useMemo, useRef, useState } from "react"; import { Button, Center, Container, Group, Loader, Stack, Text, TextInput, useComputedColorScheme, } from "@mantine/core"; import { IconChevronDown, IconLabel, IconSearch, } from "@tabler/icons-react"; import { Link, useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { useDebouncedValue } from "@mantine/hooks"; import { useLabelPagesQuery } from "@/features/label/queries/label-query.ts"; import { useGetSpacesQuery } from "@/features/space/queries/space-query.ts"; import { getLabelColor } from "@/features/label/utils/label-colors.ts"; import { LabelPageRow } from "@/features/label/components/label-page-row.tsx"; import { LabelPageRowSkeleton } from "@/features/label/components/label-page-row-skeleton.tsx"; import { normalizeLabelName } from "@/features/label/utils/normalize-label.ts"; import { SpaceFilterMenu } from "@/features/space/components/space-filter-menu.tsx"; import { EmptyState } from "@/components/ui/empty-state"; import classes from "@/features/label/label.module.css"; import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function LabelPage() { const { t } = useTranslation(); const { labelName: rawName } = useParams<{ labelName: string }>(); const labelName = normalizeLabelName(decodeURIComponent(rawName ?? "")); const scheme = useComputedColorScheme("light"); const c = getLabelColor(labelName, scheme); const [spaceId, setSpaceId] = useState(null); const [search, setSearch] = useState(""); const [debouncedSearch] = useDebouncedValue(search.trim(), 200); const activeSpaceId = spaceId ?? undefined; const { data: spacesData } = useGetSpacesQuery({ limit: 100 }); const spaces = spacesData?.items ?? []; const { data: pagesData, isLoading: pagesLoading, hasNextPage, fetchNextPage, isFetchingNextPage, } = useLabelPagesQuery(labelName, debouncedSearch, activeSpaceId); const pages = useMemo( () => pagesData?.pages.flatMap((p) => p.items) ?? [], [pagesData], ); const sentinelRef = useRef(null); useEffect(() => { const sentinel = sentinelRef.current; if (!sentinel) return; const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) { fetchNextPage(); } }, { rootMargin: "200px 0px" }, ); observer.observe(sentinel); return () => observer.disconnect(); }, [hasNextPage, isFetchingNextPage, fetchNextPage]); const selectedSpaceName = useMemo(() => { if (!spaceId) return t("All spaces"); return spaces.find((s) => s.id === spaceId)?.name ?? t("All spaces"); }, [spaceId, spaces, t]); return ( <> {t("Labels")} {" / "} {labelName} {labelName} } value={search} onChange={(e) => setSearch(e.target.value)} size="sm" style={{ flex: 1 }} /> {pagesLoading && pages.length === 0 ? (
) : pages.length > 0 ? (
{pages.map((page) => ( ))}
{isFetchingNextPage && (
)}
) : ( )} ); }