forward and reply

This commit is contained in:
hamid zarghami
2025-07-22 12:37:35 +03:30
parent b31b64a7f9
commit 6bb82bcbac
12 changed files with 681 additions and 196 deletions
+41 -158
View File
@@ -4,112 +4,26 @@ import { useTranslation } from 'react-i18next'
import { useParams } from 'react-router-dom'
import AvatarImage from '@/assets/images/avatar.svg'
import { DocumentDownload, EmojiHappy } from 'iconsax-react'
import ForwardIcon from '@/assets/images/forward.svg'
import AnswerIcon from '@/assets/images/answer.svg'
import Header from './Components/Header'
import ReactQuill from 'react-quill-new';
import Select from '@/components/Select'
import { useGetMessageDetail, useSendEmail, useDownloadAttachment } from './hooks/useEmailData'
import { useAuthStore } from '../auth/store/AuthStore'
import { SendEmailDto } from './types/Types'
import { useGetMessageDetail, useDownloadAttachment } from './hooks/useEmailData'
import { toast } from '@/components/Toast'
import { formatDate, sanitizeEmailHTML, detectTextDirection } from '@/config/func'
import SkeletonDetail from './Components/SkeletonDetail'
import { useEmailActions } from '@/hooks/useEmailActions'
import { MailboxEnum } from './enum/Enum'
import Reply from '@/components/newMessage/Reply'
import Forward from '@/components/newMessage/Forward'
const DetailEmail: FC = () => {
const { t } = useTranslation()
const emailActions = useEmailActions()
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>('')
const [activeAction, setActiveAction] = useState<'reply' | 'forward' | null>(null)
// API hooks
const { data: messageDetail, isLoading, error } = useGetMessageDetail(id || '', mailbox || '')
const sendEmailMutation = useSendEmail()
const downloadAttachmentMutation = useDownloadAttachment()
// const saveDraftMutation = useSaveDraft()
const priorityOptions = [
{ value: 'high', label: 'بالا' },
{ value: 'medium', label: 'متوسط' },
{ value: 'low', label: 'پایین' },
]
const handleSendReply = async () => {
if (!value.trim()) {
toast('لطفا متن پیام را وارد کنید', 'error')
return
}
if (!messageDetail) {
toast('اطلاعات پیام یافت نشد', 'error')
return
}
const replyData: SendEmailDto = {
from: { address: userEmail || 'sender@example.com' },
to: [{ address: messageDetail.from.address, name: messageDetail.from.name }],
subject: `Re: ${messageDetail.subject}`,
html: value,
text: value.replace(/<[^>]*>/g, ''),
reference: {
inReplyTo: messageDetail.messageId,
originalMessageId: messageDetail.id.toString()
}
}
try {
await sendEmailMutation.mutateAsync(replyData)
toast('پاسخ با موفقیت ارسال شد', 'success')
setShowAnswer(false)
setValue('')
setPriority('')
} catch {
toast('خطا در ارسال پاسخ', 'error')
}
}
const handleSaveDraft = async () => {
if (!value.trim()) {
toast('متن پیام خالی است', 'error')
return
}
if (!messageDetail) {
toast('اطلاعات پیام یافت نشد', 'error')
return
}
// const draftData: SendEmailDto = {
// from: { address: userEmail || 'sender@example.com' },
// to: [{ address: messageDetail.from.address, name: messageDetail.from.name }],
// subject: `Re: ${messageDetail.subject}`,
// html: value,
// text: value.replace(/<[^>]*>/g, ''),
// isDraft: true,
// reference: {
// inReplyTo: messageDetail.messageId,
// originalMessageId: messageDetail.id.toString()
// }
// }
try {
// await saveDraftMutation.mutateAsync(draftData)
toast('پیش‌نویس ذخیره شد', 'success')
} catch {
toast('خطا در ذخیره پیش‌نویس', 'error')
}
}
const handleForward = () => {
// TODO: Implement forward functionality
toast('قابلیت ارسال به دیگران به زودی اضافه خواهد شد', 'info')
}
const handleDownloadAttachment = async (attachmentId: string, filename: string) => {
if (!id) {
@@ -245,78 +159,47 @@ const DetailEmail: FC = () => {
)}
</div>
{
!showAnswer ?
<div className='flex mt-9 justify-end'>
<div className='flex gap-5 text-sm'>
<EmojiHappy size={24} color='black' />
{/* Email Actions */}
{activeAction === null && (
<div className='flex mt-9 justify-end'>
<div className='flex gap-5 text-sm'>
<EmojiHappy size={24} color='black' />
<div onClick={() => setShowAnswer(true)} className='flex gap-2 cursor-pointer items-center border-r border-border pr-5'>
<img src={AnswerIcon} className='w-[17px]' />
<div>{t('mail.answer')}</div>
</div>
<Reply
originalMessage={messageDetail}
className='border-r border-border pr-5'
showInitialButton={true}
onClose={() => setActiveAction(null)}
onExpand={() => setActiveAction('reply')}
/>
<div onClick={handleForward} className='flex gap-2 cursor-pointer items-center border-r border-border pr-5'>
<img src={ForwardIcon} className='w-[17px]' />
<div>{t('mail.forward')}</div>
</div>
</div>
<Forward
originalMessage={messageDetail}
showInitialButton={true}
onClose={() => setActiveAction(null)}
onExpand={() => setActiveAction('forward')}
/>
</div>
:
<div className='mt-9 flex gap-4'>
<img src={AvatarImage} className='size-10' />
<div className='w-full'>
<div className='mb-2 text-sm'>
{t('mail.message')}
</div>
<ReactQuill
modules={{
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ list: 'ordered' }, { list: 'bullet' }],
['bold', 'italic', 'underline'],
['link', 'image'],
[{ align: [] }],
['clean']
]
}}
theme="snow"
value={value}
onChange={setValue}
style={{ minHeight: '120px', width: '100%' }}
className='text-sm'
/>
</div>
)}
<div>
<div className='flex flex-col gap-4 sm:flex-row pt-4'>
<div>
<Select
items={priorityOptions}
placeholder={t('new_message.select_priority')}
className='xl:w-[190px]'
value={priority}
onChange={(e) => setPriority(e.target.value)}
/>
</div>
<div className='flex-1 justify-end flex gap-3'>
<Button
className='!w-full sm:!w-fit px-6 md:px-10 bg-white text-black border border-primary order-2 sm:order-1'
label={t('new_message.draft')}
onClick={handleSaveDraft}
// loading={saveDraftMutation.isPending}
/>
<Button
className='w-full sm:w-fit px-6 md:px-10 order-1 sm:order-2'
label={t('new_message.send')}
onClick={handleSendReply}
loading={sendEmailMutation.isPending}
/>
</div>
</div>
</div>
</div>
</div>
}
{/* Full width Reply */}
{activeAction === 'reply' && (
<Reply
originalMessage={messageDetail}
showInitialButton={false}
onClose={() => setActiveAction(null)}
/>
)}
{/* Full width Forward */}
{activeAction === 'forward' && (
<Forward
originalMessage={messageDetail}
showInitialButton={false}
onClose={() => setActiveAction(null)}
/>
)}
</div>
)
}
+1
View File
@@ -50,6 +50,7 @@ export interface SendEmailDto {
sendTime?: string;
uploadOnly?: boolean;
envelope?: EmailEnvelopeDto;
isForward?: boolean;
}
export interface MessageListQueryDto {