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