change all actions

This commit is contained in:
hamid zarghami
2025-07-20 16:47:01 +03:30
parent 1106952e22
commit 9eed4ede94
22 changed files with 309 additions and 127 deletions
+108 -23
View File
@@ -3,8 +3,9 @@ import { CloseCircle } from 'iconsax-react'
import { useTranslation } from 'react-i18next'
import { useSharedStore } from '@/shared/store/sharedStore'
import { useAuthStore } from '../../pages/auth/store/AuthStore'
import { useSendEmail } from '../../pages/received/hooks/useEmailData'
import { useGetMessageDetail, useSendEmail } from '../../pages/received/hooks/useEmailData'
import { SendEmailDto } from '../../pages/received/types/Types'
import { useUpdateDraft, useSendDraft } from '../../pages/draft/hooks/useDraftData'
import Input from '../Input'
import ReactQuill from 'react-quill-new';
import Button from '../Button'
@@ -16,8 +17,8 @@ import { toast } from '../Toast'
const NewMessage: FC = () => {
const { t } = useTranslation('global')
const [value, setValue] = useState('');
const { setOpenNewMessage, openNewMessage } = useSharedStore()
const [value, setValue] = useState<string>('');
const { setOpenNewMessage, openNewMessage, draftData, setDraftData, editingDraftId, setEditingDraftId } = useSharedStore()
const { email: userEmail } = useAuthStore()
const [isClosing, setIsClosing] = useState(false)
const [shouldRender, setShouldRender] = useState(false)
@@ -38,6 +39,33 @@ const NewMessage: FC = () => {
// API hooks
const sendEmailMutation = useSendEmail()
const updateDraftMutation = useUpdateDraft()
const sendDraftMutation = useSendDraft()
// Get draft detail if draftData exists
const { data: draftDetail } = useGetMessageDetail(
draftData?.id.toString() || '',
draftData?.mailbox || '',
!!draftData?.id
)
// Load draft data into form when draft detail is available
useEffect(() => {
if (draftDetail) {
// Extract emails from to array
const toEmailsList = draftDetail.to?.map(recipient => recipient.address) || []
setToEmails(toEmailsList)
setSubject(draftDetail.subject || '')
// Try to load HTML content if available, fallback to text or intro
const draftWithContent = draftDetail as unknown as { html?: string; text?: string; intro: string }
const content = ensureString(draftWithContent.html || draftWithContent.text || draftWithContent.intro)
setValue(content)
// Clear draft data after loading
setDraftData(null)
}
}, [draftDetail, setDraftData])
useEffect(() => {
if (openNewMessage) {
@@ -62,6 +90,9 @@ const NewMessage: FC = () => {
setIsClosing(true)
setTimeout(() => {
setOpenNewMessage(false)
resetForm()
setDraftData(null)
setEditingDraftId(null)
}, 300)
}
@@ -76,6 +107,12 @@ const NewMessage: FC = () => {
return emailRegex.test(email)
}
const ensureString = (val: unknown): string => {
if (typeof val === 'string') return val
if (val === null || val === undefined) return ''
return String(val)
}
const addEmailToList = (email: string) => {
if (!isValidEmail(email)) {
return
@@ -156,25 +193,18 @@ const NewMessage: FC = () => {
}
const handleEmailAction = async (isDraft: boolean = false) => {
const isEditingDraft = !!editingDraftId
if (!isDraft && (toEmails.length === 0 || !subject)) {
toast('لطفا تمام فیلدهای مورد نیاز را پر کنید', 'error')
return
}
if (isDraft && toEmails.length === 0 && !subject && !value) {
if (isDraft && toEmails.length === 0 && !subject && ensureString(value).trim() === '') {
toast('حداقل یکی از فیلدها باید پر باشد', 'error')
return
}
const emailData: SendEmailDto = {
from: { address: userEmail || 'sender@example.com' },
to: parseEmailAddresses(toEmails),
subject,
html: value,
text: value.replace(/<[^>]*>/g, ''), // Strip HTML for text version
...(isDraft && { isDraft: true, uploadOnly: true }),
}
// Set appropriate loading state
if (isDraft) {
setIsSavingDraft(true)
@@ -182,18 +212,73 @@ const NewMessage: FC = () => {
setIsSending(true)
}
await sendEmailMutation.mutateAsync(emailData, {
onSuccess: (data) => {
toast(data.message, 'success')
try {
let messageId = ''
if (isEditingDraft) {
// We're editing an existing draft
const draftUpdateData = {
to: parseEmailAddresses(toEmails),
subject,
html: ensureString(value),
text: ensureString(value).replace(/<[^>]*>/g, ''),
}
if (isDraft) {
// Just update the draft
await updateDraftMutation.mutateAsync({
messageId: editingDraftId.toString(),
draftData: draftUpdateData
}, {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onSuccess: (data: any) => {
messageId = data.data?.messageId
}
})
} else {
// Update first, then send
await updateDraftMutation.mutateAsync({
messageId: editingDraftId.toString(),
draftData: {
...draftUpdateData,
isDraft: true,
uploadOnly: true
}
}, {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onSuccess: (data: any) => {
messageId = data.data?.messageId
}
})
await sendDraftMutation.mutateAsync(messageId || editingDraftId.toString())
toast('پیام با موفقیت ارسال شد', 'success')
}
handleClose()
resetForm()
},
onSettled: () => {
// Reset loading states
setIsSending(false)
setIsSavingDraft(false)
} else {
// Creating new email/draft
const emailData: SendEmailDto = {
from: { address: userEmail || 'sender@example.com' },
to: parseEmailAddresses(toEmails),
subject,
html: ensureString(value),
text: ensureString(value).replace(/<[^>]*>/g, ''),
...(isDraft && { isDraft: true, uploadOnly: true }),
}
await sendEmailMutation.mutateAsync(emailData, {
onSuccess: (data) => {
toast(data.message, 'success')
handleClose()
}
})
}
})
} catch (error) {
console.error('Error in email action:', error)
toast('خطا در انجام عملیات', 'error')
} finally {
// Reset loading states
setIsSending(false)
setIsSavingDraft(false)
}
}
const handleSend = () => handleEmailAction(false)
+29 -29
View File
@@ -50,7 +50,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const markAsSeenMutation = useMutation({
mutationFn: markMessageAsSeen,
mutationFn: ({ messageId, mailbox }: { messageId: number; mailbox: string }) => markMessageAsSeen(messageId, mailbox),
onSuccess: (data) => {
toast(data?.data?.message || 'پیام به عنوان خوانده شده علامت‌گذاری شد', 'success');
invalidateAllQueries();
@@ -58,7 +58,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const archiveMutation = useMutation({
mutationFn: moveMessageToArchive,
mutationFn: ({ messageId, mailbox }: { messageId: number; mailbox: string }) => moveMessageToArchive(messageId, mailbox),
onSuccess: (data) => {
toast(data?.data?.message || 'پیام به آرشیو منتقل شد', 'success');
invalidateAllQueries();
@@ -66,7 +66,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const favoriteMutation = useMutation({
mutationFn: moveMessageToFavorite,
mutationFn: ({ messageId, mailbox }: { messageId: number; mailbox: string }) => moveMessageToFavorite(messageId, mailbox),
onSuccess: (data) => {
toast(data?.data?.message || 'پیام به علاقه‌مندی‌ها اضافه شد', 'success');
invalidateAllQueries();
@@ -74,7 +74,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const moveToTrashMutation = useMutation({
mutationFn: moveMessageToTrash,
mutationFn: ({ messageId, mailbox }: { messageId: number; mailbox: string }) => moveMessageToTrash(messageId, mailbox),
onSuccess: (data) => {
toast(data?.data?.message || 'پیام به سطل زباله منتقل شد', 'success');
invalidateAllQueries();
@@ -82,7 +82,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const deletePermanentlyMutation = useMutation({
mutationFn: deleteMessagePermanently,
mutationFn: ({ messageId, mailbox }: { messageId: number; mailbox: string }) => deleteMessagePermanently(messageId, mailbox),
onSuccess: (data) => {
toast(data?.data?.message || 'پیام به طور کامل حذف شد', 'success');
invalidateAllQueries();
@@ -90,7 +90,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const sendEmailMutation = useMutation({
mutationFn: sendEmail,
mutationFn: ({ messageId }: { messageId: number }) => sendEmail(messageId),
onSuccess: (data) => {
toast(data?.data?.message || 'ایمیل با موفقیت ارسال شد', 'success');
invalidateAllQueries();
@@ -107,7 +107,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const deleteDraftMutation = useMutation({
mutationFn: deleteDraft,
mutationFn: ({ messageId, mailbox }: { messageId: number; mailbox: string }) => deleteDraft(messageId, mailbox),
onSuccess: (data) => {
toast(data?.data?.message || 'پیش‌نویس حذف شد', 'success');
invalidateAllQueries();
@@ -115,7 +115,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const sendDraftMutation = useMutation({
mutationFn: sendDraft,
mutationFn: ({ messageId }: { messageId: number }) => sendDraft(messageId),
onSuccess: (data) => {
toast(data?.data?.message || 'پیش‌نویس ارسال شد', 'success');
invalidateAllQueries();
@@ -123,7 +123,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const unfavoriteMutation = useMutation({
mutationFn: unfavoriteMessage,
mutationFn: ({ messageId, mailbox }: { messageId: number; mailbox: string }) => unfavoriteMessage(messageId, mailbox),
onSuccess: (data) => {
toast(data?.data?.message || 'پیام از علاقه‌مندی‌ها حذف شد', 'success');
invalidateAllQueries();
@@ -131,7 +131,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const unarchiveMutation = useMutation({
mutationFn: unarchiveMessage,
mutationFn: ({ messageId, mailbox }: { messageId: number; mailbox: string }) => unarchiveMessage(messageId, mailbox),
onSuccess: (data) => {
toast(data?.data?.message || 'پیام از آرشیو خارج شد', 'success');
invalidateAllQueries();
@@ -139,7 +139,7 @@ export const useEmailActions = (customRefetch?: () => void) => {
});
const restoreMutation = useMutation({
mutationFn: restoreMessage,
mutationFn: ({ messageId, mailbox }: { messageId: number; mailbox: string }) => restoreMessage(messageId, mailbox),
onSuccess: (data) => {
toast(data?.data?.message || 'پیام بازگردانی شد', 'success');
invalidateAllQueries();
@@ -164,24 +164,24 @@ export const useEmailActions = (customRefetch?: () => void) => {
return {
// Bulk actions
bulkAction: bulkActionMutation.mutate,
bulkMarkAsSeen: (messageIds: number[]) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.SEEN }),
bulkArchive: (messageIds: number[]) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.ARCHIVE }),
bulkFavorite: (messageIds: number[]) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.FAVORITE }),
bulkMoveToTrash: (messageIds: number[]) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.TRASH }),
bulkDelete: (messageIds: number[]) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.DELETE }),
bulkJunk: (messageIds: number[]) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.JUNK }),
bulkUnfavorite: (messageIds: number[]) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.UNFAVORITE }),
bulkUnarchive: (messageIds: number[]) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.UNARCHIVE }),
bulkRestore: (messageIds: number[]) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.RESTORE }),
bulkMarkAsSeen: (messageIds: number[], mailbox: string) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.SEEN, mailbox: mailbox }),
bulkArchive: (messageIds: number[], mailbox: string) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.ARCHIVE, mailbox: mailbox }),
bulkFavorite: (messageIds: number[], mailbox: string) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.FAVORITE, mailbox: mailbox }),
bulkMoveToTrash: (messageIds: number[], mailbox: string) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.TRASH, mailbox: mailbox }),
bulkDelete: (messageIds: number[], mailbox: string) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.DELETE, mailbox: mailbox }),
bulkJunk: (messageIds: number[], mailbox: string) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.JUNK, mailbox: mailbox }),
bulkUnfavorite: (messageIds: number[], mailbox: string) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.UNFAVORITE, mailbox: mailbox }),
bulkUnarchive: (messageIds: number[], mailbox: string) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.UNARCHIVE, mailbox: mailbox }),
bulkRestore: (messageIds: number[], mailbox: string) =>
bulkActionMutation.mutate({ messageIds, action: BulkActionType.RESTORE, mailbox: mailbox }),
// Single actions (keeping for backward compatibility)
markAsSeen: markAsSeenMutation.mutate,
+1
View File
@@ -95,6 +95,7 @@
"NOTIFICATION": "اعلان",
"FINANCE": "مالی"
},
"success": "عملیات با موفقیت انجام شد",
"attachment_select": "اضافه کردن فایل ضمیمه",
"domain": {
"table": {
+4 -4
View File
@@ -82,13 +82,13 @@ const List: FC = () => {
const handleDeleteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkDelete(messageIds);
emailActions.bulkDelete(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleRestoreSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkRestore(messageIds);
emailActions.bulkRestore(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
@@ -96,12 +96,12 @@ const List: FC = () => {
{
label: 'بازگردانی',
icon: <RecoveryConvert size={16} color="black" />,
onClick: () => emailActions.restore(message.id),
onClick: () => emailActions.restore({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'حذف دائم',
icon: <Trash size={16} color="red" />,
onClick: () => emailActions.deletePermanently(message.id),
onClick: () => emailActions.deletePermanently({ messageId: message.id, mailbox: message.mailbox }),
},
];
+4 -4
View File
@@ -82,13 +82,13 @@ const List: FC = () => {
const handleDeleteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkMoveToTrash(messageIds);
emailActions.bulkMoveToTrash(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleUnarchiveSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkUnarchive(messageIds);
emailActions.bulkUnarchive(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
@@ -96,12 +96,12 @@ const List: FC = () => {
{
label: 'خروج از آرشیو',
icon: <ArchiveSlash size={16} color="black" />,
onClick: () => emailActions.unarchive(message.id),
onClick: () => emailActions.unarchive({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'حذف',
icon: <Trash size={16} color="red" />,
onClick: () => emailActions.moveToTrash(message.id),
onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }),
},
];
+13 -5
View File
@@ -10,6 +10,7 @@ import { useGetDrafts } from './hooks/useDraftData';
import { DraftMessage } from './types/DraftTypes';
import { formatDate } from '@/config/func';
import { useEmailActions } from '@/hooks/useEmailActions';
import { useSharedStore } from '@/shared/store/sharedStore';
const List: FC = () => {
const navigate = useNavigate();
@@ -17,6 +18,7 @@ const List: FC = () => {
const [currentPage, setCurrentPage] = useState(1);
const [filters, setFilters] = useState<FilterValues>({});
const emailActions = useEmailActions();
const { setDraftData, setOpenNewMessage, setEditingDraftId } = useSharedStore();
const { data: draftData, isLoading, refetch, isFetching } = useGetDrafts({
page: currentPage,
@@ -113,14 +115,14 @@ const List: FC = () => {
const handleDeleteSelected = () => {
selectedMessages.forEach(message => {
emailActions.deleteDraft(message.id);
emailActions.deleteDraft({ messageId: message.id, mailbox: message.mailbox });
});
setSelectedMessages([]);
};
const handleSendSelected = () => {
selectedMessages.forEach(message => {
emailActions.sendDraft(message.id);
emailActions.sendDraft({ messageId: message.id });
});
setSelectedMessages([]);
};
@@ -134,17 +136,23 @@ const List: FC = () => {
};
const handleDeleteDraft = (messageId: number) => {
emailActions.deleteDraft(messageId);
emailActions.deleteDraft({ messageId: messageId, mailbox: '' });
};
const handleSendDraft = (messageId: number) => {
emailActions.sendDraft(messageId);
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: 'ارسال',
@@ -193,7 +201,7 @@ const List: FC = () => {
selectedActions={selectedActions}
onSelectionChange={handleSelectionChange}
selectedRows={selectedMessages}
onRowClick={(id) => navigate(`/draft/edit/${id}`)}
onRowClick={handleRowClick}
noDataMessage={
<div className="flex flex-col items-center ">
<InfoCircle size={32} className="text-gray-300 mb-2" />
+25
View File
@@ -42,4 +42,29 @@ export const useEditDraft = () => {
queryClient.invalidateQueries({ queryKey: ['drafts'] });
},
});
};
export const useUpdateDraft = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ messageId, draftData }: { messageId: string; draftData: DraftUpdateDto }) =>
api.updateDraft(messageId, draftData),
onSuccess: () => {
// Invalidate drafts queries to refresh the list
queryClient.invalidateQueries({ queryKey: ['drafts'] });
},
});
};
export const useSendDraft = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (messageId: string) => api.sendDraft(messageId),
onSuccess: () => {
// Invalidate drafts queries to refresh the list
queryClient.invalidateQueries({ queryKey: ['drafts'] });
},
});
};
+17 -1
View File
@@ -29,9 +29,25 @@ export const editDraft = async (
return data;
};
export const updateDraft = async (
messageId: string,
draftData: DraftUpdateDto
): Promise<void> => {
const { data } = await axios.patch(
`/email/messages/drafts/${messageId}`,
draftData
);
return data;
};
export const sendDraft = async (messageId: string): Promise<void> => {
const { data } = await axios.post(`/email/messages/drafts/${messageId}/send`);
return data;
};
export const getDraftDetail = async (
messageId: string
): Promise<InboxMessage> => {
const { data } = await axios.get(`/email/messages/drafts/${messageId}`);
const { data } = await axios.get(`/email/messages/${messageId}`);
return data.data.message;
};
+2
View File
@@ -104,4 +104,6 @@ export interface DraftUpdateDto {
text?: string;
html?: string;
attachments?: EmailAttachmentDto[];
isDraft?: boolean;
uploadOnly?: boolean;
}
+6 -6
View File
@@ -83,19 +83,19 @@ const List: FC = () => {
const handleDeleteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkMoveToTrash(messageIds);
emailActions.bulkMoveToTrash(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleArchiveSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkArchive(messageIds);
emailActions.bulkArchive(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleUnfavoriteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkUnfavorite(messageIds);
emailActions.bulkUnfavorite(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
@@ -103,17 +103,17 @@ const List: FC = () => {
{
label: 'حذف از علاقه‌مندی‌ها',
icon: <Star size={16} color="black" />,
onClick: () => emailActions.unfavorite(message.id),
onClick: () => emailActions.unfavorite({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'بایگانی',
icon: <Archive size={16} color="black" />,
onClick: () => emailActions.archive(message.id),
onClick: () => emailActions.archive({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'حذف',
icon: <Trash size={16} color="red" />,
onClick: () => emailActions.moveToTrash(message.id),
onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }),
},
];
+3 -3
View File
@@ -21,14 +21,14 @@ const DetailEmail: FC = () => {
const { t } = useTranslation()
const emailActions = useEmailActions()
const { id } = useParams<{ id: string }>()
const { id, mailbox } = useParams<{ id: string, mailbox: string }>()
const { email: userEmail } = useAuthStore()
const [showAnswer, setShowAnswer] = useState<boolean>(false)
const [value, setValue] = useState<string>('')
const [priority, setPriority] = useState<string>('')
// API hooks
const { data: messageDetail, isLoading, error } = useGetMessageDetail(id || '', !!id)
const { data: messageDetail, isLoading, error } = useGetMessageDetail(id || '', mailbox || '', !!id)
const sendEmailMutation = useSendEmail()
// const saveDraftMutation = useSaveDraft()
@@ -113,7 +113,7 @@ const DetailEmail: FC = () => {
useEffect(() => {
if (messageDetail && !messageDetail.seen && id) {
console.log('Marking message as seen:', id, 'Current seen status:', messageDetail.seen)
emailActions.markAsSeen(Number(id))
emailActions.markAsSeen({ messageId: Number(id), mailbox: mailbox || '' })
}
}, [messageDetail, emailActions, id])
+9 -9
View File
@@ -144,25 +144,25 @@ const List: FC = () => {
const handleDeleteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkMoveToTrash(messageIds);
emailActions.bulkMoveToTrash(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleArchiveSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkArchive(messageIds);
emailActions.bulkArchive(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleMarkAsReadSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkMarkAsSeen(messageIds);
emailActions.bulkMarkAsSeen(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleFavoriteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkFavorite(messageIds);
emailActions.bulkFavorite(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
@@ -170,22 +170,22 @@ const List: FC = () => {
{
label: message.seen ? 'علامت‌گذاری به عنوان خوانده نشده' : 'علامت‌گذاری به عنوان خوانده شده',
icon: <Edit size={16} color="black" />,
onClick: () => emailActions.markAsSeen(message.id),
onClick: () => emailActions.markAsSeen({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'اضافه به علاقه‌مندی‌ها',
icon: <Star1 size={16} color="black" />,
onClick: () => emailActions.favorite(message.id),
onClick: () => emailActions.favorite({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'بایگانی',
icon: <Archive size={16} color="black" />,
onClick: () => emailActions.archive(message.id),
onClick: () => emailActions.archive({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'حذف',
icon: <Trash size={16} color="red" />,
onClick: () => emailActions.moveToTrash(message.id),
onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }),
},
];
@@ -229,7 +229,7 @@ const List: FC = () => {
selectedActions={selectedActions}
onSelectionChange={handleSelectionChange}
selectedRows={selectedMessages}
onRowClick={(id) => navigate(`/mail/${id}`)}
onRowClick={(id, item) => navigate(`/mail/${id}/${item.mailbox}`)}
noDataMessage={
<div className="flex flex-col items-center">
<InfoCircle size={32} className="text-gray-300 mb-2" />
+3 -3
View File
@@ -31,10 +31,10 @@ export const useGetInbox = (query: MessageListQueryDto) => {
});
};
export const useGetMessageDetail = (messageId: string, enabled: boolean = true) => {
export const useGetMessageDetail = (messageId: string, mailbox: string, enabled: boolean = true) => {
return useQuery({
queryKey: ['message', messageId],
queryFn: () => api.getMessageDetail(messageId),
queryKey: ['message', messageId, mailbox],
queryFn: () => api.getMessageDetail(messageId, mailbox),
enabled: !!messageId && enabled,
staleTime: 0, // Always fetch fresh data for message details
});
+3 -2
View File
@@ -32,10 +32,11 @@ export const getInbox = async (
};
export const getMessageDetail = async (
messageId: string
messageId: string,
mailbox: string
): Promise<MessageDetail> => {
const { data }: { data: MessageDetailResponse } = await axios.get(
`/email/messages/${messageId}`
`/email/messages/${messageId}?mailbox=${mailbox}`
);
return data.data.message;
};
+1
View File
@@ -303,4 +303,5 @@ export enum BulkActionType {
export interface BulkActionRequest {
messageIds: number[];
action: BulkActionType;
mailbox: string;
}
+6 -6
View File
@@ -90,19 +90,19 @@ const List: FC = () => {
const handleDeleteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkMoveToTrash(messageIds);
emailActions.bulkMoveToTrash(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleArchiveSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkArchive(messageIds);
emailActions.bulkArchive(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleFavoriteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkFavorite(messageIds);
emailActions.bulkFavorite(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
@@ -110,17 +110,17 @@ const List: FC = () => {
{
label: 'اضافه به علاقه‌مندی‌ها',
icon: <Star size={16} color="black" />,
onClick: () => emailActions.favorite(message.id),
onClick: () => emailActions.favorite({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'بایگانی',
icon: <Archive size={16} color="black" />,
onClick: () => emailActions.archive(message.id),
onClick: () => emailActions.archive({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'حذف',
icon: <Trash size={16} color="red" />,
onClick: () => emailActions.moveToTrash(message.id),
onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }),
},
];
+8 -8
View File
@@ -94,8 +94,8 @@ const List: FC = () => {
setCurrentPage(1);
};
const handleRowClick = (id: number) => {
navigate(`${Paths.detailEmail}${id}`);
const handleRowClick = (id: number, item: InboxMessage) => {
navigate(`${Paths.detailEmail}${id}/${item.mailbox}`);
};
const handleSelectionChange = (selectedRows: InboxMessage[]) => {
@@ -104,19 +104,19 @@ const List: FC = () => {
const handleDeleteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkMoveToTrash(messageIds);
emailActions.bulkMoveToTrash(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleArchiveSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkArchive(messageIds);
emailActions.bulkArchive(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
const handleFavoriteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkFavorite(messageIds);
emailActions.bulkFavorite(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
@@ -124,17 +124,17 @@ const List: FC = () => {
{
label: 'اضافه به علاقه‌مندی‌ها',
icon: <Star size={16} color="black" />,
onClick: () => emailActions.favorite(message.id),
onClick: () => emailActions.favorite({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'بایگانی',
icon: <Archive size={16} color="black" />,
onClick: () => emailActions.archive(message.id),
onClick: () => emailActions.archive({ messageId: message.id, mailbox: message.mailbox }),
},
{
label: 'حذف',
icon: <Trash size={16} color="red" />,
onClick: () => emailActions.moveToTrash(message.id),
onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }),
},
];
+2 -2
View File
@@ -81,7 +81,7 @@ const List: FC = () => {
const handleDeleteSelected = () => {
const messageIds = selectedMessages.map(message => message.id);
emailActions.bulkMoveToTrash(messageIds);
emailActions.bulkMoveToTrash(messageIds, selectedMessages[0].mailbox);
setSelectedMessages([]);
};
@@ -100,7 +100,7 @@ const List: FC = () => {
{
label: 'حذف',
icon: <Trash size={16} color="red" />,
onClick: () => emailActions.moveToTrash(message.id),
onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }),
},
];
+1 -1
View File
@@ -25,7 +25,7 @@ const AppRouter: FC = () => {
<Route path={Paths.favorite} element={<FavoriteList />} />
<Route path={Paths.spam} element={<SpamList />} />
<Route path={Paths.search} element={<SearchList />} />
<Route path={Paths.detailEmail + ':id'} element={<DetailEmail />} />
<Route path={Paths.detailEmail + ':id/:mailbox'} element={<DetailEmail />} />
<Route path={Paths.login} element={<Login />} />
<Route path={Paths.setting} element={<Setting />} />
</Routes>
+54 -21
View File
@@ -3,43 +3,68 @@ import { BulkActionRequest } from "../pages/received/types/Types";
// Bulk action for multiple messages
export const bulkAction = async (request: BulkActionRequest) => {
const { data } = await axios.post(`/email/messages/bulk-action`, request);
const { data } = await axios.post(
`/email/messages/bulk-action?mailbox=${request.mailbox}`,
request
);
return data;
};
// Mark message as seen
export const markMessageAsSeen = async (messageId: number) => {
const { data } = await axios.patch(`/email/messages/${messageId}/seen`);
export const markMessageAsSeen = async (messageId: number, mailbox: string) => {
const { data } = await axios.patch(
`/email/messages/${messageId}/seen?mailbox=${mailbox}`
);
return data;
};
// Move message to archive
export const moveMessageToArchive = async (messageId: number) => {
const { data } = await axios.patch(`/email/messages/${messageId}/archive`);
export const moveMessageToArchive = async (
messageId: number,
mailbox: string
) => {
const { data } = await axios.patch(
`/email/messages/${messageId}/archive?mailbox=${mailbox}`
);
return data;
};
// Move message to favorite
export const moveMessageToFavorite = async (messageId: number) => {
const { data } = await axios.patch(`/email/messages/${messageId}/favorite`);
export const moveMessageToFavorite = async (
messageId: number,
mailbox: string
) => {
const { data } = await axios.patch(
`/email/messages/${messageId}/favorite?mailbox=${mailbox}`
);
return data;
};
// Move message to trash
export const moveMessageToTrash = async (messageId: number) => {
const { data } = await axios.patch(`/email/messages/${messageId}/trash`);
export const moveMessageToTrash = async (
messageId: number,
mailbox: string
) => {
const { data } = await axios.patch(
`/email/messages/${messageId}/trash?mailbox=${mailbox}`
);
return data;
};
// Delete message permanently (only for trash mailbox)
export const deleteMessagePermanently = async (messageId: number) => {
const { data } = await axios.delete(`/email/messages/${messageId}`);
export const deleteMessagePermanently = async (
messageId: number,
mailbox: string
) => {
const { data } = await axios.delete(
`/email/messages/${messageId}?mailbox=${mailbox}`
);
return data;
};
// Send email
export const sendEmail = async (emailData: unknown) => {
const { data } = await axios.post(`/email/send`, emailData);
export const sendEmail = async (messageId: number) => {
const { data } = await axios.post(`/email/send?messageId=${messageId}`);
return data;
};
@@ -53,8 +78,10 @@ export const updateDraft = async (messageId: number, draftData: unknown) => {
};
// Delete draft
export const deleteDraft = async (messageId: number) => {
const { data } = await axios.delete(`/email/messages/drafts/${messageId}`);
export const deleteDraft = async (messageId: number, mailbox: string) => {
const { data } = await axios.delete(
`/email/messages/drafts/${messageId}?mailbox=${mailbox}`
);
return data;
};
@@ -65,19 +92,25 @@ export const sendDraft = async (messageId: number) => {
};
// Unfavorite message
export const unfavoriteMessage = async (messageId: number) => {
const { data } = await axios.patch(`/email/messages/${messageId}/unfavorite`);
export const unfavoriteMessage = async (messageId: number, mailbox: string) => {
const { data } = await axios.patch(
`/email/messages/${messageId}/unfavorite?mailbox=${mailbox}`
);
return data;
};
// Unarchive message
export const unarchiveMessage = async (messageId: number) => {
const { data } = await axios.patch(`/email/messages/${messageId}/unarchive`);
export const unarchiveMessage = async (messageId: number, mailbox: string) => {
const { data } = await axios.patch(
`/email/messages/${messageId}/unarchive?mailbox=${mailbox}`
);
return data;
};
// Restore message from trash
export const restoreMessage = async (messageId: number) => {
const { data } = await axios.patch(`/email/messages/${messageId}/restore`);
export const restoreMessage = async (messageId: number, mailbox: string) => {
const { data } = await axios.patch(
`/email/messages/${messageId}/restore?mailbox=${mailbox}`
);
return data;
};
+4
View File
@@ -6,4 +6,8 @@ export const useSharedStore = create<SharedStoreType>((set) => ({
setOpenSidebar: (value) => set({ openSidebar: value }),
openNewMessage: false,
setOpenNewMessage: (value) => set({ openNewMessage: value }),
draftData: null,
setDraftData: (draft) => set({ draftData: draft }),
editingDraftId: null,
setEditingDraftId: (id) => set({ editingDraftId: id }),
}));
+6
View File
@@ -1,6 +1,12 @@
import { DraftMessage } from "@/pages/draft/types/DraftTypes";
export type SharedStoreType = {
openSidebar: boolean;
setOpenSidebar: (value: boolean) => void;
openNewMessage: boolean;
setOpenNewMessage: (value: boolean) => void;
draftData: DraftMessage | null;
setDraftData: (draft: DraftMessage | null) => void;
editingDraftId: number | null;
setEditingDraftId: (id: number | null) => void;
};