setting shop
This commit is contained in:
@@ -65,6 +65,9 @@ export const Pages = {
|
|||||||
documents: "/pages/documents",
|
documents: "/pages/documents",
|
||||||
incomeTariff: "/pages/income-tariff",
|
incomeTariff: "/pages/income-tariff",
|
||||||
},
|
},
|
||||||
|
setting: {
|
||||||
|
shop: "/settings/shop",
|
||||||
|
},
|
||||||
wallet: "/wallet",
|
wallet: "/wallet",
|
||||||
profile: "/profile",
|
profile: "/profile",
|
||||||
customers: {
|
customers: {
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import { type FC, useState, useEffect } from 'react'
|
||||||
|
import { useFormik } from 'formik'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
import { useGetShop, useUpdateShop } from './hooks/useSettingData'
|
||||||
|
import { type UpdateShopType } from './types/Types'
|
||||||
|
import Button from '../../components/Button'
|
||||||
|
import Input from '../../components/Input'
|
||||||
|
import Textarea from '../../components/Textarea'
|
||||||
|
import UploadBox from '../../components/UploadBox'
|
||||||
|
import { TickCircle } from 'iconsax-react'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import { extractErrorMessage } from '@/helpers/utils'
|
||||||
|
|
||||||
|
// Validation schema برای UpdateShopType
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
shopName: Yup.string().required('نام شاپ الزامی است'),
|
||||||
|
shopDescription: Yup.string().required('توضیحات شاپ الزامی است'),
|
||||||
|
telephoneNumber: Yup.string().required('شماره تلفن الزامی است'),
|
||||||
|
shopPostalCode: Yup.string().required('کد پستی الزامی است'),
|
||||||
|
logo: Yup.string().required('لوگو الزامی است')
|
||||||
|
})
|
||||||
|
|
||||||
|
const Shop: FC = () => {
|
||||||
|
const { data: shopDetail, isLoading: shopDetailLoading, error: shopDetailError } = useGetShop()
|
||||||
|
const updateShopMutation = useUpdateShop()
|
||||||
|
|
||||||
|
const [logoFile, setLogoFile] = useState<File[]>([])
|
||||||
|
|
||||||
|
const formik = useFormik<UpdateShopType>({
|
||||||
|
initialValues: {
|
||||||
|
shopName: '',
|
||||||
|
shopDescription: '',
|
||||||
|
telephoneNumber: '',
|
||||||
|
shopPostalCode: '',
|
||||||
|
logo: ''
|
||||||
|
},
|
||||||
|
validationSchema,
|
||||||
|
onSubmit: async (values) => {
|
||||||
|
const logoUrl = values.logo
|
||||||
|
|
||||||
|
if (logoFile.length > 0) {
|
||||||
|
// TODO: اینجا باید API آپلود فایل اضافه شود
|
||||||
|
// const logoResponse = await uploadSingle.mutateAsync(logoFile[0])
|
||||||
|
// logoUrl = logoResponse.results?.url?.url || values.logo
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitData = {
|
||||||
|
...values,
|
||||||
|
logo: logoUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
updateShopMutation.mutate(submitData, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success('اطلاعات شاپ با موفقیت بروزرسانی شد')
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(extractErrorMessage(error))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (shopDetail?.results?.shop) {
|
||||||
|
const shop = shopDetail.results.shop
|
||||||
|
|
||||||
|
formik.setValues({
|
||||||
|
shopName: shop.shopName || '',
|
||||||
|
shopDescription: shop.shopDescription || '',
|
||||||
|
telephoneNumber: shop.telephoneNumber || '',
|
||||||
|
shopPostalCode: shop.shopPostalCode || '',
|
||||||
|
logo: shop.logo || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [shopDetail])
|
||||||
|
|
||||||
|
if (shopDetailLoading) {
|
||||||
|
return <div className="flex justify-center items-center h-64">در حال بارگذاری...</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shopDetailError) {
|
||||||
|
return <div className="text-red-500 text-center mt-8">خطا در بارگذاری اطلاعات شاپ</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='mt-4'>
|
||||||
|
<div className='flex justify-between items-center'>
|
||||||
|
<div className='flex items-center gap-3'>
|
||||||
|
<h1>ویرایش اطلاعات شاپ</h1>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
className='px-6'
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
isLoading={updateShopMutation.isPending}
|
||||||
|
disabled={!formik.isValid || updateShopMutation.isPending}
|
||||||
|
>
|
||||||
|
<div className='flex gap-2 items-center'>
|
||||||
|
<TickCircle size={16} color='#fff' />
|
||||||
|
<div>بروزرسانی شاپ</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex gap-6 xl:mt-8 mt-6'>
|
||||||
|
{/* اطلاعات اصلی */}
|
||||||
|
<div className='flex-1 text-sm bg-white py-8 xl:px-10 px-6 rounded-3xl'>
|
||||||
|
<h2 className='text-lg font-medium mb-6'>اطلاعات اصلی</h2>
|
||||||
|
|
||||||
|
<div className='space-y-6'>
|
||||||
|
<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
|
||||||
|
<Input
|
||||||
|
label='نام شاپ'
|
||||||
|
placeholder='مثال: فروشگاه آنلاین'
|
||||||
|
{...formik.getFieldProps('shopName')}
|
||||||
|
error_text={formik.touched.shopName && formik.errors.shopName ? formik.errors.shopName : ''}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label='شماره تلفن'
|
||||||
|
placeholder='مثال: ۰۹۱۲۳۴۵۶۷۸۹'
|
||||||
|
{...formik.getFieldProps('telephoneNumber')}
|
||||||
|
error_text={formik.touched.telephoneNumber && formik.errors.telephoneNumber ? formik.errors.telephoneNumber : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label='کد پستی'
|
||||||
|
placeholder='مثال: ۱۲۳۴۵۶۷۸۹۰'
|
||||||
|
{...formik.getFieldProps('shopPostalCode')}
|
||||||
|
error_text={formik.touched.shopPostalCode && formik.errors.shopPostalCode ? formik.errors.shopPostalCode : ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Textarea
|
||||||
|
label='توضیحات شاپ'
|
||||||
|
placeholder='توضیحات کامل شاپ را وارد کنید...'
|
||||||
|
{...formik.getFieldProps('shopDescription')}
|
||||||
|
error_text={formik.touched.shopDescription && formik.errors.shopDescription ? formik.errors.shopDescription : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* فایلها */}
|
||||||
|
<div className='w-80 space-y-6'>
|
||||||
|
<div className='text-sm bg-white py-8 px-6 rounded-3xl'>
|
||||||
|
<h2 className='text-lg font-medium mb-6'>فایلها</h2>
|
||||||
|
|
||||||
|
<div className='space-y-6'>
|
||||||
|
<div>
|
||||||
|
<UploadBox
|
||||||
|
label='لوگو شاپ'
|
||||||
|
isMultiple={false}
|
||||||
|
onChange={(files) => setLogoFile(files)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Shop
|
||||||
@@ -127,4 +127,21 @@ export const useDeletePricing = () => {
|
|||||||
queryClient.invalidateQueries({ queryKey: ["pricing"] });
|
queryClient.invalidateQueries({ queryKey: ["pricing"] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetShop = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["shop"],
|
||||||
|
queryFn: api.getShop,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateShop = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.updateShop,
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["shop"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
@@ -9,6 +9,8 @@ import type {
|
|||||||
FaqDetailResponse,
|
FaqDetailResponse,
|
||||||
FaqResponse,
|
FaqResponse,
|
||||||
PricingResponse,
|
PricingResponse,
|
||||||
|
ShopResponse,
|
||||||
|
UpdateShopType,
|
||||||
UpdateSiteSetting,
|
UpdateSiteSetting,
|
||||||
} from "../types/Types";
|
} from "../types/Types";
|
||||||
import { FaqPageEnum, SiteSettingPageEnum } from "../enum/Enum";
|
import { FaqPageEnum, SiteSettingPageEnum } from "../enum/Enum";
|
||||||
@@ -103,3 +105,13 @@ export const deletePricing = async (id: string) => {
|
|||||||
const { data } = await axios.delete(`/admin/financial/pricing/${id}`);
|
const { data } = await axios.delete(`/admin/financial/pricing/${id}`);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getShop = async (): Promise<ShopResponse> => {
|
||||||
|
const { data } = await axios.get(`/admin/shop`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateShop = async (params: UpdateShopType) => {
|
||||||
|
const { data } = await axios.patch(`/admin/shop`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -161,3 +161,43 @@ export interface CreatePricingItem {
|
|||||||
type: PricingPageEnum;
|
type: PricingPageEnum;
|
||||||
price: number;
|
price: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ShipmentMethod {
|
||||||
|
_id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
deliveryTime: number;
|
||||||
|
deliveryType: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShopItem {
|
||||||
|
_id: string;
|
||||||
|
shopName: string;
|
||||||
|
shopCode: number;
|
||||||
|
owner: string;
|
||||||
|
shopDescription: string;
|
||||||
|
telephoneNumber: string;
|
||||||
|
isChatActive: boolean;
|
||||||
|
shopPostalCode: string;
|
||||||
|
logo: string;
|
||||||
|
shipmentMethod: ShipmentMethod[];
|
||||||
|
address: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShopResults {
|
||||||
|
shop: ShopItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShopResponse {
|
||||||
|
status: number;
|
||||||
|
success: boolean;
|
||||||
|
results: ShopResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateShopType {
|
||||||
|
shopName: string;
|
||||||
|
shopDescription: string;
|
||||||
|
telephoneNumber: string;
|
||||||
|
shopPostalCode: string;
|
||||||
|
logo: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import ReturnPolicyPage from '@/pages/setting/ReturnPolicyPage'
|
|||||||
import JobsPage from '@/pages/setting/JobsPage'
|
import JobsPage from '@/pages/setting/JobsPage'
|
||||||
import ManageDocument from '@/pages/setting/ManageDocument'
|
import ManageDocument from '@/pages/setting/ManageDocument'
|
||||||
import Pricing from '@/pages/setting/Pricing'
|
import Pricing from '@/pages/setting/Pricing'
|
||||||
|
import Shop from '@/pages/setting/Shop'
|
||||||
|
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
|
|
||||||
@@ -135,6 +136,7 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Pages.pages.jobs} element={<JobsPage />} />
|
<Route path={Pages.pages.jobs} element={<JobsPage />} />
|
||||||
<Route path={Pages.pages.documents} element={<ManageDocument />} />
|
<Route path={Pages.pages.documents} element={<ManageDocument />} />
|
||||||
<Route path={Pages.pages.incomeTariff} element={<Pricing />} />
|
<Route path={Pages.pages.incomeTariff} element={<Pricing />} />
|
||||||
|
<Route path={Pages.setting.shop} element={<Shop />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -762,7 +762,7 @@ const SettingsSubMenu: FC = () => {
|
|||||||
<SubMenuItem
|
<SubMenuItem
|
||||||
title={'تنظیمات فروشگاه'}
|
title={'تنظیمات فروشگاه'}
|
||||||
isActive={isActive('shop')}
|
isActive={isActive('shop')}
|
||||||
link="/settings/shop"
|
link={Pages.setting.shop}
|
||||||
/>
|
/>
|
||||||
<SubMenuItem
|
<SubMenuItem
|
||||||
title={'تنظیمات فوتر'}
|
title={'تنظیمات فوتر'}
|
||||||
|
|||||||
Reference in New Issue
Block a user