domain and mail server

This commit is contained in:
hamid zarghami
2025-06-23 13:27:02 +03:30
parent 0aab282d6f
commit 95ece3691c
5 changed files with 209 additions and 197 deletions
+5 -11
View File
@@ -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' && (
+2 -1
View File
@@ -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
View File
@@ -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": {
+120 -52
View File
@@ -1,77 +1,145 @@
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() const { t } = useTranslation()
return (
<div className='bg-white rounded-4xl p-8 mt-9'> // نمونه داده‌های DNS records طبق تصویر
<div className=''> const dnsRecords: DNSRecord[] = [
<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: '۱ دقیقه', id: 1,
value: '1' status: 'verified',
type: 'TXT',
name: 'example.com',
value: 'v=spf1 include:_spf.example.com ~all',
number: 1
}, },
{ {
label: '۲ دقیقه', id: 2,
value: '2' status: 'verified',
type: 'MX | Priority:10',
name: 'example.com',
value: '10 mail.example.com',
number: 1
}, },
{ {
label: '۳ دقیقه', id: 3,
value: '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
} }
]} ]
onChange={() => null}
selected='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> </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> </div>
<div className='mt-9'> )
<div className='flex justify-between items-start pb-10'> },
<div className='flex-1'> ]
return (
<div>
<div className='flex justify-between'>
<div>
<div className='text-lg'> <div className='text-lg'>
{t('setting.setting_smtp')} {t('setting.record_dns')}
</div> </div>
<div className='text-sm text-description mt-1.5'> <p className='mt-2 text-sm font-extralight'>
{t('setting.setting_smtp_description')} {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> </div>
<div className='flex-1'>
<Input <div className='mt-9'>
label={t('setting.smtp_server')} <Table
/> columns={columns}
<div className='mt-8'> data={dnsRecords}
<Input className="!mt-0"
label={t('setting.smtp_port')}
/> />
</div> </div>
<div className='mt-8'>
<Input
label={t('setting.username')}
/>
</div>
<div className='mt-8'>
<Input
label={t('setting.password')}
/>
</div>
</div>
</div>
</div>
</div> </div>
) )
} }
+59 -114
View File
@@ -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 className='text-lg'>
{t('setting.setting_fetch')}
</div> </div>
<div className='text-sm text-description mt-1.5'>
<div className='grid grid-cols-1 gap-6'> {t('setting.setting_fetch_description')}
<div> </div>
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.server_type')}</label> </div>
<div className='flex gap-4 mb-4'> <div className='flex-1'>
<label className='flex items-center gap-2 cursor-pointer'> <RadioGroup
<input items={[
type="radio" {
checked={serverType === 'smtp'} label: '۱ دقیقه',
onChange={() => setServerType('smtp')} value: '1'
className='h-4 w-4 accent-primary' },
{
label: '۲ دقیقه',
value: '2'
},
{
label: '۳ دقیقه',
value: '3'
}
]}
onChange={() => null}
selected='1'
/> />
<span>SMTP</span> </div>
</label> </div>
<label className='flex items-center gap-2 cursor-pointer'> </div>
<input <div className='mt-9'>
type="radio" <div className='flex justify-between items-start pb-10'>
checked={serverType === 'api'} <div className='flex-1'>
onChange={() => setServerType('api')} <div className='text-lg'>
className='h-4 w-4 accent-primary' {t('setting.setting_smtp')}
/> </div>
<span>API</span> <div className='text-sm text-description mt-1.5'>
</label> {t('setting.setting_smtp_description')}
</div> </div>
</div> </div>
{serverType === 'smtp' && ( <div className='flex-1'>
<> <Input
<div className='grid grid-cols-2 gap-4'> label={t('setting.smtp_server')}
<div> />
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.host')}</label> <div className='mt-8'>
<input <Input
type="text" label={t('setting.smtp_port')}
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>
<div> <div className='mt-8'>
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.port')}</label> <Input
<input label={t('setting.username')}
type="text" />
value={port} </div>
onChange={(e) => setPort(e.target.value)} <div className='mt-8'>
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' <Input
placeholder='587' label={t('setting.password')}
/> />
</div> </div>
</div> </div>
<div>
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.username')}</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(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='username@example.com'
/>
</div> </div>
<div>
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.password')}</label>
<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>
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.encryption')}</label>
<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>
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.api_url')}</label>
<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