Files
dmail-front/src/pages/sent/List.tsx
T
hamid zarghami 916b88731c dark mode v1
2025-08-18 15:23:31 +03:30

281 lines
10 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.
import Filters, { FilterValues } from '../../components/Filters';
import { FC, useState } from 'react'
import Table from '../../components/Table';
import { ColumnType } from '../../components/types/TableTypes';
import { InfoCircle, Refresh2, AttachSquare, Trash, Archive, Star } from 'iconsax-react';
import { useGetSentMessages } from './hooks/useSentData';
import { InboxMessage } from './types/SentTypes';
import { useNavigate } from 'react-router-dom';
import { Paths } from '../../utils/Paths';
import { useEmailActions } from '@/hooks/useEmailActions';
import { RowActionItem } from '../../components/RowActionsDropdown';
import { Checkbox } from '@/components/ui/checkbox';
import React from 'react';
import { getIconColor } from '@/utils/colorUtils';
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: sentData, isLoading, refetch, isFetching } = useGetSentMessages({
next: nextCursor,
previous: previousCursor,
limit: 10,
...filters
});
const [selectedMessages, setSelectedMessages] = useState<InboxMessage[]>([]);
const [selectAll, setSelectAll] = useState(false);
const formatTime = (dateString: string) => {
try {
if (!dateString) return '--:--';
const date = new Date(dateString);
if (isNaN(date.getTime())) return '--:--';
return new Intl.DateTimeFormat("fa-IR", {
hour: "2-digit",
minute: "2-digit",
}).format(date);
} catch (error) {
console.error('خطا در فرمت زمان:', error);
return '--:--';
}
};
const desktopColumns: ColumnType<InboxMessage>[] = [
{
key: 'subject',
title: 'موضوع پیام',
render: (item) => (
<div className="flex items-center gap-2">
<span className="truncate">{item.subject}</span>
{item.attachments && (
<AttachSquare size={16} className="text-gray-500 flex-shrink-0" />
)}
</div>
)
},
{
key: 'intro',
title: 'پیش نمایش',
render: (item) => (
<div className="truncate max-w-[250px]">{item.intro || 'بدون موضوع'}</div>
)
},
{
key: 'to',
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.to.map(recipient => recipient.address).join(', ')}
</span>
</div>
)
},
{
key: 'date',
title: 'تاریخ ارسال',
align: 'center',
render: (item) => (
<span>{formatTime(item.date)}</span>
)
},
];
const mobileColumns: ColumnType<InboxMessage>[] = [
{
key: 'content',
title: 'محتوا',
render: (item) => (
<div className="flex flex-col gap-1 w-full">
<div className="flex items-center gap-2 w-full">
<span className="truncate font-medium text-sm flex-1 min-w-0">
{item.to.map(recipient => recipient.address).join(', ')}
</span>
<span className="text-xs text-gray-500 whitespace-nowrap">
{formatTime(item.date)}
</span>
</div>
<div className="truncate w-full text-sm font-medium">
{item.subject || 'بدون موضوع'}
</div>
<div className="truncate w-full text-xs text-gray-600">
{item.intro || 'بدون متن پیش‌نمایش'}
</div>
</div>
)
}
];
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
React.useEffect(() => {
const handleResize = () => {
setIsMobile(window.innerWidth < 768);
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
const columns = isMobile ? mobileColumns : desktopColumns;
const handleSelectAll = (checked: boolean) => {
setSelectAll(checked);
if (checked) {
const messages = sentData?.data?.sentMails || [];
setSelectedMessages(messages);
} else {
setSelectedMessages([]);
}
};
const tableActions = (
<div className='flex items-center gap-4 md:gap-4'>
<div className="flex items-center gap-2">
<Checkbox
checked={selectAll}
onCheckedChange={handleSelectAll}
className="w-4 h-4"
/>
</div>
<Refresh2
size={18}
color={getIconColor('primary')}
className={`cursor-pointer ${isFetching ? 'animate-spin-reverse' : ''}`}
onClick={() => refetch()}
/>
</div>
);
const selectedActions = (
<>
<Star size={18} color={getIconColor('primary')} onClick={() => handleFavoriteSelected()} className="cursor-pointer" />
<Archive size={18} color={getIconColor('primary')} onClick={() => handleArchiveSelected()} className="cursor-pointer" />
<Trash size={18} color={getIconColor('primary')} onClick={() => handleDeleteSelected()} className="cursor-pointer" />
</>
);
const handleFilterChange = (newFilters: FilterValues) => {
setFilters(newFilters);
setNextCursor(undefined);
setPreviousCursor(undefined);
};
const handleRowClick = (id: number, item: InboxMessage) => {
navigate(`${Paths.detailEmail}${id}/${item.mailbox}`);
};
const handleSelectionChange = (selectedRows: InboxMessage[]) => {
setSelectedMessages(selectedRows);
const messages = sentData?.data?.sentMails || [];
setSelectAll(selectedRows.length === messages.length && messages.length > 0);
};
const handleDeleteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkMoveToTrash(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
setSelectAll(false);
};
const handleArchiveSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkArchive(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
setSelectAll(false);
};
const handleFavoriteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkFavorite(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
setSelectAll(false);
};
const getRowActions = (message: InboxMessage): RowActionItem[] => [
{
label: 'اضافه به علاقه‌مندی‌ها',
icon: <Star size={16} color={getIconColor('primary')} />,
onClick: () => emailActions.favorite({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'بایگانی',
icon: <Archive size={16} color={getIconColor('primary')} />,
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 = sentData?.data?.sentMails || [];
const pager = sentData?.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: 'date', name: 'datestart', placeholder: 'از تاریخ' },
{ type: 'date', name: 'dateend', placeholder: 'تا تاریخ' },
{ type: 'input', name: 'search', placeholder: 'جستجو' }
]}
onChange={handleFilterChange}
searchField="search"
/>
<Table<InboxMessage>
columns={columns}
data={messages}
isLoading={isLoading || emailActions.isLoading}
showHeader={false}
actions={tableActions}
selectedActions={selectedActions}
onSelectionChange={handleSelectionChange}
selectedRows={selectedMessages}
onRowClick={handleRowClick}
noDataMessage={
<div className="flex flex-col items-center">
<InfoCircle size={32} className="text-gray-300 mb-2" />
<div className="text-sm">هیچ پیامی یافت نشد.</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