address
This commit is contained in:
@@ -5,44 +5,149 @@ import { ColumnType } from '@/components/types/TableTypes'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/components/Button'
|
||||
import { TickCircle } from 'iconsax-react'
|
||||
import { TickCircle, Edit2, Trash } from 'iconsax-react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { AddressData, CreateAddressType } from './types/Types'
|
||||
import { AddressData, CreateAddressType, UpdateAddressType } from './types/Types'
|
||||
import { useGetDomains } from '../domain/hooks/useDomainData'
|
||||
import { yupResolver } from "@hookform/resolvers/yup"
|
||||
import * as yup from "yup"
|
||||
import { useCreateAddress, useGetAddress } from './hooks/useAddressData'
|
||||
import { useCreateAddress, useGetAddress, useUpdateAddress, useDeleteAddress } from './hooks/useAddressData'
|
||||
import { useGetSpace } from '../domain/hooks/useDomainData'
|
||||
import { toast } from '@/components/Toast'
|
||||
import { ErrorType } from '@/helpers/types'
|
||||
import ProgressBarEmail from './components/ProgressBarEmail'
|
||||
import ToggleStatus from './components/ToggleStatus'
|
||||
|
||||
|
||||
import RowActionsDropdown, { RowActionItem } from '@/components/RowActionsDropdown'
|
||||
import ModalConfrim from '@/components/ModalConfrim'
|
||||
|
||||
const Address: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const schema = yup.object().shape({
|
||||
title: yup.string().required(t('errors.required')),
|
||||
username: yup.string().required(t('errors.required')),
|
||||
password: yup.string().required(t('errors.required')),
|
||||
domainId: yup.string().required(t('errors.required')),
|
||||
})
|
||||
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const [status, setStatus] = useState<string>('all')
|
||||
const [currentPage, setCurrentPage] = useState<number>(1)
|
||||
const [selectedAddress, setSelectedAddress] = useState<AddressData | null>(null)
|
||||
const [isEditMode, setIsEditMode] = useState(false)
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
|
||||
const [addressToDelete, setAddressToDelete] = useState<AddressData | null>(null)
|
||||
|
||||
const { data: domain } = useGetDomains()
|
||||
const { data: spaceData } = useGetSpace()
|
||||
const { mutate: createAddress, isPending } = useCreateAddress()
|
||||
const { data: addresses, isPending: isPendingAddress } = useGetAddress(search, status)
|
||||
const { register, handleSubmit, formState: { errors }, setValue
|
||||
} = useForm<CreateAddressType>({
|
||||
resolver: yupResolver(schema),
|
||||
const { mutate: updateAddress, isPending: isUpdating } = useUpdateAddress()
|
||||
const { mutate: deleteAddress } = useDeleteAddress()
|
||||
const { data: addresses, isPending: isPendingAddress } = useGetAddress(search, status, currentPage, 10)
|
||||
|
||||
type FormData = CreateAddressType & { password?: string; quotaMB?: number }
|
||||
|
||||
const { register, handleSubmit, formState: { errors }, setValue, reset, setError, clearErrors
|
||||
} = useForm<FormData>({
|
||||
defaultValues: {
|
||||
domainId: domain?.data?.domain?.id,
|
||||
}
|
||||
})
|
||||
|
||||
const validateForm = (params: FormData): boolean => {
|
||||
clearErrors()
|
||||
let isValid = true
|
||||
|
||||
if (!params.title) {
|
||||
setError('title', { message: t('errors.required') })
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (!params.username) {
|
||||
setError('username', { message: t('errors.required') })
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (!isEditMode && !params.password) {
|
||||
setError('password', { message: t('errors.required') })
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (!params.domainId) {
|
||||
setError('domainId', { message: t('errors.required') })
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// اعتبارسنجی quota
|
||||
if (params.quotaMB) {
|
||||
const quotaInBytes = params.quotaMB * 1024 * 1024
|
||||
const remainingSpace = spaceData?.data?.quota?.totalInGB * 1024 * 1024 * 1024 - spaceData?.data?.quota?.usedInGB * 1024 * 1024 * 1024
|
||||
|
||||
if (quotaInBytes > remainingSpace) {
|
||||
setError('quotaMB', { message: 'فضای وارد شده بیش از فضای باقیمانده است' })
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
const handleEdit = (address: AddressData) => {
|
||||
setSelectedAddress(address)
|
||||
setIsEditMode(true)
|
||||
reset({
|
||||
title: address.title,
|
||||
username: address.displayName,
|
||||
password: '',
|
||||
domainId: address.domain.id,
|
||||
quotaMB: address.emailQuota ? Math.round(address.emailQuota / (1024 * 1024)) : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const handleDelete = (address: AddressData) => {
|
||||
setAddressToDelete(address)
|
||||
setIsDeleteModalOpen(true)
|
||||
}
|
||||
|
||||
const handleConfirmDelete = () => {
|
||||
if (addressToDelete) {
|
||||
deleteAddress(addressToDelete.id, {
|
||||
onSuccess: (data) => {
|
||||
toast(data?.data?.message || 'آدرس با موفقیت حذف شد', 'success')
|
||||
setIsDeleteModalOpen(false)
|
||||
setAddressToDelete(null)
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error?.message[0] || 'خطا در حذف', 'error')
|
||||
setIsDeleteModalOpen(false)
|
||||
setAddressToDelete(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleCloseDeleteModal = () => {
|
||||
setIsDeleteModalOpen(false)
|
||||
setAddressToDelete(null)
|
||||
}
|
||||
|
||||
const handleCancelEdit = () => {
|
||||
setSelectedAddress(null)
|
||||
setIsEditMode(false)
|
||||
reset({
|
||||
title: '',
|
||||
username: '',
|
||||
password: '',
|
||||
domainId: domain?.data?.domain?.id,
|
||||
quotaMB: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const getRowActions = (address: AddressData): RowActionItem[] => [
|
||||
{
|
||||
label: 'ویرایش',
|
||||
icon: <Edit2 color='black' size={16} />,
|
||||
onClick: () => handleEdit(address),
|
||||
},
|
||||
{
|
||||
label: 'حذف',
|
||||
icon: <Trash color='red' size={16} />,
|
||||
onClick: () => handleDelete(address),
|
||||
className: 'text-red-600 hover:bg-red-50'
|
||||
}
|
||||
]
|
||||
|
||||
const columns: ColumnType<AddressData>[] = [
|
||||
{
|
||||
title: t('setting.address_title'),
|
||||
@@ -64,7 +169,6 @@ const Address: FC = () => {
|
||||
key: 'quota',
|
||||
width: '300px',
|
||||
render: (item: AddressData) => {
|
||||
|
||||
return (
|
||||
<ProgressBarEmail key={item.id} item={item} />
|
||||
)
|
||||
@@ -80,6 +184,18 @@ const Address: FC = () => {
|
||||
<ToggleStatus defaultStatus={item.isActive} id={item.id} />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'عملیات',
|
||||
key: 'actions',
|
||||
width: '120px',
|
||||
align: 'center',
|
||||
render: (item: AddressData) => (
|
||||
<RowActionsDropdown
|
||||
key={item.id}
|
||||
actions={getRowActions(item)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
@@ -89,24 +205,52 @@ const Address: FC = () => {
|
||||
{ value: 'inactive', label: t('setting.inactive') }
|
||||
]
|
||||
|
||||
const onSubmit = (params: CreateAddressType) => {
|
||||
createAddress(params, {
|
||||
onSuccess: (data) => {
|
||||
toast(data?.data?.message, 'success')
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
const onSubmit = (params: FormData) => {
|
||||
if (!validateForm(params)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isEditMode && selectedAddress) {
|
||||
const updateParams: UpdateAddressType = {
|
||||
title: params.title,
|
||||
password: params.password || undefined,
|
||||
quota: params.quotaMB ? params.quotaMB * 1024 * 1024 : undefined,
|
||||
}
|
||||
})
|
||||
updateAddress({ id: selectedAddress.id, params: updateParams }, {
|
||||
onSuccess: (data) => {
|
||||
toast(data?.data?.message || 'آپدیت با موفقیت انجام شد', 'success')
|
||||
handleCancelEdit()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error?.message[0] || 'خطا در آپدیت', 'error')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const createParams: CreateAddressType = {
|
||||
...params,
|
||||
quota: params.quotaMB ? params.quotaMB * 1024 * 1024 : undefined,
|
||||
}
|
||||
createAddress(createParams, {
|
||||
onSuccess: (data) => {
|
||||
toast(data?.data?.message, 'success')
|
||||
reset()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (domain?.data?.domain?.id) {
|
||||
if (domain?.data?.domain?.id && !isEditMode) {
|
||||
setValue('domainId', domain?.data?.domain?.id)
|
||||
}
|
||||
}, [domain])
|
||||
|
||||
}, [domain, isEditMode])
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentPage(1)
|
||||
}, [search, status])
|
||||
|
||||
return (
|
||||
<div className='flex xl:flex-row flex-col-reverse gap-8 mt-8'>
|
||||
@@ -136,26 +280,28 @@ const Address: FC = () => {
|
||||
columns={columns}
|
||||
data={addresses?.data?.users}
|
||||
pagination={{
|
||||
currentPage: 1,
|
||||
totalPages: 6,
|
||||
onPageChange: (page) => console.log('Page:', page)
|
||||
currentPage: currentPage,
|
||||
totalPages: addresses?.data?.pager?.totalPages || 1,
|
||||
onPageChange: (page) => setCurrentPage(page)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='xl:w-[350px] w-full bg-white rounded-4xl p-8'>
|
||||
<div>
|
||||
{t('setting.add_address')}
|
||||
</div>
|
||||
|
||||
{/* <div className='mt-8 flex items-center justify-between'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div>
|
||||
{t('setting.status')}
|
||||
{isEditMode ? 'ویرایش آدرس' : t('setting.add_address')}
|
||||
</div>
|
||||
<div className='dltr pt-2'>
|
||||
<Switch />
|
||||
</div>
|
||||
</div> */}
|
||||
{isEditMode && (
|
||||
<Button
|
||||
variant='secondary'
|
||||
onClick={handleCancelEdit}
|
||||
className='px-4 w-fit text-xs'
|
||||
>
|
||||
انصراف
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Input
|
||||
@@ -169,7 +315,7 @@ const Address: FC = () => {
|
||||
<Input
|
||||
label={t('setting.title1')}
|
||||
{...register('title')}
|
||||
error_text={errors.title?.message}
|
||||
error_text={errors.title?.message as string}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -178,7 +324,8 @@ const Address: FC = () => {
|
||||
label={t('setting.username')}
|
||||
className='text-left'
|
||||
{...register('username')}
|
||||
error_text={errors.username?.message}
|
||||
error_text={errors.username?.message as string}
|
||||
readOnly={isEditMode}
|
||||
/>
|
||||
<div className='absolute flex items-center dltr text-xs z-10 right-[1px] top-[29px] rounded-r-2.5 h-[38px] bg-[#EBEEF5] px-3'>
|
||||
@ {domain?.data?.domain?.name}
|
||||
@@ -187,38 +334,65 @@ const Address: FC = () => {
|
||||
|
||||
<div className='mt-6'>
|
||||
<Input
|
||||
label={t('setting.password')}
|
||||
label='حجم ایمیل (مگابایت)'
|
||||
type='number'
|
||||
{...register('quotaMB', { valueAsNumber: true })}
|
||||
error_text={errors.quotaMB?.message as string}
|
||||
placeholder='حجم ایمیل را به مگابایت وارد کنید'
|
||||
/>
|
||||
<div className='text-xs text-gray-500 mt-1'>
|
||||
فضای باقیمانده: {spaceData?.data?.quota ? Math.round((spaceData.data.quota.totalInGB - spaceData.data.quota.usedInGB) * 1024) : 0} مگابایت
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<Input
|
||||
label={isEditMode ? 'رمز عبور جدید' : t('setting.password')}
|
||||
type='password'
|
||||
{...register('password')}
|
||||
error_text={errors.password?.message}
|
||||
error_text={errors.password?.message as string}
|
||||
placeholder={isEditMode ? 'برای تغییر رمز عبور وارد کنید' : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-2 text-xs'>
|
||||
<div>رمز عبور میبایست:</div>
|
||||
<ul className='list-disc pr-3 mt-1 flex flex-col gap-1'>
|
||||
<li>حداقل ۸ کاراکتر باشد</li>
|
||||
<li>ترکیبی از حروف کوچک و بزرگ باشد</li>
|
||||
<li>شامل اعداد باشد</li>
|
||||
<li>شامل کاراکتر های خاص (نماد ها) باشد</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
{!isEditMode && (
|
||||
<div className='mt-2 text-xs'>
|
||||
<div>رمز عبور میبایست:</div>
|
||||
<ul className='list-disc pr-3 mt-1 flex flex-col gap-1'>
|
||||
<li>حداقل ۸ کاراکتر باشد</li>
|
||||
<li>ترکیبی از حروف کوچک و بزرگ باشد</li>
|
||||
<li>شامل اعداد باشد</li>
|
||||
<li>شامل کاراکتر های خاص (نماد ها) باشد</li>
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className='mt-9 flex justify-end'>
|
||||
<Button
|
||||
className='w-fit px-20'
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
loading={isPending}
|
||||
loading={isPending || isUpdating}
|
||||
>
|
||||
<div className='flex gap-2'>
|
||||
<TickCircle size={20} color='white' />
|
||||
<div>
|
||||
{t('setting.create')}
|
||||
{isEditMode ? 'آپدیت' : t('setting.create')}
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ModalConfrim
|
||||
isOpen={isDeleteModalOpen}
|
||||
close={handleCloseDeleteModal}
|
||||
onConfrim={handleConfirmDelete}
|
||||
title={t('confrim.delete_address_title')}
|
||||
label={addressToDelete ? t('confrim.delete_address_message').replace('{email}', addressToDelete.emailAddress) : ''}
|
||||
isLoading={false}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import * as api from "../service/Service";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { UpdateAddressType } from "../types/Types";
|
||||
|
||||
export const useGetAddress = (search: string, status: string) => {
|
||||
export const useGetAddress = (
|
||||
search: string,
|
||||
status: string,
|
||||
page: number = 1,
|
||||
limit: number = 10
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: ["address", search, status],
|
||||
queryFn: () => api.getAddress(search, status),
|
||||
queryKey: ["address", search, status, page, limit],
|
||||
queryFn: () => api.getAddress(search, status, page, limit),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -20,7 +26,32 @@ export const useCreateAddress = () => {
|
||||
};
|
||||
|
||||
export const useToggleStatus = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: api.toggleStatus,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["address"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateAddress = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, params }: { id: string; params: UpdateAddressType }) =>
|
||||
api.updateAddress(id, params),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["address"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteAddress = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: api.deleteAddress,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["address"] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import axios from "@/config/axios";
|
||||
import { CreateAddressType } from "../types/Types";
|
||||
import { CreateAddressType, UpdateAddressType } from "../types/Types";
|
||||
|
||||
export const getAddress = async (search: string, status: string) => {
|
||||
export const getAddress = async (
|
||||
search: string,
|
||||
status: string,
|
||||
page: number = 1,
|
||||
limit: number = 10
|
||||
) => {
|
||||
const query = new URLSearchParams();
|
||||
if (search) query.set("q", search);
|
||||
if (status && status !== "all")
|
||||
query.set("isActive", status === "active" ? "1" : "0");
|
||||
query.set("page", page.toString());
|
||||
query.set("limit", limit.toString());
|
||||
const { data } = await axios.get(`/users?${query.toString()}`);
|
||||
return data;
|
||||
};
|
||||
@@ -19,3 +26,13 @@ export const toggleStatus = async (id: string) => {
|
||||
const { data } = await axios.patch(`/users/${id}/toggle-status`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const updateAddress = async (id: string, params: UpdateAddressType) => {
|
||||
const { data } = await axios.patch(`/users/${id}`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const deleteAddress = async (id: string) => {
|
||||
const { data } = await axios.delete(`/users/${id}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,15 @@ export type CreateAddressType = {
|
||||
password: string;
|
||||
domainId: string;
|
||||
title: string;
|
||||
quota?: number;
|
||||
};
|
||||
|
||||
export type UpdateAddressType = {
|
||||
password?: string;
|
||||
displayName?: string;
|
||||
quota?: number;
|
||||
aliases?: string[];
|
||||
title?: string;
|
||||
};
|
||||
|
||||
export interface AddressData extends Record<string, unknown> {
|
||||
@@ -56,3 +65,15 @@ export interface AddressData extends Record<string, unknown> {
|
||||
business: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AddressResponse {
|
||||
data: {
|
||||
users: AddressData[];
|
||||
pager: {
|
||||
page: number;
|
||||
limit: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,46 +1,39 @@
|
||||
import Input from '@/components/Input'
|
||||
import RadioGroup from '@/components/RadioGroup'
|
||||
import { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useMailServerData } from './hooks/useMailServerData'
|
||||
|
||||
const Domain: FC = () => {
|
||||
const MailServer: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className='bg-white rounded-4xl p-8 mt-9'>
|
||||
<div>
|
||||
<div className='flex xl:flex-row gap-4 flex-col justify-between xl:items-end border-b pb-10 broder-border'>
|
||||
<div className='flex-1'>
|
||||
<div className='xl:text-lg'>
|
||||
{t('setting.setting_fetch')}
|
||||
</div>
|
||||
<div className='xl:text-sm text-xs text-description mt-1.5'>
|
||||
{t('setting.setting_fetch_description')}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex-1'>
|
||||
<RadioGroup
|
||||
items={[
|
||||
{
|
||||
label: '۱ دقیقه',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: '۲ دقیقه',
|
||||
value: '2'
|
||||
},
|
||||
{
|
||||
label: '۳ دقیقه',
|
||||
value: '3'
|
||||
}
|
||||
]}
|
||||
onChange={() => null}
|
||||
selected='1'
|
||||
/>
|
||||
</div>
|
||||
const { data, isLoading, error } = useMailServerData();
|
||||
|
||||
console.log('Mail Server Data:', data); // برای debug
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className='bg-white rounded-4xl p-8 mt-9'>
|
||||
<div className='text-center py-8'>
|
||||
در حال بارگذاری...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className='bg-white rounded-4xl p-8 mt-9'>
|
||||
<div className='text-center py-8 text-red-500'>
|
||||
خطا در بارگذاری اطلاعات
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='bg-white rounded-4xl p-8 mt-9'>
|
||||
{/* SMTP Settings */}
|
||||
<div className='mt-9'>
|
||||
<div className='flex xl:flex-row gap-4 flex-col justify-between pb-10'>
|
||||
<div className='flex xl:flex-row gap-4 flex-col justify-between pb-10'>
|
||||
<div className='flex-1'>
|
||||
<div className='xl:text-lg'>
|
||||
{t('setting.setting_smtp')}
|
||||
@@ -53,20 +46,57 @@ const Domain: FC = () => {
|
||||
<div className='flex-1'>
|
||||
<Input
|
||||
label={t('setting.smtp_server')}
|
||||
value={data?.data?.setupInfo?.smtpSettings?.server || ''}
|
||||
readOnly
|
||||
/>
|
||||
<div className='mt-8'>
|
||||
<Input
|
||||
label={t('setting.smtp_port')}
|
||||
value={data?.data?.setupInfo?.smtpSettings?.port?.toString() || ''}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-8'>
|
||||
<Input
|
||||
label={t('setting.username')}
|
||||
label={t('setting.encryption')}
|
||||
value={data?.data?.setupInfo?.smtpSettings?.encryption || ''}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* IMAP Settings */}
|
||||
<div className='mt-9 border-t pt-9'>
|
||||
<div className='flex xl:flex-row gap-4 flex-col justify-between pb-10'>
|
||||
<div className='flex-1'>
|
||||
<div className='xl:text-lg'>
|
||||
{t('setting.setting_imap')}
|
||||
</div>
|
||||
<div className='xl:text-sm text-xs text-description mt-1.5'>
|
||||
{t('setting.setting_imap_description')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex-1'>
|
||||
<Input
|
||||
label={t('setting.imap_server')}
|
||||
value={data?.data?.setupInfo?.imapSettings?.server || ''}
|
||||
readOnly
|
||||
/>
|
||||
<div className='mt-8'>
|
||||
<Input
|
||||
label={t('setting.imap_port')}
|
||||
value={data?.data?.setupInfo?.imapSettings?.port?.toString() || ''}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-8'>
|
||||
<Input
|
||||
label={t('setting.password')}
|
||||
label={t('setting.encryption')}
|
||||
value={data?.data?.setupInfo?.imapSettings?.encryption || ''}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -76,4 +106,4 @@ const Domain: FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export default Domain
|
||||
export default MailServer
|
||||
@@ -0,0 +1,9 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getServerInfo } from "../service/Service";
|
||||
|
||||
export const useMailServerData = () => {
|
||||
return useQuery({
|
||||
queryKey: ["mail-server-data"],
|
||||
queryFn: getServerInfo,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
import axios from "@/config/axios";
|
||||
|
||||
export const getServerInfo = async () => {
|
||||
const { data } = await axios.get("/mail-server/info");
|
||||
return data;
|
||||
};
|
||||
Reference in New Issue
Block a user