322 lines
11 KiB
TypeScript
322 lines
11 KiB
TypeScript
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
|