establishedYear
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
hamid zarghami
2026-06-20 09:38:43 +03:30
parent 1fa2af4139
commit 2cbd8be816
2 changed files with 221 additions and 242 deletions
+128 -150
View File
@@ -1,130 +1,133 @@
import Button from '@/components/Button' import Button from "@/components/Button";
import ColorPicker from '@/components/ColorPicker' import ColorPicker from "@/components/ColorPicker";
import Input from '@/components/Input' import DefaulModal from "@/components/DefaulModal";
import Textarea from '@/components/Textarea' import Input from "@/components/Input";
import UploadBox from '@/components/UploadBox' import Textarea from "@/components/Textarea";
import DefaulModal from '@/components/DefaulModal' import UploadBox from "@/components/UploadBox";
import LocationPicker from './components/LocationPicker' import { extractErrorMessage } from "@/config/func";
import LocationInput from './components/LocationInput' import type { ErrorType } from "@/helpers/types";
import { TickCircle } from 'iconsax-react' import { useFormik } from "formik";
import { type FC, useEffect, useState } from 'react' import { TickCircle } from "iconsax-react";
import { useGetRestaurant, useUpdateRestaurant } from './hooks/useSettingData' import { type FC, useEffect, useState } from "react";
import { useSingleUpload } from '../uploader/hooks/useUploaderData' import { toast } from "react-toastify";
import type { UpdateRestaurantType } from './types/Types' import * as yup from "yup";
import type { ErrorType } from '@/helpers/types' import { useSingleUpload } from "../uploader/hooks/useUploaderData";
import { useFormik } from 'formik' import LocationInput from "./components/LocationInput";
import * as yup from 'yup' import LocationPicker from "./components/LocationPicker";
import { toast } from 'react-toastify' import ServiceAreaPicker from "./components/ServiceAreaPicker";
import { extractErrorMessage } from '@/config/func' import { useGetRestaurant, useUpdateRestaurant } from "./hooks/useSettingData";
import ServiceAreaPicker from './components/ServiceAreaPicker' import type { UpdateRestaurantType } from "./types/Types";
const validationSchema = yup.object({ const validationSchema = yup.object({
name: yup.string().required('نام فروشگاه الزامی است'), name: yup.string().required("نام فروشگاه الزامی است"),
menuColor: yup.string().required('رنگ منو الزامی است'), menuColor: yup.string().required("رنگ منو الزامی است"),
phone: yup.string().required('شماره تماس الزامی است'), phone: yup.string().required("شماره تماس الزامی است"),
instagram: yup.string().url('لینک اینستاگرام معتبر نیست').nullable(), instagram: yup.string().url("لینک اینستاگرام معتبر نیست").nullable(),
address: yup.string().required('آدرس الزامی است'), address: yup.string().required("آدرس الزامی است"),
latitude: yup.number().required('انتخاب موقعیت روی نقشه الزامی است'), latitude: yup.number().required("انتخاب موقعیت روی نقشه الزامی است"),
longitude: yup.number().required('انتخاب موقعیت روی نقشه الزامی است'), longitude: yup.number().required("انتخاب موقعیت روی نقشه الزامی است"),
description: yup.string().nullable(), description: yup.string().nullable(),
}) establishedYear: yup
.string()
.nullable()
.matches(/^$|^\d{4}$/, "سال تاسیس باید ۴ رقم باشد"),
});
const GeneralSettings: FC = () => { const GeneralSettings: FC = () => {
const { data: restaurant, refetch } = useGetRestaurant() const { data: restaurant, refetch } = useGetRestaurant();
const { mutate: updateRestaurant, isPending } = useUpdateRestaurant() const { mutate: updateRestaurant, isPending } = useUpdateRestaurant();
const { mutate: singleUpload, isPending: isUploading } = useSingleUpload() const { mutate: singleUpload, isPending: isUploading } = useSingleUpload();
const [isLocationModalOpen, setIsLocationModalOpen] = useState(false) const [isLocationModalOpen, setIsLocationModalOpen] = useState(false);
const [logoFile, setLogoFile] = useState<File[]>([]) const [logoFile, setLogoFile] = useState<File[]>([]);
const formik = useFormik<UpdateRestaurantType>({ const formik = useFormik<UpdateRestaurantType>({
initialValues: { initialValues: {
name: '', name: "",
menuColor: '#000000', menuColor: "#000000",
phone: '', phone: "",
instagram: '', instagram: "",
address: '', address: "",
latitude: 0, latitude: 0,
longitude: 0, longitude: 0,
description: '', description: "",
logo: '', logo: "",
serviceArea: undefined, serviceArea: undefined,
establishedYear: "",
}, },
validationSchema, validationSchema,
onSubmit: (values) => { onSubmit: (values) => {
const submitSettings = (logoUrl?: string) => { const submitSettings = (logoUrl?: string) => {
const settingsData: UpdateRestaurantType = { const settingsData: UpdateRestaurantType = {
...values, ...values,
logo: logoUrl || values.logo || '' logo: logoUrl || values.logo || "",
} };
updateRestaurant(settingsData, { updateRestaurant(settingsData, {
onSuccess: () => { onSuccess: () => {
toast.success('تنظیمات با موفقیت ذخیره شد') toast.success("تنظیمات با موفقیت ذخیره شد");
refetch() refetch();
setLogoFile([]) setLogoFile([]);
}, },
onError: (error: unknown) => { onError: (error: unknown) => {
toast.error('خطا در ذخیره تنظیمات') toast.error("خطا در ذخیره تنظیمات");
console.error(error) console.error(error);
}, },
}) });
} };
if (logoFile.length > 0) { if (logoFile.length > 0) {
singleUpload(logoFile[0], { singleUpload(logoFile[0], {
onSuccess: (response) => { onSuccess: (response) => {
const logoUrl = response?.data?.url || '' const logoUrl = response?.data?.url || "";
submitSettings(logoUrl) submitSettings(logoUrl);
}, },
onError: (error: ErrorType) => { onError: (error: ErrorType) => {
toast.error(extractErrorMessage(error, 'خطا در آپلود لوگو')) toast.error(extractErrorMessage(error, "خطا در آپلود لوگو"));
}, },
}) });
} else { } else {
submitSettings() submitSettings();
} }
}, },
enableReinitialize: true, enableReinitialize: true,
}) });
useEffect(() => { useEffect(() => {
if (restaurant?.data) { if (restaurant?.data) {
const restaurantData = restaurant.data const restaurantData = restaurant.data;
formik.setValues({ formik.setValues({
name: restaurantData.name || '', name: restaurantData.name || "",
menuColor: restaurantData.menuColor || '#000000', menuColor: restaurantData.menuColor || "#000000",
phone: restaurantData.phone || '', phone: restaurantData.phone || "",
instagram: restaurantData.instagram || '', instagram: restaurantData.instagram || "",
address: restaurantData.address || '', address: restaurantData.address || "",
latitude: restaurantData.latitude || 0, latitude: restaurantData.latitude || 0,
longitude: restaurantData.longitude || 0, longitude: restaurantData.longitude || 0,
description: restaurantData.description || '', description: restaurantData.description || "",
logo: restaurantData.logo || '', logo: restaurantData.logo || "",
serviceArea: restaurantData.serviceArea || undefined, serviceArea: restaurantData.serviceArea || undefined,
}) establishedYear: restaurantData.establishedYear?.toString() || "",
});
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [restaurant]) }, [restaurant]);
const handleLocationSelect = (lat: number, lng: number) => { const handleLocationSelect = (lat: number, lng: number) => {
formik.setFieldValue('latitude', lat) formik.setFieldValue("latitude", lat);
formik.setFieldValue('longitude', lng) formik.setFieldValue("longitude", lng);
setIsLocationModalOpen(false) setIsLocationModalOpen(false);
} };
return ( return (
<> <>
<div className='bg-white rounded-4xl p-8'> <div className="bg-white rounded-4xl p-8">
<div className='text-lg font-light'> <div className="text-lg font-light">تنظیمات عمومی</div>
تنظیمات عمومی
</div>
<form onSubmit={formik.handleSubmit}> <form onSubmit={formik.handleSubmit}>
<div className='mt-8 rowTwoInput'> <div className="mt-8 rowTwoInput">
<Input <Input
label='نام فروشگاه' label="نام فروشگاه"
name='name' name="name"
value={formik.values.name} value={formik.values.name}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -132,33 +135,27 @@ const GeneralSettings: FC = () => {
/> />
<ColorPicker <ColorPicker
label='رنگ منو' label="رنگ منو"
value={formik.values.menuColor} value={formik.values.menuColor}
onChange={(value) => formik.setFieldValue('menuColor', value)} onChange={(value) => formik.setFieldValue("menuColor", value)}
error_text={formik.touched.menuColor && formik.errors.menuColor ? formik.errors.menuColor : undefined} error_text={formik.touched.menuColor && formik.errors.menuColor ? formik.errors.menuColor : undefined}
/> />
</div> </div>
<div className='mt-8'> <div className="mt-8">
<UploadBox <UploadBox label="لوگو" onChange={(files) => setLogoFile(files)} isReset={logoFile.length === 0 && !formik.values.logo} />
label='لوگو'
onChange={(files) => setLogoFile(files)}
isReset={logoFile.length === 0 && !formik.values.logo}
/>
{ {formik.values.logo && (
formik.values.logo && ( <div className="mt-3">
<div className='mt-3'> <img src={formik.values.logo} alt="logo" className="w-20 h-20 rounded-full" />
<img src={formik.values.logo} alt='logo' className='w-20 h-20 rounded-full' />
</div> </div>
) )}
}
</div> </div>
<div className='mt-8 rowTwoInput'> <div className="mt-8 rowTwoInput">
<Input <Input
label='شماره تماس' label="شماره تماس"
name='phone' name="phone"
value={formik.values.phone} value={formik.values.phone}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -166,20 +163,33 @@ const GeneralSettings: FC = () => {
/> />
<Input <Input
label='اینستاگرام' label="اینستاگرام"
name='instagram' name="instagram"
value={formik.values.instagram} value={formik.values.instagram}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
error_text={formik.touched.instagram && formik.errors.instagram ? formik.errors.instagram : undefined} error_text={formik.touched.instagram && formik.errors.instagram ? formik.errors.instagram : undefined}
placeholder='https://instagram.com/...' placeholder="https://instagram.com/..."
/> />
</div> </div>
<div className='mt-8'> <div className="mt-8 rowTwoInput">
<Input
label="سال تاسیس"
name="establishedYear"
type="number"
value={formik.values.establishedYear}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error_text={formik.touched.establishedYear && formik.errors.establishedYear ? formik.errors.establishedYear : undefined}
placeholder="مثال: 1400"
/>
</div>
<div className="mt-8">
<Textarea <Textarea
label='آدرس' label="آدرس"
name='address' name="address"
value={formik.values.address} value={formik.values.address}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
@@ -188,48 +198,33 @@ const GeneralSettings: FC = () => {
</div> </div>
<LocationInput <LocationInput
label='موقعیت روی نقشه' label="موقعیت روی نقشه"
latitude={formik.values.latitude} latitude={formik.values.latitude}
longitude={formik.values.longitude} longitude={formik.values.longitude}
onClick={() => setIsLocationModalOpen(true)} onClick={() => setIsLocationModalOpen(true)}
error_text={ error_text={(formik.touched.latitude || formik.touched.longitude) && (formik.errors.latitude || formik.errors.longitude) ? formik.errors.latitude || formik.errors.longitude : undefined}
(formik.touched.latitude || formik.touched.longitude) && (formik.errors.latitude || formik.errors.longitude)
? formik.errors.latitude || formik.errors.longitude
: undefined
}
/> />
<div className='mt-8'> <div className="mt-8">
<Textarea <Textarea
label='توضیحات' label="توضیحات"
name='description' name="description"
value={formik.values.description || ''} value={formik.values.description || ""}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
error_text={formik.touched.description && formik.errors.description ? formik.errors.description : undefined} error_text={formik.touched.description && formik.errors.description ? formik.errors.description : undefined}
/> />
</div> </div>
<div className='mt-8'> <div className="mt-8">
<ServiceAreaPicker <ServiceAreaPicker initialPolygon={formik.values.serviceArea} onPolygonSelect={(polygon) => formik.setFieldValue("serviceArea", polygon)} />
initialPolygon={formik.values.serviceArea}
onPolygonSelect={(polygon) => formik.setFieldValue('serviceArea', polygon)}
/>
</div> </div>
{/* </div> */} {/* </div> */}
<div className='mt-8 flex justify-end'> <div className="mt-8 flex justify-end">
<Button <Button type="submit" className="w-fit px-6" isloading={isPending || isUploading} disabled={isPending || isUploading}>
type='submit' <div className="flex gap-2 items-center">
className='w-fit px-6' <TickCircle size={20} color="#fff" />
isloading={isPending || isUploading}
disabled={isPending || isUploading}
>
<div className='flex gap-2 items-center'>
<TickCircle
size={20}
color='#fff'
/>
<span>ذخیره</span> <span>ذخیره</span>
</div> </div>
</Button> </Button>
@@ -237,31 +232,14 @@ const GeneralSettings: FC = () => {
</form> </form>
</div> </div>
<DefaulModal open={isLocationModalOpen} close={() => setIsLocationModalOpen(false)} isHeader={true} title_header="انتخاب موقعیت روی نقشه" width={800}>
<div className="p-4">
<DefaulModal <LocationPicker initialPosition={formik.values.latitude && formik.values.longitude ? [formik.values.latitude, formik.values.longitude] : undefined} onLocationSelect={handleLocationSelect} />
open={isLocationModalOpen} <div className="mt-4 text-xs text-gray-600 text-center">روی نقشه کلیک کنید تا موقعیت را انتخاب کنید</div>
close={() => setIsLocationModalOpen(false)}
isHeader={true}
title_header='انتخاب موقعیت روی نقشه'
width={800}
>
<div className='p-4'>
<LocationPicker
initialPosition={
formik.values.latitude && formik.values.longitude
? [formik.values.latitude, formik.values.longitude]
: undefined
}
onLocationSelect={handleLocationSelect}
/>
<div className='mt-4 text-xs text-gray-600 text-center'>
روی نقشه کلیک کنید تا موقعیت را انتخاب کنید
</div>
</div> </div>
</DefaulModal> </DefaulModal>
</> </>
) );
} };
export default GeneralSettings export default GeneralSettings;
+1
View File
@@ -17,6 +17,7 @@ export type UpdateRestaurantType = {
latitude?: number; latitude?: number;
longitude?: number; longitude?: number;
description?: string; description?: string;
establishedYear?: string;
serviceArea?: GeoJSONPolygon; serviceArea?: GeoJSONPolygon;
score?: { score?: {
purchaseAmount: string; purchaseAmount: string;