Compare commits

..

13 Commits

Author SHA1 Message Date
danak b82d6b7d0a Update Dockerfile
deploy to danak / build_and_deploy (push) Has been cancelled
2026-06-11 08:10:11 +00:00
hamid zarghami fd25fe7775 fix build
deploy to danak / build_and_deploy (push) Has been cancelled
2026-05-31 16:56:15 +03:30
hamid zarghami 7a5075ec3a dmail api base url
deploy to danak / build_and_deploy (push) Has been cancelled
2026-05-31 16:30:24 +03:30
hamid zarghami 89d237d75b dockerfile
deploy to danak / build_and_deploy (push) Has been cancelled
2026-05-31 16:22:05 +03:30
hamid zarghami 7dbd2def27 Check folder name in git 2025-11-30 12:46:43 +03:30
hamid zarghami a0ea1e1fb5 add attachment in reply 2025-11-29 16:38:32 +03:30
hamid zarghami dbe28c3b5f fix: build 2025-10-29 11:52:59 +03:30
hamid zarghami 2ccc096277 fix cc and bcc 2025-10-29 11:48:31 +03:30
hamid zarghami 4a20073ad6 cc in detail 2025-10-27 15:00:59 +03:30
hamid zarghami 0be4a02dea fix space inputs 2025-10-25 16:00:13 +03:30
hamid zarghami c76d9c788b fix build 2025-10-23 11:13:53 +03:30
hamid zarghami 58f054ccfa cc and bcc 2025-10-23 11:05:03 +03:30
hamid zarghami a081c364fe new favicon 2025-08-25 16:38:10 +03:30
28 changed files with 14037 additions and 8645 deletions
+2 -2
View File
@@ -4,10 +4,10 @@ VITE_REFRESH_TOKEN_NAME = 'dmail_refresh_token'
VITE_DANAK_BASE_URL ='https://api.danakcorp.com'
# VITE_DANAK_BASE_URL ='http://192.168.1.112:4001'
VITE_SOCKET_URL = 'wss://dmail-api.danakcorp.com/email'
VITE_SOCKET_URL = 'wss://dmail-api.dev.danakcorp.com/email'
# VITE_SOCKET_URL = 'ws://192.168.1.106:4000/email'
VITE_BASE_URL = 'https://dmail-api.danakcorp.com'
VITE_BASE_URL = 'https://dmail-api.dev.danakcorp.com'
# VITE_BASE_URL = 'http://192.168.1.117:4000'
VITE_HELP_URL = 'https://help.danakcorp.com'
+42 -7
View File
@@ -1,21 +1,56 @@
FROM node:22-alpine AS builder
# Change Alpine repo
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.de.velop.ir/alpine|g' /etc/apk/repositories
# Configure npm registry mirror (Liara)
# ENV NPM_CONFIG_REGISTRY=https://package-mirror.liara.ir/repository/npm/
# RUN npm config set registry https://package-mirror.liara.ir/repository/npm/
# Install tzdata to support timezone settings
RUN apk add --no-cache tzdata
RUN npm install -g corepack@latest
RUN corepack enable && corepack prepare pnpm@10 --activate
# RUN apk add --no-cache tzdata
# Set the timezone to Asia/Tehran
RUN cp /usr/share/zoneinfo/Asia/Tehran /etc/localtime && echo "Asia/Tehran" > /etc/timezone
# RUN cp /usr/share/zoneinfo/Asia/Tehran /etc/localtime && echo "Asia/Tehran" > /etc/timezone
WORKDIR /build
COPY package*.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile --loglevel info
# Optional build-args (CapRover / docker build --build-arg). When unset, Vite reads .env from COPY.
ARG VITE_TOKEN_NAME
ARG VITE_REFRESH_TOKEN_NAME
ARG VITE_DANAK_BASE_URL
ARG VITE_BASE_URL
ARG VITE_SERVICE_ID
ARG VITE_LOGIN_URL
ARG VITE_CONSOLE_URL
ARG VITE_WORKSPACE_ID
ARG VITE_HELP_URL
ARG VITE_SOCKET_URL
ARG VITE_MAILBOXES
COPY package*.json package-lock.json* ./
RUN npm ci --loglevel info || npm install --loglevel info
COPY . ./
RUN pnpm run build
RUN set -e; \
env_args=""; \
[ -n "$VITE_TOKEN_NAME" ] && env_args="$env_args VITE_TOKEN_NAME=$VITE_TOKEN_NAME"; \
[ -n "$VITE_REFRESH_TOKEN_NAME" ] && env_args="$env_args VITE_REFRESH_TOKEN_NAME=$VITE_REFRESH_TOKEN_NAME"; \
[ -n "$VITE_DANAK_BASE_URL" ] && env_args="$env_args VITE_DANAK_BASE_URL=$VITE_DANAK_BASE_URL"; \
[ -n "$VITE_BASE_URL" ] && env_args="$env_args VITE_BASE_URL=$VITE_BASE_URL"; \
[ -n "$VITE_SERVICE_ID" ] && env_args="$env_args VITE_SERVICE_ID=$VITE_SERVICE_ID"; \
[ -n "$VITE_LOGIN_URL" ] && env_args="$env_args VITE_LOGIN_URL=$VITE_LOGIN_URL"; \
[ -n "$VITE_CONSOLE_URL" ] && env_args="$env_args VITE_CONSOLE_URL=$VITE_CONSOLE_URL"; \
[ -n "$VITE_WORKSPACE_ID" ] && env_args="$env_args VITE_WORKSPACE_ID=$VITE_WORKSPACE_ID"; \
[ -n "$VITE_HELP_URL" ] && env_args="$env_args VITE_HELP_URL=$VITE_HELP_URL"; \
[ -n "$VITE_SOCKET_URL" ] && env_args="$env_args VITE_SOCKET_URL=$VITE_SOCKET_URL"; \
[ -n "$VITE_MAILBOXES" ] && env_args="$env_args VITE_MAILBOXES=$VITE_MAILBOXES"; \
eval "env$env_args npm run build"
FROM nginx:stable-alpine AS production-stage
# Change Alpine repo
RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirror.de.velop.ir/alpine|g' /etc/apk/repositories
COPY --from=builder /build/dist /usr/share/nginx/html
COPY --from=builder /build/nginx.con[f] /etc/nginx/conf.d/default.conf
+12322
View File
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -41,7 +41,6 @@
"react-loading-skeleton": "^3.5.0",
"react-multi-date-picker": "^4.5.2",
"react-otp-input": "^3.1.1",
"react-quill": "^2.0.0",
"react-quill-new": "^3.4.6",
"react-router-dom": "^7.5.2",
"react-spinners": "^0.17.0",
@@ -73,6 +72,5 @@
"typescript": "~5.7.2",
"typescript-eslint": "^8.26.1",
"vite": "^6.3.1"
},
"packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184"
}
}
-8578
View File
File diff suppressed because it is too large Load Diff
+1364 -18
View File
File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 103 KiB

