mark all as read + spam empty
This commit is contained in:
@@ -7,7 +7,7 @@ VITE_SOCKET_URL = 'wss://dmail-api.danakcorp.com/email'
|
||||
# VITE_SOCKET_URL = 'ws://192.168.1.118:4000/email'
|
||||
|
||||
VITE_BASE_URL = 'https://dmail-api.danakcorp.com'
|
||||
# VITE_BASE_URL = 'http://192.168.1.101:4000'
|
||||
# VITE_BASE_URL = 'http://192.168.1.106:4000'
|
||||
|
||||
|
||||
VITE_SERVICE_ID = 'e51afdc3-ea0b-49cf-8f49-2a6f131b024e'
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { FC, useState } from 'react'
|
||||
import DefaulModal from './DefaulModal'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from './Button'
|
||||
import Textarea from './Textarea'
|
||||
|
||||
type Props = {
|
||||
isLoading?: boolean,
|
||||
close: () => void,
|
||||
isOpen: boolean,
|
||||
onConfrim: (text?: string) => void,
|
||||
label?: string,
|
||||
isHasDescription?: boolean,
|
||||
title?: string
|
||||
}
|
||||
|
||||
const ModalConfrim: FC<Props> = (props: Props) => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const [description, setDescription] = useState<string>('')
|
||||
|
||||
return (
|
||||
<DefaulModal
|
||||
open={props.isOpen}
|
||||
close={props.close}
|
||||
title_header={props.title ? props.title : t('confrim.subject')}
|
||||
isHeader
|
||||
>
|
||||
<div className='mt-6'>
|
||||
<div className='text-sm text-center'>
|
||||
{
|
||||
props.label ?
|
||||
props.label
|
||||
:
|
||||
t('confrim.content')
|
||||
}
|
||||
|
||||
{
|
||||
props.isHasDescription &&
|
||||
<div className='mt-4'>
|
||||
<Textarea
|
||||
placeholder={t('description')}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
value={description}
|
||||
className='bg-transparent border border-gray-500 rounded-xl w-[300px] p-2'
|
||||
label=''
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='flex gap-4 justify-center mt-10'>
|
||||
<Button
|
||||
label={t('confrim.yes')}
|
||||
onClick={() => props.onConfrim(props.isHasDescription ? description : undefined)}
|
||||
loading={props.isLoading}
|
||||
/>
|
||||
<Button
|
||||
label={t('confrim.cancel')}
|
||||
className='bg-transparent text-black border border-primary'
|
||||
onClick={props.close}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</DefaulModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModalConfrim
|
||||
+8
-1
@@ -156,6 +156,12 @@
|
||||
"cancel": "انصراف",
|
||||
"confirm_disable": "تایید غیر فعال سازی"
|
||||
},
|
||||
"confrim": {
|
||||
"subject": "حذف",
|
||||
"content": "برای حذف این آیتم مطمئن هستید؟",
|
||||
"yes": "بله",
|
||||
"cancel": "لغو"
|
||||
},
|
||||
"success": "عملیات با موفقیت انجام شد",
|
||||
"attachment_select": "اضافه کردن فایل ضمیمه",
|
||||
"domain": {
|
||||
@@ -203,7 +209,8 @@
|
||||
"Junk": "هرزنامهها",
|
||||
"SENT": "ارسال شدهها",
|
||||
"TRASH": "سطل زباله",
|
||||
"Sent Mail": "ارسال شده ها"
|
||||
"Sent Mail": "ارسال شده ها",
|
||||
"Trash": "سطل زباله"
|
||||
},
|
||||
"auth": {
|
||||
"welcome": "خوش آمدید",
|
||||
|
||||
@@ -2,13 +2,17 @@ import Filters, { FilterValues } from '../../components/Filters';
|
||||
import { FC, useState } from 'react'
|
||||
import Table from '../../components/Table';
|
||||
import { ColumnType } from '../../components/types/TableTypes';
|
||||
import { InfoCircle, More, Refresh2, Trash, RecoveryConvert } from 'iconsax-react';
|
||||
import { InfoCircle, Refresh2, Trash, RecoveryConvert } from 'iconsax-react';
|
||||
import { RowActionItem } from '../../components/RowActionsDropdown';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useGetTrashMessages } from './hooks/useTrashData';
|
||||
import { useEmptyTrash, useGetTrashMessages } from './hooks/useTrashData';
|
||||
import { TrashMessage } from './types/TrashTypes';
|
||||
import { formatDate } from '@/config/func';
|
||||
import { useEmailActions } from '@/hooks/useEmailActions';
|
||||
import Button from '@/components/Button';
|
||||
import ModalConfrim from '@/components/ModalConfrim';
|
||||
import { ErrorType } from '@/helpers/types';
|
||||
import { toast } from '@/components/Toast';
|
||||
|
||||
const List: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -16,7 +20,8 @@ const List: FC = () => {
|
||||
const [previousCursor, setPreviousCursor] = useState<string | undefined>(undefined);
|
||||
const [filters, setFilters] = useState<FilterValues>({});
|
||||
const emailActions = useEmailActions();
|
||||
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const { mutate: emptyTrash, isPending } = useEmptyTrash();
|
||||
const { data: trashData, isLoading, refetch, isFetching } = useGetTrashMessages({
|
||||
next: nextCursor,
|
||||
previous: previousCursor,
|
||||
@@ -59,10 +64,15 @@ const List: FC = () => {
|
||||
<Refresh2
|
||||
size={18}
|
||||
color='black'
|
||||
className={`cursor-pointer ${isFetching ? 'animate-spin-reverse' : ''}`}
|
||||
className={`cursor-pointer min-w-[18px] ${isFetching ? 'animate-spin-reverse' : ''}`}
|
||||
onClick={() => refetch()}
|
||||
/>
|
||||
<More size={18} color='black' className='rotate-90 cursor-pointer' />
|
||||
<Button
|
||||
label='خالی کردن سطل زباله'
|
||||
className='h-8'
|
||||
onClick={() => setShowModal(true)}
|
||||
loading={isPending}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -95,6 +105,18 @@ const List: FC = () => {
|
||||
setSelectedMessages([]);
|
||||
};
|
||||
|
||||
const handleTrash = () => {
|
||||
emptyTrash(undefined, {
|
||||
onSuccess: () => {
|
||||
refetch();
|
||||
setShowModal(false);
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error.message[0], 'error')
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getRowActions = (message: TrashMessage): RowActionItem[] => [
|
||||
{
|
||||
label: 'بازگردانی',
|
||||
@@ -108,10 +130,6 @@ const List: FC = () => {
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const handleNext = (cursor?: string) => {
|
||||
setNextCursor(cursor);
|
||||
setPreviousCursor(undefined);
|
||||
@@ -165,6 +183,13 @@ const List: FC = () => {
|
||||
onPrevious: handlePrevious
|
||||
} : undefined}
|
||||
/>
|
||||
|
||||
<ModalConfrim
|
||||
isOpen={showModal}
|
||||
close={() => setShowModal(false)}
|
||||
onConfrim={handleTrash}
|
||||
isLoading={isPending}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getTrashMessages } from '../service/TrashService';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { emptyTrash, getTrashMessages } from '../service/TrashService';
|
||||
import { MessageListQueryDto } from '../types/TrashTypes';
|
||||
|
||||
export const useGetTrashMessages = (query: MessageListQueryDto) => {
|
||||
@@ -10,4 +10,8 @@ export const useGetTrashMessages = (query: MessageListQueryDto) => {
|
||||
});
|
||||
};
|
||||
|
||||
// Note: useRestoreMessage is now handled by useEmailActions hook
|
||||
export const useEmptyTrash = () => {
|
||||
return useMutation({
|
||||
mutationFn: () => emptyTrash(),
|
||||
});
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MailboxEnum } from "@/pages/received/enum/Enum";
|
||||
import axios from "../../../config/axios";
|
||||
import { getMailboxId } from "../../../config/func";
|
||||
import { MessageListQueryDto, TrashResponse } from "../types/TrashTypes";
|
||||
@@ -25,3 +26,11 @@ export const getTrashMessages = async (
|
||||
};
|
||||
|
||||
// Note: deleteMessagePermanently and restoreMessage are now handled by EmailActionsService
|
||||
|
||||
export const emptyTrash = async () => {
|
||||
const mailboxId = getMailboxId(MailboxEnum.TRASH);
|
||||
const { data } = await axios.patch(
|
||||
`/email/messages/empty-trash?mailbox=${mailboxId}`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { ColumnType } from '../../components/types/TableTypes';
|
||||
import { InfoCircle, More, Refresh2, Trash, Edit, Archive, ArchiveTick, Star1 } from 'iconsax-react';
|
||||
import { RowActionItem } from '../../components/RowActionsDropdown';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useGetInbox, useSummarizeEmail } from './hooks/useEmailData';
|
||||
import { useGetInbox, useMarkAllRead, useSummarizeEmail } from './hooks/useEmailData';
|
||||
import { InboxMessage } from './types/Types';
|
||||
import { formatDate } from '@/config/func';
|
||||
import MarkAsRead from '@/assets/images/mark_as_read.svg'
|
||||
@@ -15,6 +15,10 @@ import Favorite from './Components/Favorite';
|
||||
import Summerize from './Components/Summerize';
|
||||
import Intelligense from '@/assets/images/intelligense.svg'
|
||||
import SummarizeModal from './Components/SummarizeModal';
|
||||
import Button from '@/components/Button';
|
||||
import ModalConfrim from '@/components/ModalConfrim';
|
||||
import { ErrorType } from '@/helpers/types';
|
||||
import { toast } from '@/components/Toast';
|
||||
|
||||
const List: FC = () => {
|
||||
|
||||
@@ -25,7 +29,9 @@ const List: FC = () => {
|
||||
const [filters, setFilters] = useState<FilterValues>({});
|
||||
const emailActions = useEmailActions();
|
||||
const [openSummarizeModal, setOpenSummarizeModal] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const { mutate: summarizeEmail, isPending: isSummarizing, data: summarizeData } = useSummarizeEmail();
|
||||
const { mutate: markAllRead, isPending: isMarkingAllRead } = useMarkAllRead();
|
||||
|
||||
const { data: inboxData, isLoading, refetch, isFetching } = useGetInbox({
|
||||
next: nextCursor,
|
||||
@@ -77,9 +83,15 @@ const List: FC = () => {
|
||||
<Refresh2
|
||||
size={18}
|
||||
color='black'
|
||||
className={`cursor-pointer ${isFetching ? 'animate-spin-reverse' : ''}`}
|
||||
className={`cursor-pointer min-w-[18px] ${isFetching ? 'animate-spin-reverse' : ''}`}
|
||||
onClick={() => refetch()}
|
||||
/>
|
||||
<Button
|
||||
label='علامتگذاری همه به عنوان خوانده شده'
|
||||
className='h-8'
|
||||
onClick={() => setShowModal(true)}
|
||||
loading={isMarkingAllRead}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -147,6 +159,18 @@ const List: FC = () => {
|
||||
setSelectedMessages([]);
|
||||
};
|
||||
|
||||
const handleMarkAll = () => {
|
||||
markAllRead(undefined, {
|
||||
onSuccess: () => {
|
||||
refetch();
|
||||
setShowModal(false);
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error.message[0], 'error')
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getInlineActions = (item: InboxMessage) => [
|
||||
{
|
||||
icon: <Favorite flagged={item.flagged} id={item.id.toString()} mailbox={item.mailbox} />,
|
||||
@@ -285,6 +309,15 @@ const List: FC = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
<ModalConfrim
|
||||
isOpen={showModal}
|
||||
close={() => setShowModal(false)}
|
||||
onConfrim={handleMarkAll}
|
||||
isLoading={isMarkingAllRead}
|
||||
title={'خواندن همه'}
|
||||
label={'آیا از خواندن همه پیام ها اطمینان دارید؟'}
|
||||
/>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -131,3 +131,9 @@ export const useGenerateEmail = () => {
|
||||
mutationFn: (params: GenerateEmailRequest) => api.generateEmail(params),
|
||||
});
|
||||
};
|
||||
|
||||
export const useMarkAllRead = () => {
|
||||
return useMutation({
|
||||
mutationFn: () => api.markAllRead(),
|
||||
});
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SummarizeEmailRequest,
|
||||
GenerateEmailRequest,
|
||||
} from "../types/Types";
|
||||
import { MailboxEnum } from "../enum/Enum";
|
||||
|
||||
export const sendEmail = async (params: SendEmailDto) => {
|
||||
const { data } = await axios.post(`/email/send`, params);
|
||||
@@ -161,3 +162,11 @@ export const summarizeEmail = async (params: SummarizeEmailRequest) => {
|
||||
const { data } = await axios.post(`/ai-assistant/summarize-email`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const markAllRead = async () => {
|
||||
const mailboxId = getMailboxId(MailboxEnum.INBOX);
|
||||
const { data } = await axios.patch(
|
||||
`/email/messages/mark-all-read?mailbox=${mailboxId}`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
+34
-4
@@ -2,13 +2,17 @@ import Filters, { FilterValues } from '../../components/Filters';
|
||||
import { FC, useState } from 'react'
|
||||
import Table from '../../components/Table';
|
||||
import { ColumnType } from '../../components/types/TableTypes';
|
||||
import { InfoCircle, More, Refresh2, Trash, RecoveryConvert } from 'iconsax-react';
|
||||
import { InfoCircle, Refresh2, Trash, RecoveryConvert } from 'iconsax-react';
|
||||
import { RowActionItem } from '../../components/RowActionsDropdown';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useGetSpamMessages } from './hooks/useSpamData';
|
||||
import { useEmptySpam, useGetSpamMessages } from './hooks/useSpamData';
|
||||
import { SpamMessage } from './types/SpamTypes';
|
||||
import { formatDate } from '@/config/func';
|
||||
import { useEmailActions } from '@/hooks/useEmailActions';
|
||||
import Button from '@/components/Button';
|
||||
import ModalConfrim from '@/components/ModalConfrim';
|
||||
import { ErrorType } from '@/helpers/types';
|
||||
import { toast } from '@/components/Toast';
|
||||
|
||||
const List: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -16,6 +20,8 @@ const List: FC = () => {
|
||||
const [previousCursor, setPreviousCursor] = useState<string | undefined>(undefined);
|
||||
const [filters, setFilters] = useState<FilterValues>({});
|
||||
const emailActions = useEmailActions();
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const { mutate: emptySpam, isPending } = useEmptySpam();
|
||||
|
||||
const { data: spamData, isLoading, refetch, isFetching } = useGetSpamMessages({
|
||||
next: nextCursor,
|
||||
@@ -58,10 +64,15 @@ const List: FC = () => {
|
||||
<Refresh2
|
||||
size={18}
|
||||
color='black'
|
||||
className={`cursor-pointer ${isFetching ? 'animate-spin-reverse' : ''}`}
|
||||
className={`cursor-pointer min-w-[18px] ${isFetching ? 'animate-spin-reverse' : ''}`}
|
||||
onClick={() => refetch()}
|
||||
/>
|
||||
<More size={18} color='black' className='rotate-90 cursor-pointer' />
|
||||
<Button
|
||||
label='خالی کردن هرزنامه'
|
||||
className='h-8'
|
||||
onClick={() => setShowModal(true)}
|
||||
loading={isPending}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -94,6 +105,18 @@ const List: FC = () => {
|
||||
setSelectedMessages([]);
|
||||
};
|
||||
|
||||
const handleSpam = () => {
|
||||
emptySpam(undefined, {
|
||||
onSuccess: () => {
|
||||
refetch();
|
||||
setShowModal(false);
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error.message[0], 'error')
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getRowActions = (message: SpamMessage): RowActionItem[] => [
|
||||
{
|
||||
label: 'غیر اسپم',
|
||||
@@ -160,6 +183,13 @@ const List: FC = () => {
|
||||
onPrevious: handlePrevious
|
||||
} : undefined}
|
||||
/>
|
||||
|
||||
<ModalConfrim
|
||||
isOpen={showModal}
|
||||
close={() => setShowModal(false)}
|
||||
onConfrim={handleSpam}
|
||||
isLoading={isPending}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getSpamMessages } from '../service/SpamService';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { emptySpam, getSpamMessages } from '../service/SpamService';
|
||||
import { MessageListQueryDto } from '../types/SpamTypes';
|
||||
|
||||
export const useGetSpamMessages = (query: MessageListQueryDto) => {
|
||||
@@ -9,3 +9,9 @@ export const useGetSpamMessages = (query: MessageListQueryDto) => {
|
||||
staleTime: 0
|
||||
});
|
||||
};
|
||||
|
||||
export const useEmptySpam = () => {
|
||||
return useMutation({
|
||||
mutationFn: () => emptySpam(),
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import axios from "../../../config/axios";
|
||||
import { getMailboxId } from "../../../config/func";
|
||||
import { MessageListQueryDto, SpamResponse } from "../types/SpamTypes";
|
||||
import { MailboxEnum } from "@/pages/received/enum/Enum";
|
||||
|
||||
export const getSpamMessages = async (
|
||||
query: MessageListQueryDto
|
||||
@@ -23,3 +24,11 @@ export const getSpamMessages = async (
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const emptySpam = async () => {
|
||||
const mailboxId = getMailboxId(MailboxEnum.Junk);
|
||||
const { data } = await axios.patch(
|
||||
`/email/messages/empty-spam?mailbox=${mailboxId}`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
+40
-40
@@ -2,7 +2,7 @@ import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
// import { VitePWA } from "vite-plugin-pwa";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
@@ -10,49 +10,49 @@ export default defineConfig({
|
||||
react(),
|
||||
tailwindcss(),
|
||||
tsconfigPaths(),
|
||||
// VitePWA({
|
||||
// registerType: "autoUpdate",
|
||||
// devOptions: {
|
||||
// enabled: false, // در حالت توسعه هم کار کند
|
||||
// },
|
||||
// manifest: {
|
||||
// name: "Danak Mail",
|
||||
// short_name: "Danak Mail",
|
||||
// description: "Danak Mail",
|
||||
// theme_color: "#ffffff",
|
||||
// background_color: "#ffffff",
|
||||
// display: "standalone",
|
||||
// start_url: "/received", // صفحه اصلی PWA
|
||||
// icons: [
|
||||
VitePWA({
|
||||
registerType: "autoUpdate",
|
||||
devOptions: {
|
||||
enabled: false, // در حالت توسعه هم کار کند
|
||||
},
|
||||
manifest: {
|
||||
name: "Danak Mail",
|
||||
short_name: "Danak Mail",
|
||||
description: "Danak Mail",
|
||||
theme_color: "#ffffff",
|
||||
background_color: "#ffffff",
|
||||
display: "standalone",
|
||||
start_url: "/received", // صفحه اصلی PWA
|
||||
icons: [
|
||||
{
|
||||
src: "/icon-192x192.png",
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: "/icon-512x512.png",
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
},
|
||||
],
|
||||
},
|
||||
// workbox: {
|
||||
// maximumFileSizeToCacheInBytes: 4 * 1024 * 1024, // افزایش به 4MB
|
||||
// runtimeCaching: [
|
||||
// {
|
||||
// src: "/icon-192x192.png",
|
||||
// sizes: "192x192",
|
||||
// type: "image/png",
|
||||
// urlPattern: /^https:\/\/admin-panel\.danakcorp\.com\//,
|
||||
// handler: "NetworkFirst",
|
||||
// options: {
|
||||
// cacheName: "api-cache",
|
||||
// expiration: {
|
||||
// maxEntries: 50,
|
||||
// maxAgeSeconds: 60 * 60 * 24,
|
||||
// },
|
||||
// networkTimeoutSeconds: 10,
|
||||
// },
|
||||
// {
|
||||
// src: "/icon-512x512.png",
|
||||
// sizes: "512x512",
|
||||
// type: "image/png",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// // workbox: {
|
||||
// // maximumFileSizeToCacheInBytes: 4 * 1024 * 1024, // افزایش به 4MB
|
||||
// // runtimeCaching: [
|
||||
// // {
|
||||
// // urlPattern: /^https:\/\/admin-panel\.danakcorp\.com\//,
|
||||
// // handler: "NetworkFirst",
|
||||
// // options: {
|
||||
// // cacheName: "api-cache",
|
||||
// // expiration: {
|
||||
// // maxEntries: 50,
|
||||
// // maxAgeSeconds: 60 * 60 * 24,
|
||||
// // },
|
||||
// // networkTimeoutSeconds: 10,
|
||||
// // },
|
||||
// // },
|
||||
// // ],
|
||||
// // },
|
||||
// }),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user