recive email + save draft and list draft + skeleton email detail

This commit is contained in:
hamid zarghami
2025-07-10 11:06:36 +03:30
parent 4258f57662
commit df6e74c632
14 changed files with 540 additions and 228 deletions
@@ -0,0 +1,67 @@
import { FC } from 'react'
const SkeletonDetail: FC = () => {
return (
<div className='bg-white rounded-4xl p-8'>
<div className='animate-pulse'>
{/* Header Skeleton */}
<div className='h-6 bg-gray-200 rounded w-48 mb-6'></div>
{/* Subject and Tags Section */}
<div className='mt-6 flex xl:flex-row flex-col justify-between xl:items-center'>
<div className='flex xl:flex-row flex-col-reverse gap-4 xl:items-center'>
<div className='flex gap-1 items-center'>
<div className='h-4 bg-gray-200 rounded w-12 hidden xl:block'></div>
<div className='h-5 bg-gray-200 rounded w-40'></div>
</div>
<div className='flex gap-2'>
<div className='h-6 xl:h-10 bg-gray-200 rounded-full w-20'></div>
<div className='h-6 xl:h-10 bg-gray-200 rounded-full w-24'></div>
</div>
</div>
<div className='h-4 bg-gray-200 rounded w-24'></div>
</div>
{/* Sender Info */}
<div className='mt-6 flex gap-4 items-center'>
<div className='size-10 bg-gray-200 rounded-full'></div>
<div className='h-4 bg-gray-200 rounded w-32'></div>
</div>
{/* Email Content */}
<div className='xl:mt-8 mt-3 space-y-3'>
<div className='h-4 bg-gray-200 rounded w-full'></div>
<div className='h-4 bg-gray-200 rounded w-full'></div>
<div className='h-4 bg-gray-200 rounded w-3/4'></div>
<div className='h-4 bg-gray-200 rounded w-full'></div>
<div className='h-4 bg-gray-200 rounded w-2/3'></div>
</div>
{/* Attachments Section */}
<div className='mt-10'>
<div className='h-4 bg-gray-200 rounded w-20 mb-4'></div>
<div className='flex border-b border-gray-200 pb-10 mt-4 flex-wrap gap-2.5 items-center'>
<div className='h-10 bg-gray-200 rounded-full w-36'></div>
</div>
</div>
{/* Action Buttons */}
<div className='flex mt-9 justify-end'>
<div className='flex gap-5'>
<div className='size-6 bg-gray-200 rounded'></div>
<div className='flex gap-2 items-center border-r border-gray-200 pr-5'>
<div className='w-[17px] h-[17px] bg-gray-200 rounded'></div>
<div className='h-4 bg-gray-200 rounded w-12'></div>
</div>
<div className='flex gap-2 items-center border-r border-gray-200 pr-5'>
<div className='w-[17px] h-[17px] bg-gray-200 rounded'></div>
<div className='h-4 bg-gray-200 rounded w-16'></div>
</div>
</div>
</div>
</div>
</div>
)
}
export default SkeletonDetail
+18 -24
View File
@@ -9,11 +9,12 @@ 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, useSaveDraft } from './hooks/useEmailData'
import { useGetMessageDetail, useSendEmail } from './hooks/useEmailData'
import { useAuthStore } from '../auth/store/AuthStore'
import { SendEmailDto } from './types/Types'
import { toast } from '@/components/Toast'
import { formatDate } from '@/config/func'
import SkeletonDetail from './Components/SkeletonDetail'
const DetailEmail: FC = () => {
@@ -27,7 +28,7 @@ const DetailEmail: FC = () => {
// API hooks
const { data: messageDetail, isLoading, error } = useGetMessageDetail(id || '', !!id)
const sendEmailMutation = useSendEmail()
const saveDraftMutation = useSaveDraft()
// const saveDraftMutation = useSaveDraft()
const priorityOptions = [
{ value: 'high', label: 'بالا' },
@@ -80,21 +81,21 @@ const DetailEmail: FC = () => {
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()
}
}
// 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)
// await saveDraftMutation.mutateAsync(draftData)
toast('پیش‌نویس ذخیره شد', 'success')
} catch {
toast('خطا در ذخیره پیش‌نویس', 'error')
@@ -108,14 +109,7 @@ const DetailEmail: FC = () => {
if (isLoading) {
return (
<div className='bg-white rounded-4xl p-8'>
<div className='animate-pulse'>
<div className='h-6 bg-gray-200 rounded mb-4'></div>
<div className='h-4 bg-gray-200 rounded mb-2'></div>
<div className='h-4 bg-gray-200 rounded mb-2'></div>
<div className='h-20 bg-gray-200 rounded'></div>
</div>
</div>
<SkeletonDetail />
)
}
@@ -247,7 +241,7 @@ const DetailEmail: FC = () => {
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}
// loading={saveDraftMutation.isPending}
/>
<Button
className='w-full sm:w-fit px-6 md:px-10 order-1 sm:order-2'
+2 -2
View File
@@ -16,7 +16,7 @@ const List: FC = () => {
const [currentPage, setCurrentPage] = useState(1);
const [filters, setFilters] = useState<FilterValues>({});
const { data: inboxData, isLoading, refetch } = useGetInbox({
const { data: inboxData, isLoading, refetch, isFetching } = useGetInbox({
page: currentPage,
limit: 10,
...filters
@@ -57,7 +57,7 @@ const List: FC = () => {
<Refresh2
size={18}
color='black'
className="cursor-pointer"
className={`cursor-pointer ${isFetching ? 'animate-spin-reverse' : ''}`}
onClick={() => refetch()}
/>
<More size={18} color='black' className='rotate-90 cursor-pointer' />
@@ -14,6 +14,14 @@ export const useSaveDraft = () => {
});
};
export const useGetDraft = (query: MessageListQueryDto) => {
return useQuery({
queryKey: ['draft', query],
queryFn: () => api.getDraft(query),
staleTime: 1000 * 60 * 5, // 5 minutes
});
};
export const useGetInbox = (query: MessageListQueryDto) => {
return useQuery({
queryKey: ['inbox', query],
+8 -4
View File
@@ -13,10 +13,14 @@ export const sendEmail = async (params: SendEmailDto) => {
};
export const saveDraft = async (params: SendEmailDto) => {
const { data } = await axios.post(`/email/draft`, {
...params,
isDraft: true,
});
const { data } = await axios.post(`/email/messages/drafts`, params);
return data;
};
export const getDraft = async (
query: MessageListQueryDto
): Promise<InboxResponse> => {
const { data } = await axios.get(`/email/messages/drafts`, { params: query });
return data;
};