setting
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-checkbox": "^1.3.2",
|
"@radix-ui/react-checkbox": "^1.3.2",
|
||||||
|
"@radix-ui/react-switch": "^1.2.5",
|
||||||
"@react-three/drei": "^10.1.2",
|
"@react-three/drei": "^10.1.2",
|
||||||
"@react-three/fiber": "^9.1.2",
|
"@react-three/fiber": "^9.1.2",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
|
|||||||
Generated
+31
@@ -11,6 +11,9 @@ importers:
|
|||||||
'@radix-ui/react-checkbox':
|
'@radix-ui/react-checkbox':
|
||||||
specifier: ^1.3.2
|
specifier: ^1.3.2
|
||||||
version: 1.3.2(@types/react-dom@19.1.5(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 1.3.2(@types/react-dom@19.1.5(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
'@radix-ui/react-switch':
|
||||||
|
specifier: ^1.2.5
|
||||||
|
version: 1.2.5(@types/react-dom@19.1.5(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
'@react-three/drei':
|
'@react-three/drei':
|
||||||
specifier: ^10.1.2
|
specifier: ^10.1.2
|
||||||
version: 10.1.2(@react-three/fiber@9.1.2(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.177.0))(@types/react@19.1.6)(@types/three@0.177.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.177.0)
|
version: 10.1.2(@react-three/fiber@9.1.2(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.177.0))(@types/react@19.1.6)(@types/three@0.177.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(three@0.177.0)
|
||||||
@@ -544,6 +547,19 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-switch@1.2.5':
|
||||||
|
resolution: {integrity: sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-use-controllable-state@1.2.2':
|
'@radix-ui/react-use-controllable-state@1.2.2':
|
||||||
resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
|
resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -2463,6 +2479,21 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.1.6
|
'@types/react': 19.1.6
|
||||||
|
|
||||||
|
'@radix-ui/react-switch@1.2.5(@types/react-dom@19.1.5(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.2
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0)
|
||||||
|
'@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0)
|
||||||
|
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0)
|
||||||
|
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.6)(react@19.1.0)
|
||||||
|
'@radix-ui/react-use-size': 1.1.1(@types/react@19.1.6)(react@19.1.0)
|
||||||
|
react: 19.1.0
|
||||||
|
react-dom: 19.1.0(react@19.1.0)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 19.1.6
|
||||||
|
'@types/react-dom': 19.1.5(@types/react@19.1.6)
|
||||||
|
|
||||||
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.6)(react@19.1.0)':
|
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.6)(react@19.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.6)(react@19.1.0)
|
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.6)(react@19.1.0)
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { FC, InputHTMLAttributes } from 'react'
|
||||||
|
import Error from './Error'
|
||||||
|
import { clx } from '../helpers/utils'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
label: string,
|
||||||
|
error_text?: string
|
||||||
|
} & InputHTMLAttributes<HTMLTextAreaElement>
|
||||||
|
|
||||||
|
const Textarea: FC<Props> = (props: Props) => {
|
||||||
|
return (
|
||||||
|
<div className='w-full relative'>
|
||||||
|
<label className='text-sm'>
|
||||||
|
{props.label}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
className={clx(
|
||||||
|
'border border-border leading-7 w-full rounded-xl px-4 py-3 min-h-[100px] mt-1 text-xs',
|
||||||
|
props.readOnly && 'bg-gray-100 border-0 text-description'
|
||||||
|
)}
|
||||||
|
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
|
||||||
|
</textarea>
|
||||||
|
{
|
||||||
|
props.error_text &&
|
||||||
|
<Error errorText={props.error_text} />
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Textarea
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { FC, useCallback, useEffect, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useDropzone } from 'react-dropzone'
|
||||||
|
import { CloseCircle } from 'iconsax-react'
|
||||||
|
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
label: string,
|
||||||
|
isMultiple?: boolean,
|
||||||
|
onChange?: (file: File[]) => void;
|
||||||
|
isReset?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UploadBox: FC<Props> = (props: Props) => {
|
||||||
|
|
||||||
|
const { t } = useTranslation('global')
|
||||||
|
const [files, setFiles] = useState<File[]>([])
|
||||||
|
|
||||||
|
const onDrop = useCallback((acceptedFiles: File[]) => {
|
||||||
|
if (props.isMultiple) {
|
||||||
|
const array = [...files]
|
||||||
|
array.push(acceptedFiles[0])
|
||||||
|
setFiles(array)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
props.onChange && props.onChange(array)
|
||||||
|
} else {
|
||||||
|
setFiles([acceptedFiles[0]])
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
props.onChange && props.onChange([acceptedFiles[0]])
|
||||||
|
}
|
||||||
|
}, [files])
|
||||||
|
const { getRootProps, getInputProps } = useDropzone({ onDrop })
|
||||||
|
|
||||||
|
const handleRemove = (index: number) => {
|
||||||
|
const array = [...files]
|
||||||
|
array.splice(index, 1)
|
||||||
|
setFiles(array)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||||
|
props.onChange && props.onChange(array)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
if (props.isReset) {
|
||||||
|
setFiles([])
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [props.isReset])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='text-sm'>{props.label}</div>
|
||||||
|
<div className='w-full h-10 mt-1 border border-border rounded-xl items-center px-2 flex gap-2.5'>
|
||||||
|
<button {...getRootProps()} className=' w-fit h-7 rounded-lg text-xs px-6 bg-secondary'>
|
||||||
|
<input {...getInputProps()} />
|
||||||
|
|
||||||
|
{t('select_file')}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className='lg:flex gap-2 hidden'>
|
||||||
|
{
|
||||||
|
files?.map((item, index) => (
|
||||||
|
<div key={index} className='flex bg-gray-200 py-1.5 px-2 rounded-lg items-center gap-1'>
|
||||||
|
<CloseCircle onClick={() => handleRemove(index)} size={16} color='red' />
|
||||||
|
<div className='text-xs'>{item.name}</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='lg:hidden gap-2 flex flex-wrap mt-4'>
|
||||||
|
{
|
||||||
|
files?.map((item, index) => (
|
||||||
|
<div key={index} className='flex bg-gray-200 py-1.5 px-2 rounded-lg items-center gap-1'>
|
||||||
|
<CloseCircle onClick={() => handleRemove(index)} size={16} color='red' />
|
||||||
|
<div className='text-xs'>{item.name}</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UploadBox
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as SwitchPrimitive from "@radix-ui/react-switch"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Switch({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
|
||||||
|
return (
|
||||||
|
<SwitchPrimitive.Root
|
||||||
|
data-slot="switch"
|
||||||
|
className={cn(
|
||||||
|
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SwitchPrimitive.Thumb
|
||||||
|
data-slot="switch-thumb"
|
||||||
|
className={cn(
|
||||||
|
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SwitchPrimitive.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Switch }
|
||||||
+20
-1
@@ -96,8 +96,27 @@
|
|||||||
"record_dns": "رکوردهای DNS",
|
"record_dns": "رکوردهای DNS",
|
||||||
"record_dns_description": "در این قسمت میتوانید وضعیت رکوردهای DNS سرویس ایمیل خود را بررسی کنید.",
|
"record_dns_description": "در این قسمت میتوانید وضعیت رکوردهای DNS سرویس ایمیل خود را بررسی کنید.",
|
||||||
"your_domain": "دامنه شما",
|
"your_domain": "دامنه شما",
|
||||||
"submit": "ثبت"
|
"submit": "ثبت",
|
||||||
|
"status": "وضعیت",
|
||||||
|
"search": "جستجو",
|
||||||
|
"address_title": "عنوان نشانی",
|
||||||
|
"email": "ایمیل",
|
||||||
|
"marketing": "بازاریابی",
|
||||||
|
"actions": "عملیات",
|
||||||
|
"delete": "حذف کردن ایمیل",
|
||||||
|
"active": "فعال",
|
||||||
|
"inactive": "غیرفعال",
|
||||||
|
"add_address": "اضافه کردن نشانی",
|
||||||
|
"domain1": "دامنه",
|
||||||
|
"create": "ساخت",
|
||||||
|
"content_email": "محتوای ایمیل شما",
|
||||||
|
"footer_email": "پاورقی ایمیل شما",
|
||||||
|
"signature": "امضا",
|
||||||
|
"signature_description": "تصویر و توضیحات امضای خود را با دقت وارد کنید",
|
||||||
|
"upload_sign": "آپلود امضا",
|
||||||
|
"description_sign": "توضیحات امضا"
|
||||||
},
|
},
|
||||||
|
"select_file": "انتخاب فایل",
|
||||||
"upload": "آپلود",
|
"upload": "آپلود",
|
||||||
"new_message": {
|
"new_message": {
|
||||||
"title": "ارسال پیام جدید",
|
"title": "ارسال پیام جدید",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import Tabs from '@/components/Tabs'
|
import Tabs from '@/components/Tabs'
|
||||||
import { Brush2, Global, People, Setting3 } from 'iconsax-react'
|
import { Brush2, Global, PenClose, People, Setting3 } from 'iconsax-react'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { SettingTabEnum } from './enum/SettingEnum'
|
import { SettingTabEnum } from './enum/SettingEnum'
|
||||||
@@ -8,6 +8,7 @@ import Domain from './domain/Domain'
|
|||||||
import MailServer from './mail-server/MailServer'
|
import MailServer from './mail-server/MailServer'
|
||||||
import Address from './address/Address'
|
import Address from './address/Address'
|
||||||
import PersonalitySidebar from './personality/SideBar'
|
import PersonalitySidebar from './personality/SideBar'
|
||||||
|
import Signture from './signture/Signture'
|
||||||
|
|
||||||
const Setting: FC = () => {
|
const Setting: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -21,6 +22,8 @@ const Setting: FC = () => {
|
|||||||
return <Domain />;
|
return <Domain />;
|
||||||
case SettingTabEnum.SETTING_ADDRESS:
|
case SettingTabEnum.SETTING_ADDRESS:
|
||||||
return <Address />;
|
return <Address />;
|
||||||
|
case SettingTabEnum.SETTING_SIGNATURE:
|
||||||
|
return <Signture />;
|
||||||
case SettingTabEnum.SETTING_PERSONALITY:
|
case SettingTabEnum.SETTING_PERSONALITY:
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-col xl:flex-row-reverse gap-4 md:gap-6 mt-6 md:mt-8'>
|
<div className='flex flex-col xl:flex-row-reverse gap-4 md:gap-6 mt-6 md:mt-8'>
|
||||||
@@ -63,6 +66,11 @@ const Setting: FC = () => {
|
|||||||
icon: <Brush2 size={20} color={activeTab === SettingTabEnum.SETTING_PERSONALITY ? 'black' : '#8C90A3'} />,
|
icon: <Brush2 size={20} color={activeTab === SettingTabEnum.SETTING_PERSONALITY ? 'black' : '#8C90A3'} />,
|
||||||
label: t('setting.personality'),
|
label: t('setting.personality'),
|
||||||
value: SettingTabEnum.SETTING_PERSONALITY
|
value: SettingTabEnum.SETTING_PERSONALITY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <PenClose size={20} color={activeTab === SettingTabEnum.SETTING_SIGNATURE ? 'black' : '#8C90A3'} />,
|
||||||
|
label: t('setting.signature'),
|
||||||
|
value: SettingTabEnum.SETTING_SIGNATURE
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
active={activeTab}
|
active={activeTab}
|
||||||
|
|||||||
@@ -1,244 +1,187 @@
|
|||||||
|
import Input from '@/components/Input'
|
||||||
|
import Select from '@/components/Select'
|
||||||
|
import Table from '@/components/Table'
|
||||||
|
import { Switch } from "@/components/ui/switch"
|
||||||
|
import { ColumnType } from '@/components/types/TableTypes'
|
||||||
import { FC, useState } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Add, Edit2, Trash } from 'iconsax-react'
|
import Button from '@/components/Button'
|
||||||
|
import { TickCircle } from 'iconsax-react'
|
||||||
|
|
||||||
interface EmailAddress {
|
interface AddressData extends Record<string, unknown> {
|
||||||
id: number
|
id: number
|
||||||
|
title: string
|
||||||
email: string
|
email: string
|
||||||
name: string
|
status: boolean
|
||||||
isPrimary: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Address: FC = () => {
|
const Address: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [addresses, setAddresses] = useState<EmailAddress[]>([
|
const [addresses] = useState<AddressData[]>([
|
||||||
{ id: 1, email: 'user@example.com', name: 'Main Account', isPrimary: true }
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'بازاریابی',
|
||||||
|
email: 'Marketing@example.com',
|
||||||
|
status: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: 'بازاریابی',
|
||||||
|
email: 'Marketing@example.com',
|
||||||
|
status: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: 'بازاریابی',
|
||||||
|
email: 'Marketing@example.com',
|
||||||
|
status: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: 'بازاریابی',
|
||||||
|
email: 'Marketing@example.com',
|
||||||
|
status: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
title: 'بازاریابی',
|
||||||
|
email: 'Marketing@example.com',
|
||||||
|
status: true
|
||||||
|
}
|
||||||
])
|
])
|
||||||
const [showAddForm, setShowAddForm] = useState(false)
|
|
||||||
const [newEmail, setNewEmail] = useState('')
|
|
||||||
const [newName, setNewName] = useState('')
|
|
||||||
const [editingId, setEditingId] = useState<number | null>(null)
|
|
||||||
|
|
||||||
const handleAddAddress = () => {
|
const columns: ColumnType<AddressData>[] = [
|
||||||
if (newEmail && newName) {
|
{
|
||||||
const newId = addresses.length > 0 ? Math.max(...addresses.map(a => a.id)) + 1 : 1
|
title: t('setting.address_title'),
|
||||||
setAddresses([...addresses, {
|
key: 'title',
|
||||||
id: newId,
|
width: '200px'
|
||||||
email: newEmail,
|
},
|
||||||
name: newName,
|
{
|
||||||
isPrimary: addresses.length === 0
|
title: t('setting.email'),
|
||||||
}])
|
key: 'email',
|
||||||
setNewEmail('')
|
width: '300px'
|
||||||
setNewName('')
|
},
|
||||||
setShowAddForm(false)
|
{
|
||||||
|
title: t('setting.status'),
|
||||||
|
key: 'status',
|
||||||
|
width: '120px',
|
||||||
|
align: 'center',
|
||||||
|
render: (item: AddressData) => (
|
||||||
|
<div key={item.id} className='dltr flex justify-end'>
|
||||||
|
<Switch />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
|
|
||||||
const handleEditStart = (address: EmailAddress) => {
|
const statusOptions = [
|
||||||
setEditingId(address.id)
|
{ value: 'all', label: 'همه' },
|
||||||
setNewEmail(address.email)
|
{ value: 'active', label: t('setting.active') },
|
||||||
setNewName(address.name)
|
{ value: 'inactive', label: t('setting.inactive') }
|
||||||
}
|
]
|
||||||
|
|
||||||
const handleEditSave = () => {
|
|
||||||
if (editingId && newEmail && newName) {
|
|
||||||
setAddresses(addresses.map(addr =>
|
|
||||||
addr.id === editingId
|
|
||||||
? { ...addr, email: newEmail, name: newName }
|
|
||||||
: addr
|
|
||||||
))
|
|
||||||
setEditingId(null)
|
|
||||||
setNewEmail('')
|
|
||||||
setNewName('')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSetPrimary = (id: number) => {
|
|
||||||
setAddresses(addresses.map(addr => ({
|
|
||||||
...addr,
|
|
||||||
isPrimary: addr.id === id
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDelete = (id: number) => {
|
|
||||||
const filteredAddresses = addresses.filter(addr => addr.id !== id)
|
|
||||||
// If we're deleting the primary address, make the first one primary
|
|
||||||
if (addresses.find(addr => addr.id === id)?.isPrimary && filteredAddresses.length > 0) {
|
|
||||||
filteredAddresses[0].isPrimary = true
|
|
||||||
}
|
|
||||||
setAddresses(filteredAddresses)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-white rounded-4xl p-8'>
|
<div className='flex gap-8 mt-8'>
|
||||||
<div className='flex justify-between mb-8'>
|
<div className='flex-1'>
|
||||||
<h2 className='text-xl font-semibold'>{t('setting.address')}</h2>
|
<div className='flex justify-between'>
|
||||||
<button className='bg-primary text-white px-6 py-2 rounded-lg'>{t('common.save')}</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mb-6'>
|
|
||||||
<div className='flex justify-between items-center mb-4'>
|
|
||||||
<h3 className='text-md font-medium'>{t('setting.email_addresses')}</h3>
|
|
||||||
{!showAddForm && (
|
|
||||||
<button
|
|
||||||
onClick={() => setShowAddForm(true)}
|
|
||||||
className='flex items-center gap-1 text-primary'
|
|
||||||
>
|
|
||||||
<Add size={18} />
|
|
||||||
<span>{t('setting.add_address')}</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showAddForm && (
|
|
||||||
<div className='p-4 bg-gray-50 rounded-lg mb-4'>
|
|
||||||
<h4 className='font-medium mb-3'>{t('setting.new_address')}</h4>
|
|
||||||
<div className='grid grid-cols-1 gap-4 mb-4'>
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.email')}</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
value={newEmail}
|
|
||||||
onChange={(e) => setNewEmail(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='email@domain.com'
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.display_name')}</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={newName}
|
|
||||||
onChange={(e) => setNewName(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={t('setting.display_name_placeholder')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex justify-end gap-2'>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setShowAddForm(false)
|
|
||||||
setNewEmail('')
|
|
||||||
setNewName('')
|
|
||||||
}}
|
|
||||||
className='px-4 py-2 border border-gray-300 rounded-lg'
|
|
||||||
>
|
|
||||||
{t('common.cancel')}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={handleAddAddress}
|
|
||||||
disabled={!newEmail || !newName}
|
|
||||||
className={`px-4 py-2 rounded-lg ${!newEmail || !newName ? 'bg-gray-300 text-gray-500' : 'bg-primary text-white'}`}
|
|
||||||
>
|
|
||||||
{t('common.save')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className='space-y-3'>
|
|
||||||
{addresses.map((address) => (
|
|
||||||
<div key={address.id} className='p-4 bg-gray-50 rounded-lg'>
|
|
||||||
{editingId === address.id ? (
|
|
||||||
<div className='grid grid-cols-1 gap-4 mb-4'>
|
|
||||||
<div>
|
|
||||||
<label className='block text-sm font-medium text-gray-700 mb-1'>{t('setting.email')}</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
value={newEmail}
|
|
||||||
onChange={(e) => setNewEmail(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.display_name')}</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={newName}
|
|
||||||
onChange={(e) => setNewName(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 className='flex justify-end gap-2'>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setEditingId(null)
|
|
||||||
setNewEmail('')
|
|
||||||
setNewName('')
|
|
||||||
}}
|
|
||||||
className='px-4 py-2 border border-gray-300 rounded-lg'
|
|
||||||
>
|
|
||||||
{t('common.cancel')}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={handleEditSave}
|
|
||||||
disabled={!newEmail || !newName}
|
|
||||||
className={`px-4 py-2 rounded-lg ${!newEmail || !newName ? 'bg-gray-300 text-gray-500' : 'bg-primary text-white'}`}
|
|
||||||
>
|
|
||||||
{t('common.save')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className='flex items-center justify-between'>
|
|
||||||
<div>
|
|
||||||
<div className='flex items-center gap-2'>
|
|
||||||
<p className='font-medium'>{address.name}</p>
|
|
||||||
{address.isPrimary && (
|
|
||||||
<span className='px-2 py-0.5 bg-green-100 text-green-600 text-xs rounded-full'>
|
|
||||||
{t('setting.primary')}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<p className='text-gray-500'>{address.email}</p>
|
|
||||||
</div>
|
|
||||||
<div className='flex items-center gap-2'>
|
|
||||||
{!address.isPrimary && (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
onClick={() => handleSetPrimary(address.id)}
|
|
||||||
className='text-blue-600 px-3 py-1 text-sm'
|
|
||||||
>
|
|
||||||
{t('setting.set_primary')}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleEditStart(address)}
|
|
||||||
className='text-gray-500'
|
|
||||||
>
|
|
||||||
<Edit2 size={18} />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => handleDelete(address.id)}
|
|
||||||
className='text-gray-500'
|
|
||||||
>
|
|
||||||
<Trash size={18} />
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='p-4 bg-yellow-50 rounded-lg border border-yellow-100'>
|
|
||||||
<div className='flex items-start gap-3'>
|
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M12 22C17.5 22 22 17.5 22 12C22 6.5 17.5 2 12 2C6.5 2 2 6.5 2 12C2 17.5 6.5 22 12 22Z" stroke="#F59E0B" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
<path d="M12 8V13" stroke="#F59E0B" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
<path d="M11.9941 16H12.0031" stroke="#F59E0B" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
</svg>
|
|
||||||
<div>
|
<div>
|
||||||
<h4 className='font-medium text-yellow-800'>{t('setting.verification_required')}</h4>
|
<Select
|
||||||
<p className='text-sm text-yellow-700 mt-1'>{t('setting.verification_note')}</p>
|
placeholder={t('setting.status')}
|
||||||
|
items={statusOptions}
|
||||||
|
className='w-[120px]'
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Input
|
||||||
|
variant='search'
|
||||||
|
placeholder={t('setting.search')}
|
||||||
|
className='w-[300px]'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
data={addresses}
|
||||||
|
pagination={{
|
||||||
|
currentPage: 1,
|
||||||
|
totalPages: 6,
|
||||||
|
onPageChange: (page) => console.log('Page:', page)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='w-[350px] bg-white rounded-4xl p-8'>
|
||||||
|
<div>
|
||||||
|
{t('setting.add_address')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-8 flex items-center justify-between'>
|
||||||
|
<div>
|
||||||
|
{t('setting.status')}
|
||||||
|
</div>
|
||||||
|
<div className='dltr pt-2'>
|
||||||
|
<Switch />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-6'>
|
||||||
|
<Input
|
||||||
|
label={t('setting.domain1')}
|
||||||
|
value={'example.com'}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-6'>
|
||||||
|
<Input
|
||||||
|
label={t('setting.title')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-6 relative'>
|
||||||
|
<Input
|
||||||
|
label={t('setting.username')}
|
||||||
|
className='text-left'
|
||||||
|
/>
|
||||||
|
<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'>
|
||||||
|
@ example.com
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-6'>
|
||||||
|
<Input
|
||||||
|
label={t('setting.password')}
|
||||||
|
type='password'
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<div className='mt-9 flex justify-end'>
|
||||||
|
<Button
|
||||||
|
className='w-fit px-20'
|
||||||
|
>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<TickCircle size={20} color='white' />
|
||||||
|
<div>
|
||||||
|
{t('setting.create')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Address
|
export default Address
|
||||||
@@ -106,7 +106,7 @@ const Domain: FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between mt-9'>
|
||||||
<div>
|
<div>
|
||||||
<div className='text-lg'>
|
<div className='text-lg'>
|
||||||
{t('setting.record_dns')}
|
{t('setting.record_dns')}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export enum SettingTabEnum {
|
|||||||
SETTING_DOMAIN = "setting_domain",
|
SETTING_DOMAIN = "setting_domain",
|
||||||
SETTING_ADDRESS = "setting_address",
|
SETTING_ADDRESS = "setting_address",
|
||||||
SETTING_PERSONALITY = "setting_personality",
|
SETTING_PERSONALITY = "setting_personality",
|
||||||
|
SETTING_SIGNATURE = "setting_signature",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SideBarTab {
|
export enum SideBarTab {
|
||||||
|
|||||||
@@ -1,185 +1,29 @@
|
|||||||
import { FC, useState } from 'react'
|
import { AddCircle } from 'iconsax-react'
|
||||||
import { Add, Edit } from 'iconsax-react'
|
import { FC } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const Personality: FC = () => {
|
const Personality: FC = () => {
|
||||||
const [backgroundImage, setBackgroundImage] = useState<string | null>(null)
|
|
||||||
const [emailContent, setEmailContent] = useState<string>('')
|
|
||||||
const [emailFooter, setEmailFooter] = useState<string>('')
|
|
||||||
const [showContentEditor, setShowContentEditor] = useState<boolean>(false)
|
|
||||||
const [showFooterEditor, setShowFooterEditor] = useState<boolean>(false)
|
|
||||||
|
|
||||||
const handleImageUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const { t } = useTranslation()
|
||||||
const file = event.target.files?.[0]
|
|
||||||
if (file) {
|
|
||||||
const reader = new FileReader()
|
|
||||||
reader.onload = (e) => {
|
|
||||||
setBackgroundImage(e.target?.result as string)
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAddEmailContent = () => {
|
|
||||||
if (!emailContent) {
|
|
||||||
setEmailContent('متن ایمیل خود را اینجا وارد کنید')
|
|
||||||
}
|
|
||||||
setShowContentEditor(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAddEmailFooter = () => {
|
|
||||||
if (!emailFooter) {
|
|
||||||
setEmailFooter('پاورقی ایمیل خود را اینجا وارد کنید')
|
|
||||||
}
|
|
||||||
setShowFooterEditor(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-white rounded-4xl p-6 flex-1'>
|
<div className='flex-1 bg-white rounded-4xl p-8'>
|
||||||
{/* Header buttons */}
|
<div className='border flex items-center p-2.5 gap-4 border-dashed border-border h-[123px] rounded-4xl'>
|
||||||
<div className='flex justify-between mb-8'>
|
<div className='flex-1 h-[103px] rounded-4xl border border-dashed border-[#0038FF]'></div>
|
||||||
<button className='bg-black text-white px-5 py-2 rounded-xl text-sm'>ذخیره</button>
|
<div className='flex-1 h-[103px] rounded-4xl border border-dashed border-border'></div>
|
||||||
{/* <div className='flex gap-3'>
|
|
||||||
<button className='w-10 h-10 flex items-center justify-center bg-white border border-gray-200 rounded-full'>
|
|
||||||
<Edit size={20} variant="Bold" color="#FF008A" />
|
|
||||||
</button>
|
|
||||||
<button className='w-10 h-10 flex items-center justify-center bg-white border border-gray-200 rounded-full'>
|
|
||||||
<ArrowRight size={20} />
|
|
||||||
</button>
|
|
||||||
<button className='w-10 h-10 flex items-center justify-center bg-white border border-gray-200 rounded-full'>
|
|
||||||
<ArrowLeft size={20} />
|
|
||||||
</button>
|
|
||||||
<button className='w-10 h-10 flex items-center justify-center bg-white border border-gray-200 rounded-full'>
|
|
||||||
<Eye size={20} />
|
|
||||||
</button>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Background image section */}
|
<div className='mt-4 h-[286px] border border-dashed border-border rounded-4xl flex flex-col items-center justify-center p-2.5'>
|
||||||
<div className='border border-dashed border-gray-300 rounded-2xl p-8 mb-6'>
|
<div className='text-description'>{t('setting.content_email')}</div>
|
||||||
{backgroundImage ? (
|
<div className='mt-1'>
|
||||||
<div className='relative w-full'>
|
<AddCircle size={24} color='#888' />
|
||||||
<img src={backgroundImage} alt="Background" className='w-full h-48 object-cover rounded-lg' />
|
</div>
|
||||||
<button
|
|
||||||
className='absolute top-2 right-2 bg-white p-1 rounded-full shadow-sm'
|
|
||||||
onClick={() => setBackgroundImage(null)}
|
|
||||||
>
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M12 22C17.5 22 22 17.5 22 12C22 6.5 17.5 2 12 2C6.5 2 2 6.5 2 12C2 17.5 6.5 22 12 22Z" stroke="#292D32" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
<path d="M9.17 14.83L14.83 9.17" stroke="#292D32" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
<path d="M14.83 14.83L9.17 9.17" stroke="#292D32" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className='flex flex-col items-center justify-center py-10'>
|
|
||||||
<div className='text-gray-500 mb-4'>تصویر پس زمینه مورد نظر را آپلود کنید</div>
|
|
||||||
<label className='flex items-center gap-2 bg-white border border-gray-200 rounded-lg px-4 py-2 text-sm cursor-pointer'>
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
accept="image/*"
|
|
||||||
className='hidden'
|
|
||||||
onChange={handleImageUpload}
|
|
||||||
/>
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M9 17V11L7 13" stroke="#292D32" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
<path d="M9 11L11 13" stroke="#292D32" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
<path d="M22 10V15C22 20 20 22 15 22H9C4 22 2 20 2 15V9C2 4 4 2 9 2H14" stroke="#292D32" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
<path d="M22 10H18C15 10 14 9 14 6V2L22 10Z" stroke="#292D32" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
||||||
</svg>
|
|
||||||
آپلود
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className='border flex flex-col justify-center mt-4 items-center p-2.5 gap-4 border-dashed border-border h-[123px] rounded-4xl'>
|
||||||
{/* Email content section */}
|
<div className='text-description'>{t('setting.footer_email')}</div>
|
||||||
<div className='border border-dashed border-gray-300 rounded-2xl p-8 mb-6'>
|
<div className='mt-1'>
|
||||||
{emailContent && showContentEditor ? (
|
<AddCircle size={24} color='#888' />
|
||||||
<div className='w-full'>
|
</div>
|
||||||
<textarea
|
|
||||||
className='w-full p-4 border border-gray-200 rounded-lg min-h-[150px] text-right'
|
|
||||||
value={emailContent}
|
|
||||||
onChange={(e) => setEmailContent(e.target.value)}
|
|
||||||
/>
|
|
||||||
<div className='flex justify-end mt-3'>
|
|
||||||
<button
|
|
||||||
className='px-4 py-2 bg-gray-200 rounded-lg text-sm'
|
|
||||||
onClick={() => setShowContentEditor(false)}
|
|
||||||
>
|
|
||||||
تایید
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : emailContent ? (
|
|
||||||
<div className='w-full'>
|
|
||||||
<div className='p-4 border border-gray-200 rounded-lg min-h-[150px] text-right'>
|
|
||||||
{emailContent}
|
|
||||||
</div>
|
|
||||||
<div className='flex justify-end mt-3'>
|
|
||||||
<button
|
|
||||||
className='p-2 bg-gray-100 rounded-lg'
|
|
||||||
onClick={() => setShowContentEditor(true)}
|
|
||||||
>
|
|
||||||
<Edit size={16} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className='flex flex-col items-center justify-center py-14'>
|
|
||||||
<div className='text-gray-500 mb-3'>محتوای ایمیل شما</div>
|
|
||||||
<button
|
|
||||||
className='w-10 h-10 flex items-center justify-center rounded-full bg-gray-100'
|
|
||||||
onClick={handleAddEmailContent}
|
|
||||||
>
|
|
||||||
<Add size={24} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Email footer section */}
|
|
||||||
<div className='border border-dashed border-gray-300 rounded-2xl p-8 mb-6'>
|
|
||||||
{emailFooter && showFooterEditor ? (
|
|
||||||
<div className='w-full'>
|
|
||||||
<textarea
|
|
||||||
className='w-full p-4 border border-gray-200 rounded-lg min-h-[100px] text-right'
|
|
||||||
value={emailFooter}
|
|
||||||
onChange={(e) => setEmailFooter(e.target.value)}
|
|
||||||
/>
|
|
||||||
<div className='flex justify-end mt-3'>
|
|
||||||
<button
|
|
||||||
className='px-4 py-2 bg-gray-200 rounded-lg text-sm'
|
|
||||||
onClick={() => setShowFooterEditor(false)}
|
|
||||||
>
|
|
||||||
تایید
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : emailFooter ? (
|
|
||||||
<div className='w-full'>
|
|
||||||
<div className='p-4 border border-gray-200 rounded-lg min-h-[100px] text-right'>
|
|
||||||
{emailFooter}
|
|
||||||
</div>
|
|
||||||
<div className='flex justify-end mt-3'>
|
|
||||||
<button
|
|
||||||
className='p-2 bg-gray-100 rounded-lg'
|
|
||||||
onClick={() => setShowFooterEditor(true)}
|
|
||||||
>
|
|
||||||
<Edit size={16} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className='flex flex-col items-center justify-center py-10'>
|
|
||||||
<div className='text-gray-500 mb-3'>پاورقی ایمیل شما</div>
|
|
||||||
<button
|
|
||||||
className='w-10 h-10 flex items-center justify-center rounded-full bg-gray-100'
|
|
||||||
onClick={handleAddEmailFooter}
|
|
||||||
>
|
|
||||||
<Add size={24} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import Textarea from '@/components/Textarea'
|
||||||
|
import UploadBox from '@/components/UploadBox'
|
||||||
|
import { FC } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
const Signture: FC = () => {
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return (
|
||||||
|
<div className='bg-white rounded-4xl p-8 mt-9'>
|
||||||
|
<div className='mt-9'>
|
||||||
|
<div className='flex justify-between items-start pb-10'>
|
||||||
|
<div className='flex-1'>
|
||||||
|
<div className='text-lg'>
|
||||||
|
{t('setting.signature')}
|
||||||
|
</div>
|
||||||
|
<div className='text-sm text-description mt-1.5'>
|
||||||
|
{t('setting.signature_description')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex-1'>
|
||||||
|
<UploadBox
|
||||||
|
label={t('setting.upload_sign')}
|
||||||
|
/>
|
||||||
|
<div className='mt-8'>
|
||||||
|
<Textarea
|
||||||
|
label={t('setting.description_sign')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Signture
|
||||||
Reference in New Issue
Block a user