This commit is contained in:
hamid zarghami
2025-06-22 22:48:15 +03:30
parent 037113ee09
commit 0aab282d6f
12 changed files with 344 additions and 113 deletions
+45
View File
@@ -0,0 +1,45 @@
import Button from '@/components/Button'
import { ArrowRight, InfoCircle, SmsNotification, Star, VolumeMute, More, DocumentDownload, ArchiveTick, Printer, Trash } from 'iconsax-react'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
const Header: FC = () => {
const { t } = useTranslation()
return (
<div className='flex justify-between items-center border-b border-border pb-6'>
<div className='flex gap-6'>
<ArrowRight size={24} color='#8C90A3' />
<SmsNotification size={24} color='black' />
<ArchiveTick size={24} color='black' />
<InfoCircle size={24} color='black' />
<Star size={24} color='black' />
<VolumeMute size={24} color='black' />
<Trash size={24} color='#CD0000' />
<More size={24} color='black' className='rotate-90' />
</div>
<div className='flex gap-2'>
<Button
variant='secondary'
>
<div className='flex gap-2 items-center px-5'>
<DocumentDownload size={20} color='black' />
<span className='pt-0.5'>{t('mail.download')}</span>
</div>
</Button>
<Button
variant='secondary'
>
<div className='flex gap-2 items-center px-5'>
<Printer size={20} color='black' />
<span className='pt-0.5'>{t('mail.print')}</span>
</div>
</Button>
</div>
</div>
)
}
export default Header
+135
View File
@@ -0,0 +1,135 @@
import Button from '@/components/Button'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AvatarImage from '@/assets/images/avatar.svg'
import { DocumentDownload, EmojiHappy } from 'iconsax-react'
import ForwardIcon from '@/assets/images/forward.svg'
import AnswerIcon from '@/assets/images/answer.svg'
import Header from './Components/Header'
import ReactQuill from 'react-quill-new';
import Select from '@/components/Select'
const DetailEmail: FC = () => {
const { t } = useTranslation()
const [showAnswer, setShowAnswer] = useState<boolean>(false)
const [value, setValue] = useState<string>('')
return (
<div className='bg-white rounded-4xl p-8'>
<Header />
<div className='mt-6 flex justify-between items-center'>
<div className='flex gap-4 items-center'>
<div className='flex gap-1 items-center'>
<div className='font-extralight '>{t('mail.subject')}</div>
<div>لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ</div>
</div>
<Button
label='دریافتی ها'
className='bg-[#ECEFF6] text-description w-fit px-6 rounded-full'
/>
<Button
label='الویت زیاد'
className='bg-[#FFE9E9] text-[#CD0000] w-fit px-6 rounded-full'
/>
</div>
<div className='flex text-description text-sm'>
۵ اسفندماه ۱۴۰۳، ساعت ۱۲:۰۰
</div>
</div>
<div className='mt-6 flex gap-4 items-center'>
<img src={AvatarImage} className='size-10' />
<div>مهرداد مظفری</div>
</div>
<p className='mt-8 text-sm leading-7 font-light'>
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد کتابهای زیادی در شصت و سه درصد گذشته حال و آیندهلورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد کتابهای زیادی در شصت و سه درصد گذشته حال و آینده
</p>
<div className='mt-10 text-sm font-bold'>
{t('mail.attachments')}
</div>
<div className='flex border-b border-border pb-10 mt-4 flex-wrap gap-2.5 items-center'>
<div className='bg-[#EBEDF5] text-sm flex gap-2 h-10 rounded-full items-center px-4'>
<span>Lorem Ipsum.pdf</span>
<DocumentDownload size={20} color='#0038FF' />
</div>
</div>
{
!showAnswer ?
<div className='flex mt-9 justify-end'>
<div className='flex gap-5 text-sm'>
<EmojiHappy size={24} color='black' />
<div onClick={() => setShowAnswer(true)} className='flex gap-2 cursor-pointer items-center border-r border-border pr-5'>
<img src={AnswerIcon} className='w-[17px]' />
<div>{t('mail.answer')}</div>
</div>
<div className='flex gap-2 cursor-pointer items-center border-r border-border pr-5'>
<img src={ForwardIcon} className='w-[17px]' />
<div>{t('mail.forward')}</div>
</div>
</div>
</div>
:
<div className='mt-9 flex gap-4'>
<img src={AvatarImage} className='size-10' />
<div className='w-full'>
<div className='mb-2 text-sm'>
{t('mail.message')}
</div>
<ReactQuill
modules={{
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ list: 'ordered' }, { list: 'bullet' }],
['bold', 'italic', 'underline'],
['link', 'image'],
[{ align: [] }],
['clean']
]
}}
theme="snow"
value={value}
onChange={setValue}
style={{ minHeight: '120px', width: '100%' }}
className='text-sm'
/>
<div>
<div className='flex flex-col gap-4 sm:flex-row pt-4'>
<div>
<Select
items={[]}
placeholder={t('new_message.select_priority')}
className='xl:w-[190px]'
/>
</div>
<div className='flex-1 justify-end flex gap-3'>
<Button
className='!w-full sm:!w-fit px-6 md:px-10 bg-white text-black border border-primary order-2 sm:order-1'
label={t('new_message.draft')}
/>
<Button
className='w-full sm:w-fit px-6 md:px-10 order-1 sm:order-2'
label={t('new_message.send')}
/>
</div>
</div>
</div>
</div>
</div>
}
</div>
)
}
export default DetailEmail
+63 -112
View File
@@ -1,123 +1,74 @@
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 { Add, CloseCircle } from 'iconsax-react'
const Domain: FC = () => {
const { t } = useTranslation()
const [domains, setDomains] = useState<string[]>(['example.com'])
const [newDomain, setNewDomain] = useState('')
const [error, setError] = useState('')
const handleAddDomain = () => {
if (!newDomain) {
setError(t('setting.domain_required'))
return
}
// Simple domain validation
const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/
if (!domainRegex.test(newDomain)) {
setError(t('setting.invalid_domain'))
return
}
if (domains.includes(newDomain)) {
setError(t('setting.domain_exists'))
return
}
setDomains([...domains, newDomain])
setNewDomain('')
setError('')
}
const handleRemoveDomain = (domain: string) => {
setDomains(domains.filter(d => d !== domain))
}
return (
<div className='bg-white rounded-4xl p-8'>
<div className='flex justify-between mb-8'>
<h2 className='text-xl font-semibold'>{t('setting.domain')}</h2>
<button className='bg-primary text-white px-6 py-2 rounded-lg'>{t('common.save')}</button>
</div>
<div className='mb-6'>
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.add_domain')}</label>
<div className='flex gap-2'>
<input
type="text"
value={newDomain}
onChange={(e) => {
setNewDomain(e.target.value)
setError('')
}}
className={`flex-1 px-4 py-2 border ${error ? 'border-red-500' : 'border-gray-300'} rounded-lg focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent`}
placeholder='domain.com'
/>
<button
onClick={handleAddDomain}
className='bg-primary text-white px-4 py-2 rounded-lg'
>
<Add size={20} />
</button>
</div>
{error && <p className='text-red-500 text-sm mt-1'>{error}</p>}
</div>
<div>
<h3 className='text-md font-medium mb-4'>{t('setting.your_domains')}</h3>
<div className='space-y-3'>
{domains.map((domain, index) => (
<div key={index} className='flex items-center justify-between p-4 bg-gray-50 rounded-lg'>
<div>
<p className='font-medium'>{domain}</p>
<p className='text-sm text-gray-500'>
{index === 0 ? t('setting.primary_domain') : t('setting.additional_domain')}
</p>
</div>
<div className='flex items-center gap-3'>
<div className={`px-2 py-1 rounded-full text-xs ${index === 0 ? 'bg-green-100 text-green-600' : 'bg-blue-100 text-blue-600'}`}>
{index === 0 ? t('setting.verified') : t('setting.pending')}
</div>
{index !== 0 && (
<button
onClick={() => handleRemoveDomain(domain)}
className='text-gray-400 hover:text-red-500'
>
<CloseCircle size={20} />
</button>
)}
</div>
<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='mt-8 p-4 bg-blue-50 rounded-lg'>
<h3 className='text-md font-medium text-blue-700 mb-2'>{t('setting.dns_records')}</h3>
<p className='text-sm text-blue-600 mb-4'>{t('setting.dns_instructions')}</p>
<div className='bg-white p-4 rounded-lg border border-blue-100'>
<div className='grid grid-cols-3 gap-4 text-sm font-medium text-gray-500 pb-2 border-b'>
<div>{t('setting.type')}</div>
<div>{t('setting.hostname')}</div>
<div>{t('setting.value')}</div>
</div>
<div className='grid grid-cols-3 gap-4 text-sm py-4 border-b'>
<div>MX</div>
<div>@</div>
<div>mail.example.com</div>
</div>
<div className='grid grid-cols-3 gap-4 text-sm py-4 border-b'>
<div>TXT</div>
<div>@</div>
<div className='break-all'>v=spf1 include:spf.example.com ~all</div>
</div>
<div className='grid grid-cols-3 gap-4 text-sm py-4'>
<div>CNAME</div>
<div>mail</div>
<div>mailserver.example.com</div>
<div className='flex-1'>
<Input
label={t('setting.smtp_server')}
/>
<div className='mt-8'>
<Input
label={t('setting.smtp_port')}
/>
</div>
<div className='mt-8'>
<Input
label={t('setting.username')}
/>
</div>
<div className='mt-8'>
<Input
label={t('setting.password')}
/>
</div>
</div>
</div>
</div>
@@ -125,4 +76,4 @@ const Domain: FC = () => {
)
}
export default Domain
export default Domain