setting domain

This commit is contained in:
hamid zarghami
2025-05-23 19:08:14 +03:30
parent 58678d585b
commit 4c6441e03f
8 changed files with 366 additions and 61 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
VITE_TOKEN_NAME = 'dsc_token'
VITE_REFRESH_TOKEN_NAME = 'dsc_refresh_token'
# VITE_BASE_URL = 'http://192.168.1.113:4001'
VITE_BASE_URL = 'https://api-dzone.danakcorp.com'
VITE_BASE_URL = 'http://192.168.1.113:4001'
# VITE_BASE_URL = 'https://api-dzone.danakcorp.com'
# VITE_DANAK_BASE_URL ='http://192.168.1.113:4000'
VITE_DANAK_BASE_URL ='https://api.danakcorp.com'
VITE_SERVICE_ID = '7e3c2f08-b7e7-4402-ae5f-fea99cff56bd'
+33
View File
@@ -0,0 +1,33 @@
import axios from "axios";
import { SettingType } from "../pages/setting/types/Types";
const baseURL = import.meta.env.VITE_API_URL;
const instance = axios.create({
baseURL,
headers: {
"Content-Type": "application/json",
},
});
// Add request interceptor to add auth token
instance.interceptors.request.use((config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
export const api = {
// Setting endpoints
getSetting: () => instance.get("/setting").then((res) => res.data),
updateSetting: (data: SettingType) =>
instance.post("/setting", data).then((res) => res.data),
// Domain management endpoints
setDomain: (data: { domain: string }) =>
instance.post("/domain", data).then((res) => res.data),
verifyDomain: () => instance.post("/domain/verify").then((res) => res.data),
getDomainStatus: () => instance.get("/domain/status").then((res) => res.data),
};
+12 -1
View File
@@ -596,7 +596,18 @@
"zarinpal_merchant_id": "شناسه مرچنت زرین پال",
"logo": "لوگو",
"file_required": "فایل الزامی است",
"update_success": "تنظیمات با موفقیت به روز شد"
"update_success": "تنظیمات با موفقیت به روز شد",
"set_domain": "تنظیم دامنه",
"verify_domain": "تایید دامنه",
"domain_status": "وضعیت دامنه",
"verification_status": "وضعیت تایید",
"connection_status": "وضعیت اتصال",
"domain_verified": "دامنه تایید شده",
"domain_not_verified": "دامنه تایید نشده",
"domain_set_success": "دامنه با موفقیت تنظیم شد",
"domain_verify_success": "دامنه با موفقیت تایید شد",
"setup_instructions": "دستورالعمل‌های راه‌اندازی",
"completed": "تکمیل شده"
},
"wallet": {
"increese_wallet": "افزایش موجودی حساب اعتباری",
+15
View File
@@ -2,6 +2,7 @@ import { FC, useEffect, useState } from 'react'
import { useWorkspaces } from './hooks/useHomeData'
import { workspaceItem } from './types/HomeTypes'
import moment from 'moment-jalaali'
import { toast } from 'react-toastify'
const Home: FC = () => {
@@ -12,6 +13,20 @@ const Home: FC = () => {
setSelectedWorkspace(localStorage.getItem(import.meta.env.VITE_WORKSPACE_ID))
}, [])
useEffect(() => {
if (getWorkspaces.isError && getWorkspaces.error instanceof Error) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const error = getWorkspaces.error as any;
if (error.response?.status === 403) {
toast.error(error.response?.data?.error.message[0])
localStorage.removeItem(import.meta.env.VITE_WORKSPACE_ID)
window.location.href = 'https://console.danakcorp.com'
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [getWorkspaces.isError, getWorkspaces.data])
const handleChangeWorkspace = (id: string) => {
localStorage.setItem(import.meta.env.VITE_WORKSPACE_ID, id)
setSelectedWorkspace(id)
+214 -56
View File
@@ -5,22 +5,44 @@ import { KeySquare, Notification1 } from 'iconsax-react'
import Input from '../../components/Input'
import Button from '../../components/Button'
import { useFormik } from 'formik'
import { SettingType } from './types/Types'
import { SettingType, ConnectionOption } from './types/Types'
import * as yup from 'yup'
import UploadBox from '../../components/UploadBox'
import { toast } from 'react-toastify'
import { useSingleUpload } from '../service/hooks/useServiceData'
import { ErrorType } from '../../helpers/types'
import { useSettingData, useUpdateSetting } from './hooks/useSettingData'
import { useSettingData, useUpdateSetting, useSetDomain, useVerifyDomain, useGetDomainStatus } from './hooks/useSettingData'
const Setting: FC = () => {
const { t } = useTranslation('global')
const [activeTab, setActiveTab] = useState<string>('setting')
const [file, setFile] = useState<File | null>(null)
const singleUpload = useSingleUpload()
const updateSetting = useUpdateSetting()
const setDomain = useSetDomain()
const verifyDomain = useVerifyDomain()
const { data: setting } = useSettingData()
const { data: domainStatus, refetch: refetchDomainStatus } = useGetDomainStatus()
// Add polling effect for domain status
useEffect(() => {
if (activeTab === 'domain') {
let intervalId: number;
if (domainStatus?.data?.hasDomain && !domainStatus.data.domainPointsToServer) {
intervalId = window.setInterval(() => {
refetchDomainStatus();
}, 5000); // Check every 5 seconds
}
return () => {
if (intervalId) {
window.clearInterval(intervalId);
}
};
}
}, [domainStatus?.data?.hasDomain, domainStatus?.data?.domainPointsToServer, refetchDomainStatus, activeTab]);
const formik = useFormik<SettingType>({
initialValues: {
zarinpalMerchantId: '',
@@ -60,6 +82,37 @@ const Setting: FC = () => {
}
})
const domainFormik = useFormik({
initialValues: {
domain: ''
},
validationSchema: yup.object({
domain: yup.string().required(t('errors.required')),
}),
onSubmit: async (values) => {
await setDomain.mutateAsync(values, {
onSuccess: () => {
toast.success(t('setting.domain_set_success'))
refetchDomainStatus()
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error.message[0])
}
})
}
})
const handleVerifyDomain = async () => {
await verifyDomain.mutateAsync(undefined, {
onSuccess: () => {
refetchDomainStatus()
},
onError: (error: ErrorType) => {
toast.error(error.response?.data?.error.message[0])
}
})
}
useEffect(() => {
if (setting?.data) {
formik.setValues(setting?.data?.business)
@@ -117,69 +170,174 @@ const Setting: FC = () => {
</div>
</div>
:
<div className='mt-10 bg-white p-8 rounded-3xl'>
<div>
{t('setting.change_password')}
</div>
activeTab === 'domain' ?
<div className='mt-10 space-y-6'>
{/* Domain Input Section */}
<div className='bg-white p-8 rounded-3xl'>
<div className='mb-6'>
{t('setting.domain')}
</div>
<div className='flex xl:flex-row flex-col xl:gap-10 gap-4'>
<div className='flex-1'>
<Input
type='password'
className='mt-6'
placeholder={t('setting.current_password')}
/>
<Input
type='password'
className='mt-6'
placeholder={t('setting.new_password')}
/>
<Input
type='password'
className='mt-6'
placeholder={t('setting.reapeat_password')}
/>
<div className='flex flex-col gap-6'>
<Input
label={t('setting.domain')}
{...domainFormik.getFieldProps('domain')}
error_text={domainFormik.touched.domain && domainFormik.errors.domain ? domainFormik.errors.domain : undefined}
/>
<div className='mt-9 xl:block hidden'>
<Button
label={t('setting.save_change')}
className='w-32'
label={t('setting.set_domain')}
onClick={() => domainFormik.handleSubmit()}
isLoading={setDomain.isPending}
/>
</div>
</div>
<div className='flex-1 xl:p-8 p-4 bg-[#EEF0F7] rounded-3xl text-sm'>
<div>
{t('setting.password_is')}
{/* Domain Status Section */}
{domainStatus?.data?.hasDomain && (
<div className='bg-white p-8 rounded-3xl'>
<div className='mb-6'>
{t('setting.domain_status')}
</div>
<div className='space-y-6'>
{/* Domain Info */}
<div className='flex items-center gap-4'>
<div className='text-sm font-medium'>
{t('setting.domain')}:
</div>
<div className='text-sm'>
{domainStatus.data.domain}
</div>
</div>
{/* Verification Status */}
<div className='p-4 bg-gray-50 rounded-lg'>
<div className='flex items-center gap-4 mb-2'>
<div className='text-sm font-medium'>
{t('setting.verification_status')}:
</div>
<div className={`text-sm ${domainStatus.data.isVerified ? 'text-green-500' : 'text-red-500'}`}>
{domainStatus.data.isVerified ? t('setting.domain_verified') : domainStatus.data.verificationStatus}
</div>
</div>
</div>
{/* Connection Status */}
<div className='p-4 bg-gray-50 rounded-lg'>
<div className='flex items-center gap-4 mb-2'>
<div className='text-sm font-medium'>
{t('setting.connection_status')}:
</div>
<div className={`text-sm ${domainStatus.data.domainPointsToServer ? 'text-green-500' : 'text-red-500'}`}>
{domainStatus.data.connectionStatus}
</div>
</div>
</div>
{!domainStatus.data.isVerified && (
<Button
label={t('setting.verify_domain')}
onClick={handleVerifyDomain}
isLoading={verifyDomain.isPending}
/>
)}
{domainStatus.data.setupInstructions && (
<div className='space-y-6'>
{/* Verification Instructions */}
{domainStatus.data.setupInstructions.verificationInstructions && (
<div className='p-4 bg-gray-50 rounded-lg'>
<div className='flex items-center gap-2 mb-2'>
<div className='font-bold'>
{domainStatus.data.setupInstructions.verificationInstructions.title}
</div>
{domainStatus.data.isVerified && (
<div className='text-green-500 text-sm'>
{t('setting.completed')}
</div>
)}
</div>
<div className='text-sm mb-4'>
{domainStatus.data.setupInstructions.verificationInstructions.description}
</div>
<div className='bg-white p-3 rounded border text-sm'>
<div>Host: {domainStatus.data.setupInstructions.verificationInstructions.host}</div>
<div>Type: {domainStatus.data.setupInstructions.verificationInstructions.type}</div>
<div>Value: {domainStatus.data.setupInstructions.verificationInstructions.value}</div>
<div>TTL: {domainStatus.data.setupInstructions.verificationInstructions.ttl}</div>
</div>
</div>
)}
{/* Connection Instructions */}
{domainStatus.data.setupInstructions.connectionInstructions && (
<div className='p-4 bg-gray-50 rounded-lg'>
<div className='flex items-center gap-2 mb-2'>
<div className='font-bold'>
{domainStatus.data.setupInstructions.connectionInstructions.title}
</div>
{domainStatus.data.domainPointsToServer && (
<div className='text-green-500 text-sm'>
{t('setting.completed')}
</div>
)}
</div>
<div className='text-sm mb-4'>
{domainStatus.data.setupInstructions.connectionInstructions.description}
</div>
<div className='space-y-4'>
{domainStatus.data.setupInstructions.connectionInstructions.options.map((option: ConnectionOption, index: number) => (
<div key={index} className='bg-white p-3 rounded border'>
<div className='font-medium mb-2'>{option.title}</div>
<ul className='list-disc list-inside space-y-1 text-sm'>
{option.steps.map((step: string, stepIndex: number) => (
<li key={stepIndex}>{step}</li>
))}
</ul>
</div>
))}
</div>
{domainStatus.data.setupInstructions.connectionInstructions.troubleshooting && (
<div className='mt-4'>
<div className='font-bold mb-2'>Troubleshooting:</div>
<ul className='list-disc list-inside space-y-1 text-sm'>
{domainStatus.data.setupInstructions.connectionInstructions.troubleshooting.map((item: string, index: number) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
)}
</div>
)}
{/* General Instructions */}
{domainStatus.data.setupInstructions.general && (
<div className='p-4 bg-gray-50 rounded-lg'>
<div className='text-sm mb-4'>
{domainStatus.data.setupInstructions.general.propagationNote}
</div>
{domainStatus.data.setupInstructions.general.troubleshooting && (
<div>
<div className='font-medium mb-2'>Troubleshooting:</div>
<ul className='list-disc list-inside space-y-1 text-sm'>
{domainStatus.data.setupInstructions.general.troubleshooting.map((item: string, index: number) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
)}
</div>
)}
</div>
)}
</div>
</div>
<ul className='list-disc ps-4 flex flex-col gap-2 text-xs mt-4'>
<li>
{t('setting.min-8')}
</li>
<li>
{t('setting.mix_charachter_number')}
</li>
<li>
{t('setting.exsist_number')}
</li>
<li>
{t('setting.exsist_special_character')}
</li>
</ul>
</div>
<div className=' xl:hidden block'>
<Button
label={t('setting.save_change')}
className='w-32'
/>
</div>
)}
</div>
<div className='h-14'></div>
</div>
:
null
}
</div>
)
}
+28 -2
View File
@@ -1,6 +1,13 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import { getSetting, updateSetting } from "../service/Service";
import {
getSetting,
updateSetting,
setDomain,
verifyDomain,
getDomainStatus,
} from "../service/Service";
import { SettingType } from "../types/Types";
export const useSettingData = () => {
return useQuery({
queryKey: ["setting"],
@@ -10,6 +17,25 @@ export const useSettingData = () => {
export const useUpdateSetting = () => {
return useMutation({
mutationFn: (params: SettingType) => updateSetting(params),
mutationFn: (variables: SettingType) => updateSetting(variables),
});
};
export const useSetDomain = () => {
return useMutation({
mutationFn: (variables: { domain: string }) => setDomain(variables),
});
};
export const useVerifyDomain = () => {
return useMutation({
mutationFn: () => verifyDomain(),
});
};
export const useGetDomainStatus = () => {
return useQuery({
queryKey: ["domain-status"],
queryFn: getDomainStatus,
});
};
+17
View File
@@ -1,6 +1,7 @@
import axios from "../../../config/axios";
import { SettingType } from "../types/Types";
// Setting endpoints
export const getSetting = async () => {
const { data } = await axios.get("/business/settings");
return data;
@@ -10,3 +11,19 @@ export const updateSetting = async (params: SettingType) => {
const { data } = await axios.patch("/business/settings", params);
return data;
};
// Domain management endpoints
export const setDomain = async (params: { domain: string }) => {
const { data } = await axios.post("/business/domain", params);
return data;
};
export const verifyDomain = async () => {
const { data } = await axios.post("/business/domain/verify");
return data;
};
export const getDomainStatus = async () => {
const { data } = await axios.get("/business/domain/status");
return data;
};
+45
View File
@@ -2,3 +2,48 @@ export type SettingType = {
zarinpalMerchantId: string;
logoUrl: string;
};
export type VerificationInstructions = {
title: string;
description: string;
host: string;
type: string;
value: string;
ttl: string;
};
export type ConnectionOption = {
title: string;
steps: string[];
};
export type ConnectionInstructions = {
title: string;
description: string;
options: ConnectionOption[];
troubleshooting: string[];
};
export type GeneralInstructions = {
verificationEndpoint: string;
propagationNote: string;
troubleshooting: string[];
};
export type SetupInstructions = {
verificationInstructions: VerificationInstructions;
connectionInstructions: ConnectionInstructions;
general: GeneralInstructions;
};
export type DomainStatus = {
hasDomain: boolean;
domain: string;
isVerified: boolean;
verifiedAt: string | null;
domainPointsToServer: boolean;
verificationStatus: string;
connectionStatus: string;
fullSetupComplete: boolean;
setupInstructions: SetupInstructions;
};