From 9eed4ede94a18ec300550925d01722f9a3e9e3ba Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sun, 20 Jul 2025 16:47:01 +0330 Subject: [PATCH] change all actions --- src/components/newMessage/NewMessage.tsx | 131 +++++++++++++++++---- src/hooks/useEmailActions.tsx | 58 ++++----- src/langs/fa.json | 1 + src/pages/Trash/List.tsx | 8 +- src/pages/archive/List.tsx | 8 +- src/pages/draft/List.tsx | 18 ++- src/pages/draft/hooks/useDraftData.tsx | 25 ++++ src/pages/draft/service/DraftService.ts | 18 ++- src/pages/draft/types/DraftTypes.ts | 2 + src/pages/favorite/List.tsx | 12 +- src/pages/received/Detail.tsx | 6 +- src/pages/received/List.tsx | 18 +-- src/pages/received/hooks/useEmailData.tsx | 6 +- src/pages/received/service/EmailService.ts | 5 +- src/pages/received/types/Types.ts | 1 + src/pages/search/List.tsx | 12 +- src/pages/sent/List.tsx | 16 +-- src/pages/spam/List.tsx | 4 +- src/router/AppRouter.tsx | 2 +- src/services/EmailActionsService.ts | 75 ++++++++---- src/shared/store/sharedStore.ts | 4 + src/shared/types/SharedTypes.ts | 6 + 22 files changed, 309 insertions(+), 127 deletions(-) diff --git a/src/components/newMessage/NewMessage.tsx b/src/components/newMessage/NewMessage.tsx index 9fae874..8aaf773 100644 --- a/src/components/newMessage/NewMessage.tsx +++ b/src/components/newMessage/NewMessage.tsx @@ -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(''); + 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) diff --git a/src/hooks/useEmailActions.tsx b/src/hooks/useEmailActions.tsx index ea64efb..15e302b 100644 --- a/src/hooks/useEmailActions.tsx +++ b/src/hooks/useEmailActions.tsx @@ -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, diff --git a/src/langs/fa.json b/src/langs/fa.json index 669e14b..b4290c5 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -95,6 +95,7 @@ "NOTIFICATION": "اعلان", "FINANCE": "مالی" }, + "success": "عملیات با موفقیت انجام شد", "attachment_select": "اضافه کردن فایل ضمیمه", "domain": { "table": { diff --git a/src/pages/Trash/List.tsx b/src/pages/Trash/List.tsx index 0dd285f..cabf089 100644 --- a/src/pages/Trash/List.tsx +++ b/src/pages/Trash/List.tsx @@ -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: , - onClick: () => emailActions.restore(message.id), + onClick: () => emailActions.restore({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'حذف دائم', icon: , - onClick: () => emailActions.deletePermanently(message.id), + onClick: () => emailActions.deletePermanently({ messageId: message.id, mailbox: message.mailbox }), }, ]; diff --git a/src/pages/archive/List.tsx b/src/pages/archive/List.tsx index 782f16a..1688014 100644 --- a/src/pages/archive/List.tsx +++ b/src/pages/archive/List.tsx @@ -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: , - onClick: () => emailActions.unarchive(message.id), + onClick: () => emailActions.unarchive({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'حذف', icon: , - onClick: () => emailActions.moveToTrash(message.id), + onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }), }, ]; diff --git a/src/pages/draft/List.tsx b/src/pages/draft/List.tsx index 50babde..07e48b5 100644 --- a/src/pages/draft/List.tsx +++ b/src/pages/draft/List.tsx @@ -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({}); 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={
diff --git a/src/pages/draft/hooks/useDraftData.tsx b/src/pages/draft/hooks/useDraftData.tsx index 36a3eac..4972bcd 100644 --- a/src/pages/draft/hooks/useDraftData.tsx +++ b/src/pages/draft/hooks/useDraftData.tsx @@ -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'] }); + }, + }); }; \ No newline at end of file diff --git a/src/pages/draft/service/DraftService.ts b/src/pages/draft/service/DraftService.ts index 31a1612..664d376 100644 --- a/src/pages/draft/service/DraftService.ts +++ b/src/pages/draft/service/DraftService.ts @@ -29,9 +29,25 @@ export const editDraft = async ( return data; }; +export const updateDraft = async ( + messageId: string, + draftData: DraftUpdateDto +): Promise => { + const { data } = await axios.patch( + `/email/messages/drafts/${messageId}`, + draftData + ); + return data; +}; + +export const sendDraft = async (messageId: string): Promise => { + const { data } = await axios.post(`/email/messages/drafts/${messageId}/send`); + return data; +}; + export const getDraftDetail = async ( messageId: string ): Promise => { - const { data } = await axios.get(`/email/messages/drafts/${messageId}`); + const { data } = await axios.get(`/email/messages/${messageId}`); return data.data.message; }; diff --git a/src/pages/draft/types/DraftTypes.ts b/src/pages/draft/types/DraftTypes.ts index b18b5a0..1e6c572 100644 --- a/src/pages/draft/types/DraftTypes.ts +++ b/src/pages/draft/types/DraftTypes.ts @@ -104,4 +104,6 @@ export interface DraftUpdateDto { text?: string; html?: string; attachments?: EmailAttachmentDto[]; + isDraft?: boolean; + uploadOnly?: boolean; } diff --git a/src/pages/favorite/List.tsx b/src/pages/favorite/List.tsx index bbd6a15..9ab999c 100644 --- a/src/pages/favorite/List.tsx +++ b/src/pages/favorite/List.tsx @@ -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: , - onClick: () => emailActions.unfavorite(message.id), + onClick: () => emailActions.unfavorite({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'بایگانی', icon: , - onClick: () => emailActions.archive(message.id), + onClick: () => emailActions.archive({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'حذف', icon: , - onClick: () => emailActions.moveToTrash(message.id), + onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }), }, ]; diff --git a/src/pages/received/Detail.tsx b/src/pages/received/Detail.tsx index a601e18..e76f239 100644 --- a/src/pages/received/Detail.tsx +++ b/src/pages/received/Detail.tsx @@ -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(false) const [value, setValue] = useState('') const [priority, setPriority] = useState('') // 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]) diff --git a/src/pages/received/List.tsx b/src/pages/received/List.tsx index 24af334..188dc5c 100644 --- a/src/pages/received/List.tsx +++ b/src/pages/received/List.tsx @@ -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: , - onClick: () => emailActions.markAsSeen(message.id), + onClick: () => emailActions.markAsSeen({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'اضافه به علاقه‌مندی‌ها', icon: , - onClick: () => emailActions.favorite(message.id), + onClick: () => emailActions.favorite({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'بایگانی', icon: , - onClick: () => emailActions.archive(message.id), + onClick: () => emailActions.archive({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'حذف', icon: , - 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={
diff --git a/src/pages/received/hooks/useEmailData.tsx b/src/pages/received/hooks/useEmailData.tsx index 031ff24..e61e783 100644 --- a/src/pages/received/hooks/useEmailData.tsx +++ b/src/pages/received/hooks/useEmailData.tsx @@ -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 }); diff --git a/src/pages/received/service/EmailService.ts b/src/pages/received/service/EmailService.ts index 5756d23..068ae13 100644 --- a/src/pages/received/service/EmailService.ts +++ b/src/pages/received/service/EmailService.ts @@ -32,10 +32,11 @@ export const getInbox = async ( }; export const getMessageDetail = async ( - messageId: string + messageId: string, + mailbox: string ): Promise => { const { data }: { data: MessageDetailResponse } = await axios.get( - `/email/messages/${messageId}` + `/email/messages/${messageId}?mailbox=${mailbox}` ); return data.data.message; }; diff --git a/src/pages/received/types/Types.ts b/src/pages/received/types/Types.ts index 22068ce..5b686af 100644 --- a/src/pages/received/types/Types.ts +++ b/src/pages/received/types/Types.ts @@ -303,4 +303,5 @@ export enum BulkActionType { export interface BulkActionRequest { messageIds: number[]; action: BulkActionType; + mailbox: string; } diff --git a/src/pages/search/List.tsx b/src/pages/search/List.tsx index b7b4597..b3d565a 100644 --- a/src/pages/search/List.tsx +++ b/src/pages/search/List.tsx @@ -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: , - onClick: () => emailActions.favorite(message.id), + onClick: () => emailActions.favorite({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'بایگانی', icon: , - onClick: () => emailActions.archive(message.id), + onClick: () => emailActions.archive({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'حذف', icon: , - onClick: () => emailActions.moveToTrash(message.id), + onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }), }, ]; diff --git a/src/pages/sent/List.tsx b/src/pages/sent/List.tsx index e074334..52b81b5 100644 --- a/src/pages/sent/List.tsx +++ b/src/pages/sent/List.tsx @@ -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: , - onClick: () => emailActions.favorite(message.id), + onClick: () => emailActions.favorite({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'بایگانی', icon: , - onClick: () => emailActions.archive(message.id), + onClick: () => emailActions.archive({ messageId: message.id, mailbox: message.mailbox }), }, { label: 'حذف', icon: , - onClick: () => emailActions.moveToTrash(message.id), + onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }), }, ]; diff --git a/src/pages/spam/List.tsx b/src/pages/spam/List.tsx index 600085b..15636f0 100644 --- a/src/pages/spam/List.tsx +++ b/src/pages/spam/List.tsx @@ -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: , - onClick: () => emailActions.moveToTrash(message.id), + onClick: () => emailActions.moveToTrash({ messageId: message.id, mailbox: message.mailbox }), }, ]; diff --git a/src/router/AppRouter.tsx b/src/router/AppRouter.tsx index bc0c8c4..ffee078 100644 --- a/src/router/AppRouter.tsx +++ b/src/router/AppRouter.tsx @@ -25,7 +25,7 @@ const AppRouter: FC = () => { } /> } /> } /> - } /> + } /> } /> } /> diff --git a/src/services/EmailActionsService.ts b/src/services/EmailActionsService.ts index c61e527..227d5bc 100644 --- a/src/services/EmailActionsService.ts +++ b/src/services/EmailActionsService.ts @@ -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; }; diff --git a/src/shared/store/sharedStore.ts b/src/shared/store/sharedStore.ts index 566cfc4..589bbde 100644 --- a/src/shared/store/sharedStore.ts +++ b/src/shared/store/sharedStore.ts @@ -6,4 +6,8 @@ export const useSharedStore = create((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 }), })); diff --git a/src/shared/types/SharedTypes.ts b/src/shared/types/SharedTypes.ts index 012e4b5..866eaf8 100644 --- a/src/shared/types/SharedTypes.ts +++ b/src/shared/types/SharedTypes.ts @@ -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; };