(null)
- // نمونه دادههای DNS records طبق تصویر
- const dnsRecords: DNSRecord[] = [
- {
- id: 1,
- status: 'verified',
- type: 'TXT',
- name: 'example.com',
- value: 'v=spf1 include:_spf.example.com ~all',
- number: 1
- },
- {
- id: 2,
- status: 'verified',
- type: 'MX | Priority:10',
- name: 'example.com',
- value: '10 mail.example.com',
- number: 1
- },
- {
- 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
+ useEffect(() => {
+ if (data?.data?.overallStatus?.isVerified && !disabled) {
+ setDisabled(true)
}
- ]
+ }, [data])
+
+ const getStatusIcon = (status: DnsRecordType['status']) => {
+ switch (status) {
+ case 'VERIFIED':
+ return (
+
+
+ {t('domain.status.verified')}
+
+ )
+ case 'PENDING':
+ return (
+
+
+ {t('domain.status.pending')}
+
+ )
+ case 'FAILED':
+ return (
+
+
+ {t('domain.status.failed')}
+
+ )
+ default:
+ return null
+ }
+ }
+
+ const copyToClipboard = (text: string, fieldKey: string) => {
+ navigator.clipboard.writeText(text).then(() => {
+ setCopiedField(fieldKey)
+ setTimeout(() => {
+ setCopiedField(null)
+ }, 2000)
+ })
+ }
const columns = [
{
- title: 'شماره',
- key: 'number',
- width: '80px',
+ title: t('domain.table.record_type'),
+ key: 'type',
+ width: '100px',
align: 'center' as const,
},
{
- title: 'مقدار رکورد',
- key: 'value',
- render: (record: DNSRecord) => (
-
-
- {record.value}
-
- )
- },
- {
- title: 'نام رکورد',
+ title: t('domain.table.record_name'),
key: 'name',
- render: (record: DNSRecord) => (
-
-
- {record.name}
-
+ render: (record: DnsRecordType) => {
+ const fieldKey = `name-${record.id}`
+ const isCopied = copiedField === fieldKey
+
+ return (
+
+
+ {record.name}
+
+ )
+ }
+ },
+ {
+ title: t('domain.table.record_value'),
+ key: 'value',
+ render: (record: DnsRecordType) => {
+ const fieldKey = `value-${record.id}`
+ const isCopied = copiedField === fieldKey
+
+ return (
+
+
+ {record.value}
+
+ )
+ }
+ },
+ {
+ title: t('domain.table.priority'),
+ key: 'priority',
+ width: '80px',
+ align: 'center' as const,
+ render: (record: DnsRecordType) => (
+ {record.priority || '-'}
)
},
{
- title: 'نوع رکورد',
- key: 'type',
+ title: t('domain.table.ttl'),
+ key: 'ttl',
+ width: '80px',
+ align: 'center' as const,
+ render: (record: DnsRecordType) => (
+ {record.ttl}
+ )
},
{
- title: 'وضعیت',
+ title: t('domain.table.status'),
key: 'status',
- render: (record: DNSRecord) => (
-
- {record.status === 'verified' && (
- <>
-
- ثبت شده
- >
- )}
-
- )
+ render: (record: DnsRecordType) => getStatusIcon(record.status)
},
]
+ const dnsRecords = data?.data?.dnsRecords || []
+
return (
-
-
-
- {t('setting.record_dns')}
-
-
- {t('setting.record_dns_description')}
-
-
-
-
-
-
-
-
-
-
+
-
-
)
}
diff --git a/src/pages/setting/domain/components/CreateDomain.tsx b/src/pages/setting/domain/components/CreateDomain.tsx
new file mode 100644
index 0000000..1721bd9
--- /dev/null
+++ b/src/pages/setting/domain/components/CreateDomain.tsx
@@ -0,0 +1,76 @@
+import Button from '@/components/Button'
+import Input from '@/components/Input'
+import { toast } from '@/components/Toast'
+import { FC, useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { useCreateDomain, useGetDomains } from '../hooks/useDomainData'
+import { ErrorType } from '@/helpers/types'
+
+const CreateDomain: FC = () => {
+
+ const { t } = useTranslation()
+ const { mutate: createDomain, isPending } = useCreateDomain()
+ const { data: domains } = useGetDomains()
+ const [domain, setDomain] = useState('')
+ const [isVerified, setIsVerified] = useState(false)
+
+ useEffect(() => {
+ if (domains?.data?.domain) {
+ setDomain(domains?.data?.domain?.name)
+ if (domains?.data?.domain?.isVerified) {
+ setIsVerified(true)
+ }
+ }
+ }, [domains])
+
+
+ const handleCreateDomain = () => {
+ createDomain({
+ name: domain,
+ notes: domain
+ }, {
+ onSuccess: (data) => {
+ toast(data.message, 'success')
+ setDomain('')
+ },
+ onError: (error: ErrorType) => {
+ toast(error.response?.data?.error?.message[0], 'error')
+ }
+ })
+ }
+ return (
+
+
+
+ {t('setting.record_dns')}
+
+
+ {t('setting.record_dns_description')}
+
+
+
+
+ setDomain(e.target.value)}
+ readOnly={isVerified}
+ />
+
+ {
+ !isVerified &&
+
+ }
+
+
+ )
+}
+
+export default CreateDomain
\ No newline at end of file
diff --git a/src/pages/setting/domain/hooks/useDomainData.ts b/src/pages/setting/domain/hooks/useDomainData.ts
new file mode 100644
index 0000000..f807910
--- /dev/null
+++ b/src/pages/setting/domain/hooks/useDomainData.ts
@@ -0,0 +1,32 @@
+import { useMutation, useQuery } from "@tanstack/react-query";
+import { CreateDomainType } from "../types/Types";
+import * as api from "../service/DomainService";
+
+export const useCreateDomain = () => {
+ return useMutation({
+ mutationFn: (variables: CreateDomainType) => api.createDomain(variables),
+ });
+};
+
+export const useGetDomains = () => {
+ return useQuery({
+ queryKey: ["domains"],
+ queryFn: () => api.getDomains(),
+ });
+};
+
+export const useGetDnsRecords = (disabled?: boolean) => {
+ return useQuery({
+ queryKey: ["dns-records"],
+ queryFn: () => api.getDnsRecords(),
+ refetchInterval: 3000,
+ enabled: !disabled,
+ });
+};
+
+export const useVerifyDnsRecord = () => {
+ return useQuery({
+ queryKey: ["verify-dns-record"],
+ queryFn: () => api.verifyDnsRecord(),
+ });
+};
diff --git a/src/pages/setting/domain/service/DomainService.ts b/src/pages/setting/domain/service/DomainService.ts
new file mode 100644
index 0000000..990f073
--- /dev/null
+++ b/src/pages/setting/domain/service/DomainService.ts
@@ -0,0 +1,22 @@
+import axios from "@/config/axios";
+import { CreateDomainType } from "../types/Types";
+
+export const createDomain = async (params: CreateDomainType) => {
+ const { data } = await axios.post(`/domains`, params);
+ return data;
+};
+
+export const getDomains = async () => {
+ const { data } = await axios.get(`/domains`);
+ return data;
+};
+
+export const getDnsRecords = async () => {
+ const { data } = await axios.get(`/domains/dns-records`);
+ return data;
+};
+
+export const verifyDnsRecord = async () => {
+ const { data } = await axios.get(`/domains/verify-dns`);
+ return data;
+};
diff --git a/src/pages/setting/domain/types/Types.ts b/src/pages/setting/domain/types/Types.ts
new file mode 100644
index 0000000..33ed777
--- /dev/null
+++ b/src/pages/setting/domain/types/Types.ts
@@ -0,0 +1,33 @@
+import { IResponse } from "@/types/response.types";
+
+export type CreateDomainType = {
+ name: string;
+ notes?: string;
+};
+
+export type DnsRecordType = {
+ id: string;
+ createdAt: string;
+ updatedAt: string;
+ deletedAt: string | null;
+ wildduckId: string | null;
+ name: string;
+ type: "MX" | "SPF" | "DMARC" | "DKIM" | "TXT" | "A" | "AAAA" | "CNAME";
+ value: string;
+ ttl: number;
+ priority: number | null;
+ status: "PENDING" | "VERIFIED" | "FAILED";
+ isRequired: boolean;
+ isActive: boolean;
+ lastVerifiedAt: string | null;
+ lastCheckedAt: string | null;
+ description: string;
+ errorMessage: string | null;
+ verificationAttempts: number;
+ nextVerificationAt: string | null;
+ domain: string;
+};
+
+export type DnsRecordResponseType = IResponse<{
+ dnsRecords: DnsRecordType[];
+}>;
diff --git a/src/router/AppRouter.tsx b/src/router/AppRouter.tsx
index 77c46da..05409d3 100644
--- a/src/router/AppRouter.tsx
+++ b/src/router/AppRouter.tsx
@@ -8,10 +8,11 @@ import DraftList from '@/pages/draft/List'
import ArchiveList from '@/pages/archive/List'
import TrashList from '@/pages/Trash/List'
import DetailEmail from '@/pages/received/Detail'
+import Home from '@/pages/home/Home'
const AppRouter: FC = () => {
return (
- } />
+ } />
} />
} />
} />
diff --git a/src/types/error.types.ts b/src/types/error.types.ts
new file mode 100644
index 0000000..facf569
--- /dev/null
+++ b/src/types/error.types.ts
@@ -0,0 +1,14 @@
+import { DefaultError } from "@tanstack/react-query";
+import { IBaseResponse } from "./response.types";
+
+interface IError {
+ message: string[];
+}
+
+export interface IErrorResponse extends IBaseResponse {
+ error: IError;
+}
+
+export interface IApiErrorRepsonse extends DefaultError {
+ response?: IErrorResponse;
+}
diff --git a/src/types/response.types.ts b/src/types/response.types.ts
new file mode 100644
index 0000000..b427094
--- /dev/null
+++ b/src/types/response.types.ts
@@ -0,0 +1,7 @@
+export interface IBaseResponse {
+ statusCode: number;
+ success: boolean;
+}
+export interface IResponse extends IBaseResponse {
+ data: T;
+}