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

322 lines
11 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 { useTranslation } from 'react-i18next'
import Table from '../../components/Table';
import { ColumnType } from '../../components/types/TableTypes';
import { InfoCircle, Refresh2, Trash, Edit, Send } from 'iconsax-react';
import { RowActionItem } from '../../components/RowActionsDropdown';
import { useNavigate } from 'react-router-dom';
import { useGetDrafts } from './hooks/useDraftData';
import { DraftMessage } from './types/DraftTypes';
import { useEmailActions } from '@/hooks/useEmailActions';
import { useSharedStore } from '@/shared/store/sharedStore';
import { Checkbox } from '@/components/ui/checkbox';
import React from 'react';
import { getIconColor } from '@/utils/colorUtils';
const List: FC = () => {
const navigate = useNavigate();
const { t } = useTranslation();
const [nextCursor, setNextCursor] = useState<string | undefined>(undefined);
const [previousCursor, setPreviousCursor] = useState<string | undefined>(undefined);
const [filters, setFilters] = useState<FilterValues>({});
const emailActions = useEmailActions();
const { setDraftData, setOpenNewMessage, setEditingDraftId } = useSharedStore();
const { data: draftData, isLoading, refetch, isFetching } = useGetDrafts({
next: nextCursor,
previous: previousCursor,
limit: 10,
...filters
});
const [selectedMessages, setSelectedMessages] = useState<DraftMessage[]>([]);
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<DraftMessage>[] = [
{
key: 'title',
title: 'title',
render: () => (
<div className="text-[#0038FF]">
پیش نویس
</div>
)
},
{
key: 'subject',
title: 'عنوان پیام',
render: (item) => (
<div className="flex items-center">
<span className="truncate">{item.subject || 'بدون موضوع'}</span>
</div>
)
},
{
key: 'to',
title: 'گیرنده',
render: (item) => (
<span className="truncate">
{item.to && item.to.length > 0
? (item.to[0].name || item.to[0].address)
: 'بدون گیرنده'
}
</span>
)
},
{
key: 'date',
title: 'تاریخ',
align: 'center',
render: (item) => (
<span>{formatTime(item.date)}</span>
)
},
];
// ستون‌های موبایل (سه خطی)
const mobileColumns: ColumnType<DraftMessage>[] = [
{
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="text-[#0038FF] text-sm font-medium">پیش نویس</span>
<span className="text-[10px] text-gray-500 whitespace-nowrap ml-auto">
{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.to && item.to.length > 0
? (item.to[0].name || item.to[0].address)
: 'بدون گیرنده'
}
</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 = draftData?.data?.draftsMail || [];
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 = (
<>
<Send
size={18}
color={getIconColor('primary')}
onClick={() => handleSendSelected()}
className="cursor-pointer"
/>
<Edit
size={18}
color={getIconColor('primary')}
onClick={() => handleEditSelected()}
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 handleSelectionChange = (selectedRows: DraftMessage[]) => {
setSelectedMessages(selectedRows);
// تنظیم وضعیت select all بر اساس انتخاب شده‌ها
const messages = draftData?.data?.draftsMail || [];
setSelectAll(selectedRows.length === messages.length && messages.length > 0);
};
const handleDeleteSelected = () => {
selectedMessages.forEach(message => {
emailActions.deleteDraft({ messageId: message.id, mailbox: message.mailbox });
});
setSelectedMessages([]);
setSelectAll(false);
};
const handleSendSelected = () => {
selectedMessages.forEach(message => {
emailActions.sendDraft({ messageId: message.id });
});
setSelectedMessages([]);
setSelectAll(false);
};
const handleEditSelected = () => {
if (selectedMessages.length === 1) {
navigate(`/draft/edit/${selectedMessages[0].id}`);
}
setSelectedMessages([]);
setSelectAll(false);
};
const handleDeleteDraft = (messageId: number) => {
emailActions.deleteDraft({ messageId: messageId, mailbox: '' });
};
const handleSendDraft = (messageId: number) => {
emailActions.sendDraft({ messageId: messageId });
};
const handleEditDraft = (messageId: number) => {
navigate(`/draft/edit/${messageId}`);
};
const handleRowClick = (messageId: number, item: DraftMessage) => {
setDraftData(item);
setEditingDraftId(messageId);
setOpenNewMessage(true);
};
const getRowActions = (message: DraftMessage): RowActionItem[] => [
{
label: 'ارسال',
icon: <Send size={16} color={getIconColor('primary')} />,
onClick: () => handleSendDraft(message.id),
},
{
label: 'ویرایش',
icon: <Edit size={16} color={getIconColor('primary')} />,
onClick: () => handleEditDraft(message.id),
},
{
label: 'حذف',
icon: <Trash size={16} color="red" />,
onClick: () => handleDeleteDraft(message.id),
},
];
const handleNext = (cursor?: string) => {
setNextCursor(cursor);
setPreviousCursor(undefined);
};
const handlePrevious = (cursor?: string) => {
setPreviousCursor(cursor);
setNextCursor(undefined);
};
const messages = draftData?.data?.draftsMail || [];
const pager = draftData?.data?.pager;
return (
<div className='mt-2 md:mt-4 px-2 md:px-0'>
<h1 className="text-lg mb-4 md:mb-0">{t('draft.title')}</h1>
<Filters
fields={[
{ type: 'date', name: 'datestart', placeholder: t('draft.from_date') },
{ type: 'date', name: 'dateend', placeholder: t('draft.to_date') },
{ type: 'input', name: 'search', placeholder: t('draft.search') }
]}
onChange={handleFilterChange}
searchField="search"
/>
<Table<DraftMessage>
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