import { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react'; import { ApiErrorPanel } from '../ApiErrorPanel'; import type { Column } from '../DataTable'; import { DataTable } from '../DataTable'; import { DataTableSkeleton } from '../Skeleton'; import { useDebouncedValue } from '../../../hooks/useDebouncedValue'; import { useWpUserCollection } from '../../../hooks/useWpUserCollection'; import { columnVisibilityStorageKey, loadColumnVisibility, saveColumnVisibility } from '../../../lib/columnVisibility'; import type { WpRestUser } from '../../../types'; import { BulkActionsBar } from './BulkActionsBar'; import { ColumnVisibilityMenu } from './ColumnVisibilityMenu'; import { ListEmptyState } from './ListEmptyState'; import { ListPanel } from './ListPanel'; import { ListSearchToolbar, type SortFieldOption } from './ListSearchToolbar'; import { DEFAULT_LIST_PER_PAGE, ListPaginationBar } from './ListPaginationBar'; import { resolveColumnPickerLabel } from '../../../lib/columnPickerLabel'; type Props = { roleSlug: string; /** Increment to force refetch (e.g. after creating a user). */ refreshToken?: number; /** Shown on the toolbar row (e.g. “WordPress users with the Student role”). */ contextHint: string; searchPlaceholder: string; sortFieldOptions: SortFieldOption[]; defaultSortField: string; columns: Column[]; emptyMessage: string; skeletonHeaders?: string[]; columnPickerStorageKey?: string; emptyStateTitle?: string; emptyStateDescription?: string; emptyStateAction?: ReactNode; }; /** * Same chrome as {@link EntityListView} but for `/wp/v2/users` (no post status pills). */ export function UserEntityListView({ roleSlug, refreshToken, contextHint, searchPlaceholder, sortFieldOptions, defaultSortField, columns, emptyMessage, skeletonHeaders: skeletonHeadersProp, columnPickerStorageKey, emptyStateTitle, emptyStateDescription, emptyStateAction, }: Props) { const [search, setSearch] = useState(''); const debouncedSearch = useDebouncedValue(search, 320); const [page, setPage] = useState(1); const [orderby, setOrderby] = useState(defaultSortField); const [order, setOrder] = useState<'asc' | 'desc'>('asc'); const [bulkValue, setBulkValue] = useState(''); const noopBulkApply = useCallback(() => {}, []); useEffect(() => { setPage(1); }, [debouncedSearch, orderby, order, roleSlug]); const pickable = useMemo(() => columns.filter((c) => !c.alwaysVisible), [columns]); const [colVis, setColVis] = useState>({}); useEffect(() => { if (!columnPickerStorageKey || pickable.length === 0) { setColVis({}); return; } const key = columnVisibilityStorageKey(columnPickerStorageKey); setColVis( loadColumnVisibility( key, pickable.map((c) => ({ id: c.id, defaultHidden: c.defaultHidden })) ) ); }, [columnPickerStorageKey, pickable]); const onColumnToggle = useCallback( (id: string, next: boolean) => { if (!columnPickerStorageKey) { return; } const key = columnVisibilityStorageKey(columnPickerStorageKey); setColVis((prev) => { const merged = { ...prev, [id]: next }; saveColumnVisibility(key, merged); return merged; }); }, [columnPickerStorageKey] ); const pickerVisibility = useMemo(() => { const o: Record = {}; for (const c of pickable) { const v = colVis[c.id]; o[c.id] = v === undefined ? !c.defaultHidden : v; } return o; }, [pickable, colVis]); const visibleColumns = useMemo(() => { if (!columnPickerStorageKey) { return columns; } return columns.filter((c) => { if (c.alwaysVisible) { return true; } const v = colVis[c.id]; if (v === undefined) { return !c.defaultHidden; } return v; }); }, [columns, columnPickerStorageKey, colVis]); const skeletonHeaders = useMemo(() => { if (skeletonHeadersProp?.length) { return skeletonHeadersProp; } return columns.map((c) => resolveColumnPickerLabel(c) || '\u00a0'); }, [columns, skeletonHeadersProp]); const listQuery = useWpUserCollection({ roleSlug, search: debouncedSearch, orderby, order, page, perPage: DEFAULT_LIST_PER_PAGE, refreshToken, }); const rows = Array.isArray(listQuery.data?.data) ? listQuery.data.data : []; const totalLine = useMemo(() => { const t = listQuery.data?.total; if (t == null) { return null; } return `Showing ${rows.length} of ${t}`; }, [listQuery.data?.total, rows.length]); const onSortOrderToggle = () => setOrder((o) => (o === 'asc' ? 'desc' : 'asc')); const onSortColumn = useCallback( (key: string) => { if (key === orderby) { setOrder((o) => (o === 'asc' ? 'desc' : 'asc')); } else { setOrderby(key); setOrder('asc'); } }, [orderby] ); const columnPicker = columnPickerStorageKey && pickable.length > 0 ? ( ({ id: c.id, label: resolveColumnPickerLabel(c) }))} visibility={pickerVisibility} onChange={onColumnToggle} /> ) : null; const emptyContent = ( ); return (

{contextHint}

{totalLine ? (
{totalLine}
) : null} {listQuery.error ? (
) : listQuery.loading ? ( ) : ( <> r.id} emptyContent={emptyContent} wrapInCard={false} sortState={{ orderby, order }} onSortColumn={onSortColumn} /> )}
); }