domain and mail server
This commit is contained in:
@@ -125,7 +125,7 @@ const Table = <T extends RowDataType>({
|
|||||||
<thead className={`h-[60px] md:h-[69px] bg-white text-sm text-[#8C90A3] ${headerClassName}`}>
|
<thead className={`h-[60px] md:h-[69px] bg-white text-sm text-[#8C90A3] ${headerClassName}`}>
|
||||||
<tr>
|
<tr>
|
||||||
{selectable && (
|
{selectable && (
|
||||||
<th className="px-3 md:px-6 py-3 md:py-4 w-8 md:w-10 relative z-[5]">
|
<th className="px-3 md:px-6 py-3 md:py-4 w-8 md:w-10 relative z-[5] first:rounded-tr-2xl md:first:rounded-tr-3xl">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={data.length > 0 && selectedRows.length === data.length}
|
checked={data.length > 0 && selectedRows.length === data.length}
|
||||||
onCheckedChange={handleSelectAll}
|
onCheckedChange={handleSelectAll}
|
||||||
@@ -133,19 +133,13 @@ const Table = <T extends RowDataType>({
|
|||||||
</th>
|
</th>
|
||||||
)}
|
)}
|
||||||
{columns.map((col) => (
|
{columns.map((col) => (
|
||||||
<th
|
<Td key={col.key} text={col.title} />
|
||||||
key={col.key}
|
|
||||||
className={getCellClassName(col)}
|
|
||||||
style={{ width: col.width }}
|
|
||||||
>
|
|
||||||
<Td text={col.title} />
|
|
||||||
</th>
|
|
||||||
))}
|
))}
|
||||||
{rowActions && (
|
{rowActions && (
|
||||||
<th className="px-3 md:px-6 py-3 md:py-4 w-8 md:w-10"></th>
|
<th className="px-3 md:px-6 py-3 md:py-4 w-8 md:w-10 rounded-tl-2xl md:rounded-tl-3xl"></th>
|
||||||
)}
|
)}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead >
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -166,7 +160,7 @@ const Table = <T extends RowDataType>({
|
|||||||
<div className={`relative mt-6 md:mt-9 w-full ${className}`}>
|
<div className={`relative mt-6 md:mt-9 w-full ${className}`}>
|
||||||
{(actionsPosition === 'top' || actionsPosition === 'above-header') && renderActions()}
|
{(actionsPosition === 'top' || actionsPosition === 'above-header') && renderActions()}
|
||||||
|
|
||||||
<div className="overflow-x-auto overflow-hidden rounded-b-2xl md:rounded-b-3xl bg-white shadow-sm">
|
<div className="overflow-x-auto overflow-hidden rounded-2xl md:rounded-3xl bg-white shadow-sm">
|
||||||
<table className="w-full text-sm border-collapse min-w-[600px]">
|
<table className="w-full text-sm border-collapse min-w-[600px]">
|
||||||
{renderHeader()}
|
{renderHeader()}
|
||||||
{actionsPosition === 'header-replace' && (
|
{actionsPosition === 'header-replace' && (
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ interface Props {
|
|||||||
text: string | number | ReactNode,
|
text: string | number | ReactNode,
|
||||||
children?: ReactNode,
|
children?: ReactNode,
|
||||||
dir?: string,
|
dir?: string,
|
||||||
|
className?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Td: FC<Props> = (props: Props) => {
|
const Td: FC<Props> = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<td className='px-3 md:px-6 py-3 md:py-4 whitespace-nowrap text-xs' style={{ direction: props.dir === "ltr" ? "ltr" : "rtl" }}>
|
<td className={`px-3 md:px-6 py-3 md:py-4 whitespace-nowrap text-xs ${props.className}`} style={{ direction: props.dir === "ltr" ? "ltr" : "rtl" }}>
|
||||||
{
|
{
|
||||||
props.text ?
|
props.text ?
|
||||||
props.text
|
props.text
|
||||||
|
|||||||
+5
-1
@@ -92,7 +92,11 @@
|
|||||||
"smtp_server": "SMTP سرور",
|
"smtp_server": "SMTP سرور",
|
||||||
"smtp_port": "SMTP پورت",
|
"smtp_port": "SMTP پورت",
|
||||||
"username": "نام کاربری",
|
"username": "نام کاربری",
|
||||||
"password": "پسورد"
|
"password": "پسورد",
|
||||||
|
"record_dns": "رکوردهای DNS",
|
||||||
|
"record_dns_description": "در این قسمت میتوانید وضعیت رکوردهای DNS سرویس ایمیل خود را بررسی کنید.",
|
||||||
|
"your_domain": "دامنه شما",
|
||||||
|
"submit": "ثبت"
|
||||||
},
|
},
|
||||||
"upload": "آپلود",
|
"upload": "آپلود",
|
||||||
"new_message": {
|
"new_message": {
|
||||||
|
|||||||
@@ -1,79 +1,147 @@
|
|||||||
|
import Button from '@/components/Button'
|
||||||
import Input from '@/components/Input'
|
import Input from '@/components/Input'
|
||||||
import RadioGroup from '@/components/RadioGroup'
|
import Table from '@/components/Table'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { TickCircle } from 'iconsax-react'
|
||||||
|
import { Copy } from 'iconsax-react'
|
||||||
|
|
||||||
|
interface DNSRecord extends Record<string, unknown> {
|
||||||
|
id: number
|
||||||
|
status: 'verified' | 'pending'
|
||||||
|
type: string
|
||||||
|
name: string
|
||||||
|
value: string
|
||||||
|
number: number
|
||||||
|
}
|
||||||
|
|
||||||
const Domain: FC = () => {
|
const Domain: FC = () => {
|
||||||
const { t } = useTranslation()
|
|
||||||
return (
|
|
||||||
<div className='bg-white rounded-4xl p-8 mt-9'>
|
|
||||||
<div className=''>
|
|
||||||
<div className='flex justify-between items-end border-b pb-10 broder-border'>
|
|
||||||
<div className='flex-1'>
|
|
||||||
<div className='text-lg'>
|
|
||||||
{t('setting.setting_fetch')}
|
|
||||||
</div>
|
|
||||||
<div className='text-sm 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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='mt-9'>
|
|
||||||
<div className='flex justify-between items-start pb-10'>
|
|
||||||
<div className='flex-1'>
|
|
||||||
<div className='text-lg'>
|
|
||||||
{t('setting.setting_smtp')}
|
|
||||||
</div>
|
|
||||||
<div className='text-sm text-description mt-1.5'>
|
|
||||||
{t('setting.setting_smtp_description')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex-1'>
|
const { t } = useTranslation()
|
||||||
<Input
|
|
||||||
label={t('setting.smtp_server')}
|
// نمونه دادههای DNS records طبق تصویر
|
||||||
/>
|
const dnsRecords: DNSRecord[] = [
|
||||||
<div className='mt-8'>
|
{
|
||||||
<Input
|
id: 1,
|
||||||
label={t('setting.smtp_port')}
|
status: 'verified',
|
||||||
/>
|
type: 'TXT',
|
||||||
</div>
|
name: 'example.com',
|
||||||
<div className='mt-8'>
|
value: 'v=spf1 include:_spf.example.com ~all',
|
||||||
<Input
|
number: 1
|
||||||
label={t('setting.username')}
|
},
|
||||||
/>
|
{
|
||||||
</div>
|
id: 2,
|
||||||
<div className='mt-8'>
|
status: 'verified',
|
||||||
<Input
|
type: 'MX | Priority:10',
|
||||||
label={t('setting.password')}
|
name: 'example.com',
|
||||||
/>
|
value: '10 mail.example.com',
|
||||||
</div>
|
number: 1
|
||||||
</div>
|
},
|
||||||
</div>
|
{
|
||||||
</div>
|
id: 3,
|
||||||
|
status: 'verified',
|
||||||
|
type: 'Cname | No CDN',
|
||||||
|
name: 'example.com',
|
||||||
|
value: 'v=spf1 include:_spf.example.com ~all',
|
||||||
|
number: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
status: 'verified',
|
||||||
|
type: 'TXT',
|
||||||
|
name: 'example.com',
|
||||||
|
value: 'v=spf1 include:_spf.example.com ~all',
|
||||||
|
number: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'شماره',
|
||||||
|
key: 'number',
|
||||||
|
width: '80px',
|
||||||
|
align: 'center' as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'مقدار رکورد',
|
||||||
|
key: 'value',
|
||||||
|
render: (record: DNSRecord) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
|
||||||
|
<span className="truncate max-w-[300px]">{record.value}</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'نام رکورد',
|
||||||
|
key: 'name',
|
||||||
|
render: (record: DNSRecord) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<button className="text-blue-500 hover:text-blue-700">
|
||||||
|
<Copy size={16} color='#0038FF' />
|
||||||
|
</button>
|
||||||
|
<span>{record.name}</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'نوع رکورد',
|
||||||
|
key: 'type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'وضعیت',
|
||||||
|
key: 'status',
|
||||||
|
render: (record: DNSRecord) => (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{record.status === 'verified' && (
|
||||||
|
<>
|
||||||
|
<TickCircle size={20} color="#22C55E" variant="Bold" />
|
||||||
|
<span className="text-green-500 text-xs">ثبت شده</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='flex justify-between'>
|
||||||
|
<div>
|
||||||
|
<div className='text-lg'>
|
||||||
|
{t('setting.record_dns')}
|
||||||
|
</div>
|
||||||
|
<p className='mt-2 text-sm font-extralight'>
|
||||||
|
{t('setting.record_dns_description')}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<Input
|
||||||
|
placeholder={t('setting.your_domain')}
|
||||||
|
className='w-[300px]'
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant='secondary'
|
||||||
|
className='w-fit px-14 border border-black'
|
||||||
|
label={t('setting.submit')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='mt-9'>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
data={dnsRecords}
|
||||||
|
className="!mt-0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Domain
|
export default Domain
|
||||||
@@ -1,134 +1,79 @@
|
|||||||
import { FC, useState } from 'react'
|
import Input from '@/components/Input'
|
||||||
|
import RadioGroup from '@/components/RadioGroup'
|
||||||
|
import { FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const MailServer: FC = () => {
|
const Domain: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [serverType, setServerType] = useState('smtp')
|
|
||||||
const [host, setHost] = useState('')
|
|
||||||
const [port, setPort] = useState('')
|
|
||||||
const [username, setUsername] = useState('')
|
|
||||||
const [password, setPassword] = useState('')
|
|
||||||
const [encryption, setEncryption] = useState('tls')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-white rounded-4xl p-8'>
|
<div className='bg-white rounded-4xl p-8 mt-9'>
|
||||||
<div className='flex justify-between mb-8'>
|
<div className=''>
|
||||||
<h2 className='text-xl font-semibold'>{t('setting.mail_server')}</h2>
|
<div className='flex justify-between items-end border-b pb-10 broder-border'>
|
||||||
<button className='bg-primary text-white px-6 py-2 rounded-lg'>{t('common.save')}</button>
|
<div className='flex-1'>
|
||||||
</div>
|
<div className='text-lg'>
|
||||||
|
{t('setting.setting_fetch')}
|
||||||
<div className='grid grid-cols-1 gap-6'>
|
</div>
|
||||||
<div>
|
<div className='text-sm text-description mt-1.5'>
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.server_type')}</label>
|
{t('setting.setting_fetch_description')}
|
||||||
<div className='flex gap-4 mb-4'>
|
</div>
|
||||||
<label className='flex items-center gap-2 cursor-pointer'>
|
</div>
|
||||||
<input
|
<div className='flex-1'>
|
||||||
type="radio"
|
<RadioGroup
|
||||||
checked={serverType === 'smtp'}
|
items={[
|
||||||
onChange={() => setServerType('smtp')}
|
{
|
||||||
className='h-4 w-4 accent-primary'
|
label: '۱ دقیقه',
|
||||||
/>
|
value: '1'
|
||||||
<span>SMTP</span>
|
},
|
||||||
</label>
|
{
|
||||||
<label className='flex items-center gap-2 cursor-pointer'>
|
label: '۲ دقیقه',
|
||||||
<input
|
value: '2'
|
||||||
type="radio"
|
},
|
||||||
checked={serverType === 'api'}
|
{
|
||||||
onChange={() => setServerType('api')}
|
label: '۳ دقیقه',
|
||||||
className='h-4 w-4 accent-primary'
|
value: '3'
|
||||||
/>
|
}
|
||||||
<span>API</span>
|
]}
|
||||||
</label>
|
onChange={() => null}
|
||||||
|
selected='1'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{serverType === 'smtp' && (
|
<div className='mt-9'>
|
||||||
<>
|
<div className='flex justify-between items-start pb-10'>
|
||||||
<div className='grid grid-cols-2 gap-4'>
|
<div className='flex-1'>
|
||||||
<div>
|
<div className='text-lg'>
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.host')}</label>
|
{t('setting.setting_smtp')}
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={host}
|
|
||||||
onChange={(e) => setHost(e.target.value)}
|
|
||||||
className='w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent'
|
|
||||||
placeholder='mail.example.com'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.port')}</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={port}
|
|
||||||
onChange={(e) => setPort(e.target.value)}
|
|
||||||
className='w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent'
|
|
||||||
placeholder='587'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className='text-sm text-description mt-1.5'>
|
||||||
|
{t('setting.setting_smtp_description')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div className='flex-1'>
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.username')}</label>
|
<Input
|
||||||
<input
|
label={t('setting.smtp_server')}
|
||||||
type="text"
|
/>
|
||||||
value={username}
|
<div className='mt-8'>
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
<Input
|
||||||
className='w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent'
|
label={t('setting.smtp_port')}
|
||||||
placeholder='username@example.com'
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='mt-8'>
|
||||||
<div>
|
<Input
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.password')}</label>
|
label={t('setting.username')}
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
className='w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent'
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className='mt-8'>
|
||||||
<div>
|
<Input
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.encryption')}</label>
|
label={t('setting.password')}
|
||||||
<select
|
|
||||||
value={encryption}
|
|
||||||
onChange={(e) => setEncryption(e.target.value)}
|
|
||||||
className='w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent'
|
|
||||||
>
|
|
||||||
<option value="none">{t('setting.none')}</option>
|
|
||||||
<option value="ssl">SSL</option>
|
|
||||||
<option value="tls">TLS</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button className='bg-blue-50 text-blue-600 px-4 py-2 rounded-lg mt-4'>{t('setting.test_connection')}</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{serverType === 'api' && (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.api_key')}</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
className='w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent'
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</div>
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.api_url')}</label>
|
</div>
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className='w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent'
|
|
||||||
placeholder='https://api.example.com/send'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MailServer
|
export default Domain
|
||||||
Reference in New Issue
Block a user