admin/components/paginated-list/PaginatedListToolbar.tsx
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

110 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import type { ReactNode } from 'react'
import { Toolbar } from '@heroui/react'
import type { Selection } from '@/types/heroui'
import Button from '@/components/formElements/Button'
import AngleDownIcon from '@/components/icons/AngleDownIcon'
interface Props {
activeFilterCount: number
backBtnUrl?: string
bulkActions?: ReactNode
dynamicButtonText: string
dynamicTopSection?: ReactNode
hasDynamicButton?: boolean
hasFilters: boolean
selectedKeys: Selection
selectionMode: 'single' | 'multiple' | 'none'
onBack: (url: string) => void
onDynamicButtonClick?: () => void
onOpenFilters: () => void
onClearSelection: () => void
}
export default function PaginatedListToolbar({
activeFilterCount,
backBtnUrl,
bulkActions,
dynamicButtonText,
dynamicTopSection,
hasDynamicButton,
hasFilters,
selectedKeys,
selectionMode,
onBack,
onDynamicButtonClick,
onOpenFilters,
onClearSelection,
}: Props) {
const hasSelection = selectionMode !== 'none' && (selectedKeys === 'all' || selectedKeys.size > 0)
const hasDesktopToolbarContent = Boolean(backBtnUrl || dynamicTopSection || hasDynamicButton || hasSelection)
const hasToolbarContent = hasDesktopToolbarContent || hasFilters || hasSelection
if (!hasToolbarContent) return null
return (
<Toolbar
aria-label="ابزارهای فهرست"
className={`flex min-h-16 flex-wrap items-center justify-between gap-3 border-b border-secondary-40 bg-white px-4 py-3 lg:flex-row ${
hasDesktopToolbarContent ? '' : 'md:hidden'
}`}
>
<div className="flex w-full flex-1 items-center gap-3 md:w-auto">
{backBtnUrl ? (
<div className="hidden shrink-0 md:block">
<Button
iconOnly
aria-label="بازگشت"
className="z-10 !size-10 !rounded-2xl bg-secondary-30/30"
variant="solid"
onClick={() => {
onBack(backBtnUrl)
}}
>
<AngleDownIcon />
</Button>
</div>
) : null}
</div>
<div className="flex w-full flex-col items-stretch gap-2 sm:w-auto sm:flex-row sm:items-center">
{hasSelection ? (
<div
aria-live="polite"
className="flex min-h-10 items-center gap-2 rounded-xl border border-accent-soft bg-accent-soft px-3 text-xs font-semibold text-accent-soft-foreground"
role="status"
>
<span>
{selectedKeys === 'all' ? 'همه سطرها انتخاب شده‌اند' : `${selectedKeys.size.toLocaleString('fa-IR')} سطر انتخاب شده`}
</span>
{bulkActions}
<Button
color="default"
size="sm"
variant="light"
onClick={onClearSelection}
>
لغو انتخاب
</Button>
</div>
) : null}
{hasFilters ? (
<Button
className="md:hidden"
color="default"
size="sm"
variant="flat"
onClick={onOpenFilters}
>
فیلترها{activeFilterCount > 0 ? ` (${activeFilterCount.toLocaleString('fa-IR')})` : ''}
</Button>
) : null}
{dynamicTopSection}
{hasDynamicButton && <Button onClick={onDynamicButtonClick}>{dynamicButtonText}</Button>}
</div>
</Toolbar>
)
}