+1 -1
View File
@@ -92,7 +92,7 @@ const Filters: FC<FiltersProps> = ({
}, [fields, initialValues, onChange]);
useEffect(() => {
const timeouts: Record<string, number> = {};
const timeouts: Record<string, ReturnType<typeof setTimeout>> = {};
Object.keys(inputValues).forEach(fieldName => {
timeouts[fieldName] = setTimeout(() => {
+14 -1
View File
@@ -8,6 +8,8 @@ interface EmailInputProps {
onRemoveEmail: (email: string) => void
suggestions: EmailSuggestion[]
onSearchSuggestions?: (query: string) => void
showCcBccButton?: boolean
onCcBccClick?: () => void
}
const EmailInput: FC<EmailInputProps> = ({
@@ -15,7 +17,9 @@ const EmailInput: FC<EmailInputProps> = ({
onAddEmail,
onRemoveEmail,
suggestions,
onSearchSuggestions
onSearchSuggestions,
showCcBccButton = false,
onCcBccClick
}) => {
const [to, setTo] = useState('')
const [filteredSuggestions, setFilteredSuggestions] = useState<EmailSuggestion[]>([])
@@ -195,6 +199,15 @@ const EmailInput: FC<EmailInputProps> = ({
placeholder={toEmails.length === 0 ? "ایمیل را وارد کرده و Enter بزنید" : ""}
className="flex-1 min-w-32 bg-transparent outline-none"
/>
{showCcBccButton && (
<button
onClick={onCcBccClick}
className='text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors pt-[3px] cursor-pointer '
>
CC/BCC
</button>
)}
</div>
{showSuggestions && filteredSuggestions.length > 0 && (
+69 -19
View File
@@ -21,10 +21,13 @@ const NewMessage: FC = () => {
const [isOpening, setIsOpening] = useState(false)
const [isIntelligenseOpen, setIsIntelligenseOpen] = useState(false)
const [prompt, setPrompt] = useState('')
const [showCcBcc, setShowCcBcc] = useState(false)
const {
// Form state
toEmails,
ccEmails,
bccEmails,
subject,
content,
priority,
@@ -38,6 +41,10 @@ const NewMessage: FC = () => {
// Form handlers
addEmail,
removeEmail,
addCcEmail,
removeCcEmail,
addBccEmail,
removeBccEmail,
setSubject,
setContent,
setPriority,
@@ -76,6 +83,7 @@ const NewMessage: FC = () => {
setTimeout(() => {
setOpenNewMessage(false)
resetForm()
setShowCcBcc(false)
}, 300)
}
@@ -130,23 +138,63 @@ const NewMessage: FC = () => {
</div>
{/* Content */}
<div className='space-y-6 md:space-y-8'>
<div>
<div>
<label className='text-sm'>
<label className='text-sm mb-1 block'>
{t('new_message.to')}
</label>
<div className='mt-1'>
<div>
<EmailInput
toEmails={toEmails}
onAddEmail={addEmail}
onRemoveEmail={removeEmail}
suggestions={emailSuggestions}
onSearchSuggestions={searchEmailSuggestions}
showCcBccButton={true}
onCcBccClick={() => setShowCcBcc(!showCcBcc)}
/>
</div>
</div>
<div className='flex gap-2 items-end'>
{/* CC/BCC Accordion */}
<div
className={clx(
'overflow-hidden transition-all duration-300 ease-in-out',
showCcBcc ? 'max-h-96 opacity-100' : 'max-h-0 opacity-0'
)}
>
<div className='mt-4'>
<label className='text-sm mb-1 block'>
CC
</label>
<div>
<EmailInput
toEmails={ccEmails}
onAddEmail={addCcEmail}
onRemoveEmail={removeCcEmail}
suggestions={emailSuggestions}
onSearchSuggestions={searchEmailSuggestions}
/>
</div>
</div>
<div className='mt-4'>
<label className='text-sm mb-1 block'>
BCC
</label>
<div>
<EmailInput
toEmails={bccEmails}
onAddEmail={addBccEmail}
onRemoveEmail={removeBccEmail}
suggestions={emailSuggestions}
onSearchSuggestions={searchEmailSuggestions}
/>
</div>
</div>
</div>
<div className='flex gap-2 items-end mt-4'>
<div className='flex lg:flex-row flex-col flex-1 items-end gap-2'>
<Input
label={t('new_message.subject')}
@@ -175,28 +223,30 @@ const NewMessage: FC = () => {
</div>
</div>
<div>
<div className='mt-4'>
<EmailEditor
value={content}
onChange={setContent}
/>
</div>
<EmailAttachments
attachments={attachments}
onAttachmentsChange={setAttachments}
/>
<div className='mt-4'>
<EmailAttachments
attachments={attachments}
onAttachmentsChange={setAttachments}
/>
</div>
<EmailFormActions
priority={priority}
onPriorityChange={setPriority}
onSend={handleSendWithClose}
onSaveDraft={handleSaveDraftWithClose}
isSending={isSending}
isSavingDraft={isSavingDraft}
/>
<div className='mt-4'>
<EmailFormActions
priority={priority}
onPriorityChange={setPriority}
onSend={handleSendWithClose}
onSaveDraft={handleSaveDraftWithClose}
isSending={isSending}
isSavingDraft={isSavingDraft}
/>
</div>
</div>
</div>
</>
+10
View File
@@ -5,6 +5,7 @@ import AvatarImage from '@/assets/images/avatar.svg'
import AnswerIcon from '@/assets/images/answer.svg'
import EmailEditor from './EmailEditor'
import EmailFormActions from './EmailFormActions'
import EmailAttachments from './EmailAttachments'
import { useReply } from './hooks/useReply'
import { MessageDetail } from '@/pages/received/types/Types'
import { toast } from '@/components/Toast'
@@ -30,7 +31,9 @@ const Reply: FC<ReplyProps> = ({
content,
isExpanded,
isSending,
attachments,
setContent,
setAttachments,
expandReply,
collapseReply,
handleSendReply,
@@ -137,6 +140,13 @@ const Reply: FC<ReplyProps> = ({
/>
</div>
<div className='mb-4'>
<EmailAttachments
attachments={attachments}
onAttachmentsChange={setAttachments}
/>
</div>
<EmailFormActions
onSend={handleSend}
onClose={handleClose}
@@ -21,6 +21,8 @@ export const useNewMessage = () => {
const { email: userEmail } = useAuthStore();
const [toEmails, setToEmails] = useState<string[]>([]);
const [ccEmails, setCcEmails] = useState<string[]>([]);
const [bccEmails, setBccEmails] = useState<string[]>([]);
const [subject, setSubject] = useState("");
const [content, setContent] = useState("");
const [priority, setPriority] = useState("");
@@ -46,8 +48,23 @@ export const useNewMessage = () => {
useEffect(() => {
if (draftDetail) {
const toEmailsList =
draftDetail.to?.map((recipient) => recipient.address) || [];
draftDetail.to?.map(
(recipient: { address: string; name: string }) => recipient.address
) || [];
setToEmails(toEmailsList);
const ccEmailsList =
draftDetail.cc?.map(
(recipient: { address: string; name: string }) => recipient.address
) || [];
setCcEmails(ccEmailsList);
const bccEmailsList =
draftDetail.bcc?.map(
(recipient: { address: string; name: string }) => recipient.address
) || [];
setBccEmails(bccEmailsList);
setSubject(draftDetail.subject || "");
const draftWithContent = (draftDetail as unknown) as {
@@ -96,6 +113,40 @@ export const useNewMessage = () => {
setToEmails(toEmails.filter((email) => email !== emailToRemove));
};
const addCcEmail = (email: string) => {
if (!isValidEmail(email)) {
return;
}
if (ccEmails.includes(email)) {
toast("این ایمیل قبلاً اضافه شده است", "error");
return;
}
setCcEmails([...ccEmails, email]);
};
const removeCcEmail = (emailToRemove: string) => {
setCcEmails(ccEmails.filter((email) => email !== emailToRemove));
};
const addBccEmail = (email: string) => {
if (!isValidEmail(email)) {
return;
}
if (bccEmails.includes(email)) {
toast("این ایمیل قبلاً اضافه شده است", "error");
return;
}
setBccEmails([...bccEmails, email]);
};
const removeBccEmail = (emailToRemove: string) => {
setBccEmails(bccEmails.filter((email) => email !== emailToRemove));
};
const parseEmailAddresses = (emails: string[]) => {
return emails
.filter((email) => email.length > 0)
@@ -104,6 +155,8 @@ export const useNewMessage = () => {
const resetForm = () => {
setToEmails([]);
setCcEmails([]);
setBccEmails([]);
setSubject("");
setContent("");
setPriority("");
@@ -141,6 +194,8 @@ export const useNewMessage = () => {
const textDirection = detectTextDirection(contentString);
const draftUpdateData = {
to: parseEmailAddresses(toEmails),
...(ccEmails.length > 0 && { cc: parseEmailAddresses(ccEmails) }),
...(bccEmails.length > 0 && { bcc: parseEmailAddresses(bccEmails) }),
subject,
html: `<div dir="${textDirection}" style="text-align: start; unicode-bidi: plaintext; font-family: 'Irancell', 'Vazirmatn', sans-serif;">${contentString}</div>`,
text: contentString.replace(/<[^>]*>/g, ""),
@@ -193,6 +248,8 @@ export const useNewMessage = () => {
const emailData: SendEmailDto = {
from: { address: userEmail || "sender@example.com" },
to: parseEmailAddresses(toEmails),
...(ccEmails.length > 0 && { cc: parseEmailAddresses(ccEmails) }),
...(bccEmails.length > 0 && { bcc: parseEmailAddresses(bccEmails) }),
subject,
html: `<div dir="${textDirection}" style="text-align: start; unicode-bidi: plaintext; font-family: 'Irancell', 'Vazirmatn', sans-serif;">${contentString}</div>`,
text: contentString.replace(/<[^>]*>/g, ""),
@@ -231,6 +288,8 @@ export const useNewMessage = () => {
return {
toEmails,
ccEmails,
bccEmails,
subject,
content,
priority,
@@ -242,6 +301,10 @@ export const useNewMessage = () => {
addEmail,
removeEmail,
addCcEmail,
removeCcEmail,
addBccEmail,
removeBccEmail,
setSubject,
setContent,
setPriority,
+6 -1
View File
@@ -1,7 +1,7 @@
import { useState } from "react";
import { useAuthStore } from "@/pages/auth/store/AuthStore";
import { useSendEmail } from "@/pages/received/hooks/useEmailData";
import { SendEmailDto } from "@/pages/received/types/Types";
import { SendEmailDto, EmailAttachmentDto } from "@/pages/received/types/Types";
import { MessageDetail } from "@/pages/received/types/Types";
import { toast } from "@/components/Toast";
@@ -19,12 +19,14 @@ export const useReply = ({
const { email: userEmail } = useAuthStore();
const [content, setContent] = useState<string>("");
const [isExpanded, setIsExpanded] = useState<boolean>(false);
const [attachments, setAttachments] = useState<EmailAttachmentDto[]>([]);
const sendEmailMutation = useSendEmail();
const resetForm = () => {
setContent("");
setIsExpanded(false);
setAttachments([]);
};
const handleSendReply = async (html: string) => {
@@ -97,6 +99,7 @@ ${
originalMessage.html?.join("").replace(/<[^>]*>/g, "") ||
""
}`,
attachments: attachments.length > 0 ? attachments : undefined,
};
try {
@@ -144,8 +147,10 @@ ${
content,
isExpanded,
isSending: sendEmailMutation.isPending,
attachments,
setContent,
setAttachments,
expandReply,
collapseReply,
handleSendReply,
+1 -1
View File
@@ -8,7 +8,7 @@ import { useNavigate } from 'react-router-dom';
import { useGetFavoriteMessages } from './hooks/useFavoriteData';
import { FavoriteMessage } from './types/FavoriteTypes';
import { useEmailActions } from '@/hooks/useEmailActions';
import Favorite from '../received/Components/Favorite';
import Favorite from '../received/components/Favorite';
import { Checkbox } from '@/components/ui/checkbox';
import React from 'react';
import { getIconColor } from '@/utils/colorUtils';
+17 -7
View File
@@ -4,25 +4,28 @@ import { useTranslation } from 'react-i18next'
import { useParams } from 'react-router-dom'
import AvatarImage from '@/assets/images/avatar.svg'
import { DocumentDownload } from 'iconsax-react'
import Header from './Components/Header'
import Header from './components/Header'
import { useGetMessageDetail, useDownloadAttachment, useSendEmail } from './hooks/useEmailData'
import { toast } from '@/components/Toast'
import { formatDate, sanitizeEmailHTML, detectTextDirection } from '@/config/func'
import SkeletonDetail from './Components/SkeletonDetail'
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'
import EmojiReaction from '@/components/EmojiReaction'
import { useAuthStore } from '@/pages/auth/store/AuthStore'
import { SendEmailDto } from './types/Types'
import { getIconColor } from '@/utils/colorUtils'
import CcRecipients from './components/CcRecipients'
import BccRecipients from './components/BccRecipients'
import { useGetProfile } from '@/pages/profile/hooks/useProfileData'
const DetailEmail: FC = () => {
const { t } = useTranslation()
const emailActions = useEmailActions()
const { email: userEmail } = useAuthStore()
const { data: profileData } = useGetProfile()
const userEmail = profileData?.data?.user?.emailAddress || ''
const { id, mailbox } = useParams<{ id: string, mailbox: string }>()
const [activeAction, setActiveAction] = useState<'reply' | 'forward' | null>(null)
@@ -179,18 +182,25 @@ const DetailEmail: FC = () => {
<img src={AvatarImage} className='size-10' />
{
messageDetail.mailboxName === MailboxEnum.SENT ?
<div>
<div className='flex-1'>
<div className='text-sm'>{messageDetail.to[0].name || messageDetail.to[0].address}</div>
<div className='text-xs text-description mt-0.5'>{messageDetail.to[0]?.address}</div>
</div>
:
<div>
<div className='flex-1'>
<div className='text-sm'>{messageDetail.from.name || messageDetail.from.address}</div>
<div className='text-xs text-description mt-0.5'>{messageDetail.from?.address}</div>
</div>
}
</div>
<CcRecipients recipients={messageDetail.cc || []} />
<BccRecipients
messageDetail={messageDetail}
userEmail={userEmail}
mailboxName={messageDetail.mailboxName as MailboxEnum}
/>
</div>
<div className='xl:mt-8 mt-3 text-[13px] leading-7 font-light'>
+3 -3
View File
@@ -11,10 +11,10 @@ import { InboxMessage } from './types/Types';
import MarkAsRead from '@/assets/images/mark_as_read.svg'
import { useEmailActions } from '@/hooks/useEmailActions';
import Favorite from './Components/Favorite';
import Summerize from './Components/Summerize';
import Favorite from './components/Favorite';
import Summerize from './components/Summerize';
import Intelligense from '@/assets/images/intelligense.svg'
import SummarizeModal from './Components/SummarizeModal';
import SummarizeModal from './components/SummarizeModal';
import ModalConfrim from '@/components/ModalConfrim';
import { ErrorType } from '@/helpers/types';
import { toast } from '@/components/Toast';
@@ -0,0 +1,74 @@
import { FC } from 'react'
import { MessageDetail } from '../types/Types'
import { MailboxEnum } from '../enum/Enum'
interface BccRecipientsProps {
messageDetail: MessageDetail
userEmail: string | null
mailboxName: MailboxEnum
}
const BccRecipients: FC<BccRecipientsProps> = ({ messageDetail, userEmail, mailboxName }) => {
// If userEmail is not available, we can't determine BCC status
if (!userEmail || userEmail.trim() === '') {
return null
}
// Check if current user was BCC recipient (for received messages)
const isInTo = messageDetail.to?.some(recipient =>
recipient.address.toLowerCase().trim() === userEmail.toLowerCase().trim()
) || false
const isInCc = messageDetail.cc?.some(recipient =>
recipient.address.toLowerCase().trim() === userEmail.toLowerCase().trim()
) || false
// Check if user is explicitly in BCC field OR implicitly (not in to/cc but received)
const isExplicitlyInBcc = messageDetail.bcc?.some(recipient =>
recipient.address.toLowerCase().trim() === userEmail.toLowerCase().trim()
) || false
const isInBcc = isExplicitlyInBcc || (!isInTo && !isInCc && mailboxName !== MailboxEnum.SENT)
// Show BCC notice for received messages
if (isInBcc) {
return (
<div className='mt-4 mr-14'>
<div className='bg-accent border border-border rounded-lg px-4 py-2 text-xs text-muted-foreground'>
شما به عنوان رونوشت مخفی (BCC) این پیام را دریافت کردهاید
</div>
</div>
)
}
// Show BCC recipients for sent messages
if (mailboxName === MailboxEnum.SENT && messageDetail.envelope?.rcpt) {
const toAddresses = messageDetail.to?.map(r => r.address.toLowerCase()) || []
const ccAddresses = messageDetail.cc?.map(r => r.address.toLowerCase()) || []
const bccRecipients = messageDetail.envelope.rcpt.filter(rcpt => {
const address = rcpt.value.toLowerCase()
return !toAddresses.includes(address) && !ccAddresses.includes(address)
})
if (bccRecipients.length === 0) {
return null
}
return (
<div className='mt-4 mr-14'>
<div className='text-xs text-description mb-2'>رونوشت مخفی (BCC):</div>
<div className='flex flex-wrap gap-2'>
{bccRecipients.map((recipient, index) => (
<div key={index} className='bg-secondary rounded-full px-3 py-1 text-xs'>
{recipient.formatted || recipient.value}
</div>
))}
</div>
</div>
)
}
return null
}
export default BccRecipients
@@ -0,0 +1,28 @@
import { FC } from 'react'
import { type EmailRecipientDto } from '../types/Types'
interface CcRecipientsProps {
recipients: EmailRecipientDto[]
}
const CcRecipients: FC<CcRecipientsProps> = ({ recipients }) => {
if (!recipients || recipients.length === 0) {
return null
}
return (
<div className='mt-4 mr-14'>
<div className='text-xs text-description mb-2'>رونوشت (CC):</div>
<div className='flex flex-wrap gap-2'>
{recipients.map((recipient, index) => (
<div key={index} className='bg-secondary rounded-full px-3 py-1 text-xs'>
{recipient.name || recipient.address} {recipient.name && `<${recipient.address}>`}
</div>
))}
</div>
</div>
)
}
export default CcRecipients
+18 -2
View File
@@ -198,6 +198,14 @@ export interface MessageDetailResponse {
address: string;
name: string;
}>;
cc?: Array<{
address: string;
name: string;
}>;
bcc?: Array<{
address: string;
name: string;
}>;
subject: string;
messageId: string;
date: string;
@@ -214,7 +222,7 @@ export interface MessageDetailResponse {
attachments: EmailAttachmentDto[];
references: string[];
metaData: Record<string, unknown>;
verificationResults: {
verificationResults?: {
tls: {
name: string;
standardName: string;
@@ -258,6 +266,14 @@ export interface MessageDetail {
address: string;
name: string;
}>;
cc?: Array<{
address: string;
name: string;
}>;
bcc?: Array<{
address: string;
name: string;
}>;
subject: string;
messageId: string;
date: string;
@@ -274,7 +290,7 @@ export interface MessageDetail {
attachments: EmailAttachmentDto[];
references: string[];
metaData: Record<string, unknown>;
verificationResults: {
verificationResults?: {
tls: {
name: string;
standardName: string;