Files
dmail-front/src/pages/search/List.tsx
T
hamid zarghami 59f9eb5d42 pagination
2025-07-27 08:54:25 +03:30

203 lines
7.9 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
import Filters, { FilterValues } from '../../components/Filters';
import { FC, useState } from 'react'
import Table from '../../components/Table';
import { ColumnType } from '../../components/types/TableTypes';
import { InfoCircle, More, Refresh2, Trash, Archive, Star } from 'iconsax-react';
import { RowActionItem } from '../../components/RowActionsDropdown';
import { useNavigate } from 'react-router-dom';
import { useSearchMessages } from './hooks/useSearchData';
import { SearchMessage } from './types/SearchTypes';
import { formatDate } from '@/config/func';
import { useEmailActions } from '@/hooks/useEmailActions';
const List: FC = () => {
const navigate = useNavigate();
const [nextCursor, setNextCursor] = useState<string | undefined>(undefined);
const [previousCursor, setPreviousCursor] = useState<string | undefined>(undefined);
const [filters, setFilters] = useState<FilterValues>({});
const emailActions = useEmailActions();
const { data: searchData, isLoading, refetch, isFetching } = useSearchMessages({
next: nextCursor,
previous: previousCursor,
limit: 10,
...filters
});
const [selectedMessages, setSelectedMessages] = useState<SearchMessage[]>([]);
const columns: ColumnType<SearchMessage>[] = [
{
key: 'subject',
title: 'عنوان پیام',
render: (item) => (
<span className="truncate">{item.subject || 'بدون موضوع'}</span>
)
},
{
key: 'from',
title: 'فرستنده',
render: (item) => (
<div className="flex items-center">
{!item.seen && <div className="w-1.5 h-1.5 md:w-2 md:h-2 rounded-full bg-blue-500 ml-2" />}
<span className="truncate">{item.from.name || item.from.address}</span>
</div>
)
},
{
key: 'mailbox',
title: 'پوشه',
render: (item) => (
<span className="text-xs bg-gray-100 px-2 py-1 rounded">{item.mailbox}</span>
)
},
{
key: 'date',
title: 'تاریخ',
align: 'center',
render: (item) => (
<span>{formatDate(item.date)}</span>
)
},
];
const tableActions = (
<div className='flex items-center gap-2 md:gap-4'>
<Refresh2
size={18}
color='black'
className={`cursor-pointer ${isFetching ? 'animate-spin-reverse' : ''}`}
onClick={() => refetch()}
/>
<More size={18} color='black' className='rotate-90 cursor-pointer' />
</div>
);
const selectedActions = (
<>
<Star size={18} color='black' onClick={() => handleFavoriteSelected()} className="cursor-pointer" />
<Archive size={18} color='black' onClick={() => handleArchiveSelected()} className="cursor-pointer" />
<Trash size={18} color='black' onClick={() => handleDeleteSelected()} className="cursor-pointer" />
</>
);
const handleFilterChange = (newFilters: FilterValues) => {
setFilters(newFilters);
setNextCursor(undefined);
setPreviousCursor(undefined);
};
const handleSelectionChange = (selectedRows: SearchMessage[]) => {
setSelectedMessages(selectedRows);
};
const handleDeleteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkMoveToTrash(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleArchiveSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkArchive(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleFavoriteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkFavorite(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const getRowActions = (message: SearchMessage): RowActionItem[] => [
{
label: 'اضافه به علاقه‌مندی‌ها',
icon: <Star size={16} color="black" />,
onClick: () => emailActions.favorite({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'بایگانی',
icon: <Archive size={16} color="black" />,
onClick: () => emailActions.archive({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'حذف',
icon: <Trash size={16} color="red" />,
onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }),
},
];
const handleNext = (cursor?: string) => {
setNextCursor(cursor);
setPreviousCursor(undefined);
};
const handlePrevious = (cursor?: string) => {
setPreviousCursor(cursor);
setNextCursor(undefined);
};
const messages = searchData?.data?.results || [];
const pager = searchData?.data?.pager;
return (
<div className='mt-2 md:mt-4 px-2 md:px-0'>
<h1 className="text-lg mb-4 md:mb-0">جستجو در پیامها</h1>
<Filters
fields={[
{ type: 'input', name: 'q', placeholder: 'جستجو در عنوان، متن یا فرستنده...' },
{ type: 'date', name: 'dateFrom', placeholder: 'از تاریخ' },
{ type: 'date', name: 'dateTo', placeholder: 'تا تاریخ' },
{
type: 'select',
name: 'mailbox',
placeholder: 'تمام پوشه‌ها',
options: [
{ value: '', label: 'تمام پوشه‌ها' },
{ value: 'INBOX', label: 'دریافتی' },
{ value: 'Sent', label: 'ارسال شده' },
{ value: 'Drafts', label: 'پیش‌نویس' },
{ value: 'Trash', label: 'سطل آشغال' },
{ value: 'Archive', label: 'بایگانی' },
{ value: 'Junk', label: 'اسپم' },
]
}
]}
onChange={handleFilterChange}
searchField="q"
/>
<Table<SearchMessage>
columns={columns}
data={messages}
isLoading={isLoading || emailActions.isLoading}
showHeader={false}
actions={tableActions}
selectedActions={selectedActions}
onSelectionChange={handleSelectionChange}
selectedRows={selectedMessages}
onRowClick={(id) => navigate(`/mail/${id}`)}
noDataMessage={
<div className="flex flex-col items-center">
<InfoCircle size={32} className="text-gray-300 mb-2" />
<div className="text-sm">
{filters.q ? 'هیچ نتیجه‌ای یافت نشد.' : 'برای جستجو کلمه مورد نظر را وارد کنید.'}
</div>
</div>
}
selectable={true}
rowActions={getRowActions}
pagination={pager ? {
hasNext: typeof pager.nextCursor === 'string',
nextCursor: typeof pager.nextCursor === 'string' ? pager.nextCursor : undefined,
previousCursor: typeof pager.previousCursor === 'string' ? pager.previousCursor : undefined,
onNext: handleNext,
onPrevious: handlePrevious
} : undefined}
/>
</div>
)
}
export default List