change food to product name

This commit is contained in:
hamid zarghami
2026-02-09 14:04:21 +03:30
parent eae2ba0e7a
commit 687fc1e6c2
28 changed files with 186 additions and 194 deletions
+5 -5
View File
@@ -15,11 +15,11 @@ export const Pages = {
add: "/payment_methods/create", add: "/payment_methods/create",
update: "/payment_methods/update/", update: "/payment_methods/update/",
}, },
foods: { products: {
list: "/foods/list", list: "/products/list",
add: "/foods/add", add: "/products/add",
category: "/foods/category", category: "/products/category",
update: "/foods/update/", update: "/products/update/",
}, },
orders: { orders: {
list: "/orders/list", list: "/orders/list",
+1 -1
View File
@@ -1,5 +1,5 @@
export const SideBarItemHasSubMenu = [ export const SideBarItemHasSubMenu = [
"foods", "products",
"orders", "orders",
"customers", "customers",
"discounts", "discounts",
+5 -5
View File
@@ -7,8 +7,8 @@ import { useNavigate } from 'react-router-dom'
import Button from '@/components/Button' import Button from '@/components/Button'
import type { CreateCouponType } from './types/Types' import type { CreateCouponType } from './types/Types'
import { useCreateCoupon } from './hooks/useCouponData' import { useCreateCoupon } from './hooks/useCouponData'
import { useGetCategories } from '@/pages/food/hooks/useFoodData' import { useGetCategories } from '@/pages/product/hooks/useProductData'
import { useGetFoods } from '@/pages/food/hooks/useFoodData' import { useGetProducts } from '@/pages/product/hooks/useProductData'
import { Pages } from '@/config/Pages' import { Pages } from '@/config/Pages'
import type { ErrorType } from '@/helpers/types' import type { ErrorType } from '@/helpers/types'
import { extractErrorMessage } from '@/config/func' import { extractErrorMessage } from '@/config/func'
@@ -21,12 +21,12 @@ const CreateCoupon: FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
const { mutate: createCoupon, isPending: isCreating } = useCreateCoupon() const { mutate: createCoupon, isPending: isCreating } = useCreateCoupon()
const { data: categoriesData } = useGetCategories() const { data: categoriesData } = useGetCategories()
const { data: foodsData } = useGetFoods() const { data: productsData } = useGetProducts()
const [couponType, setCouponType] = useState<'PERCENTAGE' | 'FIXED'>('PERCENTAGE') const [couponType, setCouponType] = useState<'PERCENTAGE' | 'FIXED'>('PERCENTAGE')
const categories = useMemo(() => categoriesData?.data || [], [categoriesData?.data]) const categories = useMemo(() => categoriesData?.data || [], [categoriesData?.data])
const foods = useMemo(() => foodsData?.data || [], [foodsData?.data]) const products = useMemo(() => productsData?.data || [], [productsData?.data])
const formik = useFormik<CreateCouponType>({ const formik = useFormik<CreateCouponType>({
initialValues: { initialValues: {
@@ -119,7 +119,7 @@ const CreateCoupon: FC = () => {
<CouponSidebar <CouponSidebar
formik={formik} formik={formik}
categories={categories} categories={categories}
foods={foods} foods={products}
/> />
</div> </div>
</div> </div>
+5 -5
View File
@@ -7,8 +7,8 @@ import { toast } from 'react-toastify'
import Button from '@/components/Button' import Button from '@/components/Button'
import type { CreateCouponType } from './types/Types' import type { CreateCouponType } from './types/Types'
import { useGetCouponDetails, useUpdateCoupon } from './hooks/useCouponData' import { useGetCouponDetails, useUpdateCoupon } from './hooks/useCouponData'
import { useGetCategories } from '@/pages/food/hooks/useFoodData' import { useGetCategories } from '@/pages/product/hooks/useProductData'
import { useGetFoods } from '@/pages/food/hooks/useFoodData' import { useGetProducts } from '@/pages/product/hooks/useProductData'
import { Pages } from '@/config/Pages' import { Pages } from '@/config/Pages'
import type { ErrorType } from '@/helpers/types' import type { ErrorType } from '@/helpers/types'
import { extractErrorMessage } from '@/config/func' import { extractErrorMessage } from '@/config/func'
@@ -23,12 +23,12 @@ const UpdateCoupon: FC = () => {
const { data: couponData, isLoading } = useGetCouponDetails(id || '') const { data: couponData, isLoading } = useGetCouponDetails(id || '')
const { mutate: updateCoupon, isPending: isUpdating } = useUpdateCoupon() const { mutate: updateCoupon, isPending: isUpdating } = useUpdateCoupon()
const { data: categoriesData } = useGetCategories() const { data: categoriesData } = useGetCategories()
const { data: foodsData } = useGetFoods() const { data: productsData } = useGetProducts()
const [couponType, setCouponType] = useState<'PERCENTAGE' | 'FIXED'>('PERCENTAGE') const [couponType, setCouponType] = useState<'PERCENTAGE' | 'FIXED'>('PERCENTAGE')
const categories = useMemo(() => categoriesData?.data || [], [categoriesData?.data]) const categories = useMemo(() => categoriesData?.data || [], [categoriesData?.data])
const foods = useMemo(() => foodsData?.data || [], [foodsData?.data]) const products = useMemo(() => productsData?.data || [], [productsData?.data])
const formik = useFormik<CreateCouponType>({ const formik = useFormik<CreateCouponType>({
initialValues: { initialValues: {
@@ -167,7 +167,7 @@ const UpdateCoupon: FC = () => {
<CouponSidebar <CouponSidebar
formik={formik} formik={formik}
categories={categories} categories={categories}
foods={foods} foods={products}
/> />
</div> </div>
</div> </div>
@@ -13,7 +13,7 @@ import {
useCreateCategory, useCreateCategory,
useUpdateCategory, useUpdateCategory,
useDeleteCategory useDeleteCategory
} from './hooks/useFoodData' } from './hooks/useProductData'
import { useSingleUpload } from '../uploader/hooks/useUploaderData' import { useSingleUpload } from '../uploader/hooks/useUploaderData'
import { extractErrorMessage } from '@/config/func' import { extractErrorMessage } from '@/config/func'
@@ -24,7 +24,7 @@ type CategoryFormValues = {
parentId?: string, parentId?: string,
} }
const CategoryFood: FC = () => { const CategoryProduct: FC = () => {
const [isActive, setIsActive] = useState<boolean>(true) const [isActive, setIsActive] = useState<boolean>(true)
const [iconFiles, setIconFiles] = useState<File[]>([]) const [iconFiles, setIconFiles] = useState<File[]>([])
const [selectedIconUrl, setSelectedIconUrl] = useState<string>('') const [selectedIconUrl, setSelectedIconUrl] = useState<string>('')
@@ -226,4 +226,4 @@ const CategoryFood: FC = () => {
) )
} }
export default CategoryFood export default CategoryProduct
@@ -8,24 +8,24 @@ import Select from '@/components/Select'
import Textarea from '@/components/Textarea' import Textarea from '@/components/Textarea'
import Button from '@/components/Button' import Button from '@/components/Button'
import { Checkbox } from '@/components/ui/checkbox' import { Checkbox } from '@/components/ui/checkbox'
import CreateFoodSidebar from './components/CreateFoodSidebar' import CreateProductSidebar from './components/CreateProductSidebar'
import WeekDaysSection from './components/WeekDaysSection' import WeekDaysSection from './components/WeekDaysSection'
import MealTimesSection from './components/MealTimesSection' import MealTimesSection from './components/MealTimesSection'
import type { CreateFoodType } from './types/Types' import type { CreateProductType } from './types/Types'
import { useCreateFood, useGetCategories } from './hooks/useFoodData' import { useCreateProduct, useGetCategories } from './hooks/useProductData'
import { useMultipleUpload } from '../uploader/hooks/useUploaderData' import { useMultipleUpload } from '../uploader/hooks/useUploaderData'
import { Pages } from '@/config/Pages' import { Pages } from '@/config/Pages'
import type { ErrorType } from '@/helpers/types' import type { ErrorType } from '@/helpers/types'
const CreateFood: FC = () => { const CreateProduct: FC = () => {
const { data } = useGetCategories(); const { data } = useGetCategories();
const [isActive, setIsActive] = useState<boolean>(true) const [isActive, setIsActive] = useState<boolean>(true)
const [isSpecial, setIsSpecial] = useState<boolean>(false) const [isSpecial, setIsSpecial] = useState<boolean>(false)
const [imageFiles, setImageFiles] = useState<File[]>([]) const [imageFiles, setImageFiles] = useState<File[]>([])
const { mutate: createFood, isPending } = useCreateFood() const { mutate: createProduct, isPending } = useCreateProduct()
const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload() const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload()
const categories = data?.data?.map(category => ({ const categories = data?.data?.map(category => ({
@@ -33,7 +33,7 @@ const CreateFood: FC = () => {
value: category.id value: category.id
})) || [] })) || []
const formik = useFormik<CreateFoodType>({ const formik = useFormik<CreateProductType>({
initialValues: { initialValues: {
title: '', title: '',
desc: '', desc: '',
@@ -64,8 +64,8 @@ const CreateFood: FC = () => {
mealTypes: Yup.array().of(Yup.string()) mealTypes: Yup.array().of(Yup.string())
}), }),
onSubmit: (values) => { onSubmit: (values) => {
const submitFood = (imageUrls: string[] = []) => { const submitProduct = (imageUrls: string[] = []) => {
const foodData: CreateFoodType = { const productData: CreateProductType = {
...values, ...values,
isActive, isActive,
isSpecialOffer: isSpecial, isSpecialOffer: isSpecial,
@@ -73,10 +73,10 @@ const CreateFood: FC = () => {
content: values.content.filter(item => item && item.trim().length > 0) content: values.content.filter(item => item && item.trim().length > 0)
} }
createFood(foodData, { createProduct(productData, {
onSuccess: () => { onSuccess: () => {
toast.success('کالا با موفقیت ایجاد شد') toast.success('کالا با موفقیت ایجاد شد')
window.location.href = Pages.foods.list window.location.href = Pages.products.list
}, },
onError: (error: ErrorType) => { onError: (error: ErrorType) => {
toast.error(error?.response?.data?.error?.message[0] || 'خطا در ایجاد کالا') toast.error(error?.response?.data?.error?.message[0] || 'خطا در ایجاد کالا')
@@ -88,14 +88,14 @@ const CreateFood: FC = () => {
multipleUpload(imageFiles, { multipleUpload(imageFiles, {
onSuccess: (response) => { onSuccess: (response) => {
const imageUrls = response?.data?.map(item => item.url) || [] const imageUrls = response?.data?.map(item => item.url) || []
submitFood(imageUrls) submitProduct(imageUrls)
}, },
onError: (error: ErrorType) => { onError: (error: ErrorType) => {
toast.error(error?.response?.data?.error?.message[0] || 'خطا در آپلود تصاویر') toast.error(error?.response?.data?.error?.message[0] || 'خطا در آپلود تصاویر')
} }
}) })
} else { } else {
submitFood() submitProduct()
} }
} }
}) })
@@ -279,7 +279,7 @@ const CreateFood: FC = () => {
</div> </div>
<div className='w-full lg:w-auto'> <div className='w-full lg:w-auto'>
<CreateFoodSidebar <CreateProductSidebar
isActive={isActive} isActive={isActive}
isSpecial={isSpecial} isSpecial={isSpecial}
onChangeActive={setIsActive} onChangeActive={setIsActive}
@@ -292,4 +292,4 @@ const CreateFood: FC = () => {
) )
} }
export default CreateFood export default CreateProduct
@@ -3,42 +3,42 @@ import Table from '@/components/Table'
import Filters from '@/components/Filters' import Filters from '@/components/Filters'
import { Add } from 'iconsax-react' import { Add } from 'iconsax-react'
import Button from '@/components/Button' import Button from '@/components/Button'
import { useGetFoods, useDeleteFood } from './hooks/useFoodData' import { useGetProducts, useDeleteProduct } from './hooks/useProductData'
import { useFoodFilters } from './hooks/useFoodFilters' import { useProductFilters } from './hooks/useProductFilters'
import { getFoodTableColumns } from './components/FoodTableColumns' import { getProductTableColumns } from './components/ProductTableColumns'
import { useFoodFiltersFields } from './components/FoodFiltersFields' import { useProductFiltersFields } from './components/ProductFiltersFields'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Pages } from '@/config/Pages' import { Pages } from '@/config/Pages'
const FoodsList: FC = () => { const ProductsList: FC = () => {
const { const {
filters, filters,
currentPage, currentPage,
apiParams, apiParams,
handleFiltersChange, handleFiltersChange,
handlePageChange, handlePageChange,
} = useFoodFilters() } = useProductFilters()
const { data: foodsData, isLoading } = useGetFoods(apiParams, filters) const { data: productsData, isLoading } = useGetProducts(apiParams, filters)
const { mutate: deleteFood, isPending: isDeleting } = useDeleteFood() const { mutate: deleteProduct, isPending: isDeleting } = useDeleteProduct()
const foods = foodsData?.data || [] const products = productsData?.data || []
const columns = getFoodTableColumns({ const columns = getProductTableColumns({
onDelete: (id: string) => deleteFood(id), onDelete: (id: string) => deleteProduct(id),
isDeleting isDeleting
}) })
const filterFields = useFoodFiltersFields() const filterFields = useProductFiltersFields()
const totalPages = foodsData?.meta?.totalPages || 1 const totalPages = productsData?.meta?.totalPages || 1
return ( return (
<div className='mt-5'> <div className='mt-5'>
<div className='flex justify-between items-center'> <div className='flex justify-between items-center'>
<h1 className='text-lg font-light'>لیست کالاها</h1> <h1 className='text-lg font-light'>لیست کالاها</h1>
<Link <Link
to={Pages.foods.add} to={Pages.products.add}
> >
<Button className='w-fit px-6'> <Button className='w-fit px-6'>
<div className='flex gap-2 items-center'> <div className='flex gap-2 items-center'>
@@ -60,7 +60,7 @@ const FoodsList: FC = () => {
<Table <Table
columns={columns} columns={columns}
data={foods} data={products}
isloading={isLoading} isloading={isLoading}
pagination={{ pagination={{
currentPage, currentPage,
@@ -72,4 +72,4 @@ const FoodsList: FC = () => {
) )
} }
export default FoodsList export default ProductsList
@@ -5,28 +5,28 @@ import { TickCircle } from 'iconsax-react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { useNavigate, useParams } from 'react-router-dom' import { useNavigate, useParams } from 'react-router-dom'
import Button from '@/components/Button' import Button from '@/components/Button'
import CreateFoodSidebar from './components/CreateFoodSidebar' import CreateProductSidebar from './components/CreateProductSidebar'
import BasicInfoSection from './components/BasicInfoSection' import BasicInfoSection from './components/BasicInfoSection'
import FoodContentSection from './components/FoodContentSection' import ProductContentSection from './components/ProductContentSection'
import MealTimesSection from './components/MealTimesSection' import MealTimesSection from './components/MealTimesSection'
import WeekDaysSection from './components/WeekDaysSection' import WeekDaysSection from './components/WeekDaysSection'
import ServiceOptionsSection from './components/ServiceOptionsSection' import ServiceOptionsSection from './components/ServiceOptionsSection'
import type { CreateFoodType } from './types/Types' import type { CreateProductType } from './types/Types'
import { useUpdateFood, useGetFoodDetails, useGetCategories } from './hooks/useFoodData' import { useUpdateProduct, useGetProductDetails, useGetCategories } from './hooks/useProductData'
import { useMultipleUpload } from '../uploader/hooks/useUploaderData' import { useMultipleUpload } from '../uploader/hooks/useUploaderData'
import { Pages } from '@/config/Pages' import { Pages } from '@/config/Pages'
import type { ErrorType } from '@/helpers/types' import type { ErrorType } from '@/helpers/types'
import { extractErrorMessage } from '@/config/func' import { extractErrorMessage } from '@/config/func'
const UpdateFood: FC = () => { const UpdateProduct: FC = () => {
const { id } = useParams<{ id: string }>() const { id } = useParams<{ id: string }>()
const { data: foodData, isLoading } = useGetFoodDetails(id || '') const { data: productData, isLoading } = useGetProductDetails(id || '')
const { data: categoriesData } = useGetCategories() const { data: categoriesData } = useGetCategories()
const [isActive, setIsActive] = useState<boolean>(true) const [isActive, setIsActive] = useState<boolean>(true)
const [isSpecial, setIsSpecial] = useState<boolean>(false) const [isSpecial, setIsSpecial] = useState<boolean>(false)
const [imageFiles, setImageFiles] = useState<File[]>([]) const [imageFiles, setImageFiles] = useState<File[]>([])
const [existingImages, setExistingImages] = useState<string[]>([]) const [existingImages, setExistingImages] = useState<string[]>([])
const { mutate: updateFood, isPending } = useUpdateFood() const { mutate: updateProduct, isPending } = useUpdateProduct()
const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload() const { mutate: multipleUpload, isPending: isUploading } = useMultipleUpload()
const navigate = useNavigate() const navigate = useNavigate()
const categories = categoriesData?.data?.map(category => ({ const categories = categoriesData?.data?.map(category => ({
@@ -34,9 +34,9 @@ const UpdateFood: FC = () => {
value: category.id value: category.id
})) || [] })) || []
const food = foodData?.data const product = productData?.data
const formik = useFormik<CreateFoodType>({ const formik = useFormik<CreateProductType>({
initialValues: { initialValues: {
title: '', title: '',
desc: '', desc: '',
@@ -72,9 +72,9 @@ const UpdateFood: FC = () => {
return return
} }
const submitFood = (newImageUrls: string[] = []) => { const submitProduct = (newImageUrls: string[] = []) => {
const allImages = [...values.images, ...newImageUrls] const allImages = [...values.images, ...newImageUrls]
const foodData: CreateFoodType = { const productData: CreateProductType = {
...values, ...values,
isActive, isActive,
isSpecialOffer: isSpecial, isSpecialOffer: isSpecial,
@@ -82,10 +82,10 @@ const UpdateFood: FC = () => {
content: values.content.filter(item => item && item.trim().length > 0) content: values.content.filter(item => item && item.trim().length > 0)
} }
updateFood({ id, params: foodData }, { updateProduct({ id, params: productData }, {
onSuccess: () => { onSuccess: () => {
toast.success('کالا با موفقیت به‌روزرسانی شد') toast.success('کالا با موفقیت به‌روزرسانی شد')
navigate(Pages.foods.list) navigate(Pages.products.list)
}, },
onError: (error: ErrorType) => { onError: (error: ErrorType) => {
toast.error(extractErrorMessage(error)) toast.error(extractErrorMessage(error))
@@ -97,44 +97,44 @@ const UpdateFood: FC = () => {
multipleUpload(imageFiles, { multipleUpload(imageFiles, {
onSuccess: (response) => { onSuccess: (response) => {
const imageUrls = response?.data?.map(item => item.url) || [] const imageUrls = response?.data?.map(item => item.url) || []
submitFood(imageUrls) submitProduct(imageUrls)
}, },
onError: (error: ErrorType) => { onError: (error: ErrorType) => {
toast.error(error?.response?.data?.error?.message[0] || 'خطا در آپلود تصاویر') toast.error(error?.response?.data?.error?.message[0] || 'خطا در آپلود تصاویر')
} }
}) })
} else { } else {
submitFood() submitProduct()
} }
} }
}) })
useEffect(() => { useEffect(() => {
if (food) { if (product) {
formik.setValues({ formik.setValues({
title: food.title || '', title: product.title || '',
desc: food.desc || '', desc: product.desc || '',
content: food.content || [], content: product.content || [],
categoryId: food.category?.id || '', categoryId: product.category?.id || '',
price: food.price || 0, price: product.price || 0,
discount: food.discount || 0, discount: product.discount || 0,
prepareTime: food.prepareTime || 0, prepareTime: product.prepareTime || 0,
weekDays: food.weekDays || [], weekDays: product.weekDays || [],
mealTypes: food.mealTypes || [], mealTypes: product.mealTypes || [],
pickupServe: food.pickupServe || false, pickupServe: product.pickupServe || false,
inPlaceServe: food.inPlaceServe || false, inPlaceServe: product.inPlaceServe || false,
isActive: food.isActive || false, isActive: product.isActive || false,
images: food.images || [], images: product.images || [],
isSpecialOffer: food.isSpecialOffer || false, isSpecialOffer: product.isSpecialOffer || false,
dailyStock: food.inventory?.totalStock || 0, dailyStock: product.inventory?.totalStock || 0,
order: food.order || 0 order: product.order || 0
}) })
setIsActive(food.isActive || false) setIsActive(product.isActive || false)
setIsSpecial(food.isSpecialOffer || false) setIsSpecial(product.isSpecialOffer || false)
setExistingImages(food.images || []) setExistingImages(product.images || [])
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [food]) }, [product])
useEffect(() => { useEffect(() => {
formik.setFieldValue('images', existingImages) formik.setFieldValue('images', existingImages)
@@ -149,7 +149,7 @@ const UpdateFood: FC = () => {
) )
} }
if (!food) { if (!product) {
return ( return (
<div className='w-full mt-4 flex justify-center items-center'> <div className='w-full mt-4 flex justify-center items-center'>
<div>کالا یافت نشد</div> <div>کالا یافت نشد</div>
@@ -181,7 +181,7 @@ const UpdateFood: FC = () => {
<div className='flex-1'> <div className='flex-1'>
<div className='bg-white py-6 sm:py-8 xl:px-10 px-4 rounded-3xl'> <div className='bg-white py-6 sm:py-8 xl:px-10 px-4 rounded-3xl'>
<BasicInfoSection formik={formik} categories={categories} /> <BasicInfoSection formik={formik} categories={categories} />
<FoodContentSection formik={formik} /> <ProductContentSection formik={formik} />
<MealTimesSection formik={formik} /> <MealTimesSection formik={formik} />
<WeekDaysSection formik={formik} /> <WeekDaysSection formik={formik} />
<ServiceOptionsSection formik={formik} /> <ServiceOptionsSection formik={formik} />
@@ -189,7 +189,7 @@ const UpdateFood: FC = () => {
</div> </div>
<div className='w-full lg:w-auto'> <div className='w-full lg:w-auto'>
<CreateFoodSidebar <CreateProductSidebar
isActive={isActive} isActive={isActive}
isSpecial={isSpecial} isSpecial={isSpecial}
onChangeActive={setIsActive} onChangeActive={setIsActive}
@@ -204,4 +204,4 @@ const UpdateFood: FC = () => {
) )
} }
export default UpdateFood export default UpdateProduct
@@ -3,10 +3,10 @@ import type { FormikProps } from 'formik'
import Input from '@/components/Input' import Input from '@/components/Input'
import Select from '@/components/Select' import Select from '@/components/Select'
import Textarea from '@/components/Textarea' import Textarea from '@/components/Textarea'
import type { CreateFoodType } from '../types/Types' import type { CreateProductType } from '../types/Types'
type BasicInfoSectionProps = { type BasicInfoSectionProps = {
formik: FormikProps<CreateFoodType> formik: FormikProps<CreateProductType>
categories: Array<{ label: string; value: string }> categories: Array<{ label: string; value: string }>
} }
@@ -109,4 +109,3 @@ const BasicInfoSection: FC<BasicInfoSectionProps> = ({ formik, categories }) =>
} }
export default BasicInfoSection export default BasicInfoSection
@@ -4,7 +4,7 @@ import Input from '@/components/Input'
import SwitchComponent from '@/components/Switch' import SwitchComponent from '@/components/Switch'
import Button from '@/components/Button' import Button from '@/components/Button'
import type { FormikProps } from 'formik' import type { FormikProps } from 'formik'
import { useGetCategories, useGetIcons } from '../hooks/useFoodData' import { useGetCategories, useGetIcons } from '../hooks/useProductData'
import IconSelectModal from './IconSelectModal' import IconSelectModal from './IconSelectModal'
import Select from '@/components/Select' import Select from '@/components/Select'
@@ -165,4 +165,3 @@ const CategoryForm: FC<Props> = ({
} }
export default CategoryForm export default CategoryForm
@@ -78,4 +78,3 @@ const CategoryTableColumns = ({ onStatusChange, onEdit, onDelete, isDeleting = f
} }
export default CategoryTableColumns export default CategoryTableColumns
@@ -13,7 +13,7 @@ type Props = {
onChangeExistingImages?: (images: string[]) => void onChangeExistingImages?: (images: string[]) => void
} }
const CreateFoodSidebar: FC<Props> = (props: Props) => { const CreateProductSidebar: FC<Props> = (props: Props) => {
return ( return (
<div className='bg-white w-full lg:w-[300px] text-xs h-fit px-5 py-7 rounded-3xl'> <div className='bg-white w-full lg:w-[300px] text-xs h-fit px-5 py-7 rounded-3xl'>
@@ -54,4 +54,4 @@ const CreateFoodSidebar: FC<Props> = (props: Props) => {
) )
} }
export default CreateFoodSidebar export default CreateProductSidebar
@@ -114,4 +114,3 @@ const IconSelectModal: FC<Props> = ({
} }
export default IconSelectModal export default IconSelectModal
@@ -1,10 +1,10 @@
import { type FC } from 'react' import { type FC } from 'react'
import type { FormikProps } from 'formik' import type { FormikProps } from 'formik'
import { Checkbox } from '@/components/ui/checkbox' import { Checkbox } from '@/components/ui/checkbox'
import type { CreateFoodType } from '../types/Types' import type { CreateProductType } from '../types/Types'
type MealTimesSectionProps = { type MealTimesSectionProps = {
formik: FormikProps<CreateFoodType> formik: FormikProps<CreateProductType>
} }
const MealTimesSection: FC<MealTimesSectionProps> = ({ formik }) => { const MealTimesSection: FC<MealTimesSectionProps> = ({ formik }) => {
@@ -51,4 +51,3 @@ const MealTimesSection: FC<MealTimesSectionProps> = ({ formik }) => {
} }
export default MealTimesSection export default MealTimesSection
@@ -2,13 +2,13 @@ import { type FC } from 'react'
import type { FormikProps } from 'formik' import type { FormikProps } from 'formik'
import Input from '@/components/Input' import Input from '@/components/Input'
import Button from '@/components/Button' import Button from '@/components/Button'
import type { CreateFoodType } from '../types/Types' import type { CreateProductType } from '../types/Types'
type FoodContentSectionProps = { type ProductContentSectionProps = {
formik: FormikProps<CreateFoodType> formik: FormikProps<CreateProductType>
} }
const FoodContentSection: FC<FoodContentSectionProps> = ({ formik }) => { const ProductContentSection: FC<ProductContentSectionProps> = ({ formik }) => {
return ( return (
<div className='mt-5'> <div className='mt-5'>
@@ -51,4 +51,4 @@ const FoodContentSection: FC<FoodContentSectionProps> = ({ formik }) => {
) )
} }
export default FoodContentSection export default ProductContentSection
@@ -1,8 +1,8 @@
import { useMemo } from 'react' import { useMemo } from 'react'
import type { FieldType } from '@/components/Filters' import type { FieldType } from '@/components/Filters'
import { useGetCategories } from '../hooks/useFoodData' import { useGetCategories } from '../hooks/useProductData'
export const useFoodFiltersFields = (): FieldType[] => { export const useProductFiltersFields = (): FieldType[] => {
const { data } = useGetCategories() const { data } = useGetCategories()
@@ -1,24 +1,24 @@
import { Eye } from 'iconsax-react' import { Eye } from 'iconsax-react'
import type { ColumnType } from '@/components/types/TableTypes' import type { ColumnType } from '@/components/types/TableTypes'
import type { Food } from '../types/Types' import type { Product } from '../types/Types'
import { formatPrice, formatTime } from '../utils/formatters' import { formatPrice, formatTime } from '../utils/formatters'
import Status from '@/components/Status' import Status from '@/components/Status'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { Pages } from '@/config/Pages' import { Pages } from '@/config/Pages'
import TrashWithConfrim from '@/components/TrashWithConfrim' import TrashWithConfrim from '@/components/TrashWithConfrim'
interface GetFoodTableColumnsParams { interface GetProductTableColumnsParams {
onDelete: (id: string) => void onDelete: (id: string) => void
isDeleting: boolean isDeleting: boolean
} }
export const getFoodTableColumns = ({ onDelete, isDeleting }: GetFoodTableColumnsParams): ColumnType<Food>[] => { export const getProductTableColumns = ({ onDelete, isDeleting }: GetProductTableColumnsParams): ColumnType<Product>[] => {
return [ return [
{ {
key: 'image', key: 'image',
title: 'تصویر', title: 'تصویر',
render: (item: Food) => { render: (item: Product) => {
const imageUrl = item.images && item.images.length > 0 ? item.images[0] : '' const imageUrl = item.images && item.images.length > 0 ? item.images[0] : ''
return ( return (
<div className='w-10 h-10 rounded-full overflow-hidden bg-gray-200'> <div className='w-10 h-10 rounded-full overflow-hidden bg-gray-200'>
@@ -40,7 +40,7 @@ export const getFoodTableColumns = ({ onDelete, isDeleting }: GetFoodTableColumn
{ {
key: 'categories', key: 'categories',
title: 'دسته بندی', title: 'دسته بندی',
render: (item: Food) => { render: (item: Product) => {
return item.category?.title return item.category?.title
? item.category.title ? item.category.title
: '-' : '-'
@@ -49,21 +49,21 @@ export const getFoodTableColumns = ({ onDelete, isDeleting }: GetFoodTableColumn
{ {
key: 'price', key: 'price',
title: 'قیمت', title: 'قیمت',
render: (item: Food) => { render: (item: Product) => {
return formatPrice(item.price) return formatPrice(item.price)
} }
}, },
{ {
key: 'prepareTime', key: 'prepareTime',
title: 'زمان آماده سازی', title: 'زمان آماده سازی',
render: (item: Food) => { render: (item: Product) => {
return formatTime(item.prepareTime || 0) return formatTime(item.prepareTime || 0)
} }
}, },
{ {
key: 'inventory', key: 'inventory',
title: 'موجودی', title: 'موجودی',
render: (item: Food) => { render: (item: Product) => {
const availableStock = item.inventory.availableStock const availableStock = item.inventory.availableStock
const totalStock = item.inventory.totalStock const totalStock = item.inventory.totalStock
return ( return (
@@ -81,7 +81,7 @@ export const getFoodTableColumns = ({ onDelete, isDeleting }: GetFoodTableColumn
{ {
key: 'isActive', key: 'isActive',
title: 'وضعیت', title: 'وضعیت',
render: (item: Food) => { render: (item: Product) => {
return ( return (
<Status variant={item.isActive ? 'success' : 'error'} label={item.isActive ? 'فعال' : 'غیرفعال'} /> <Status variant={item.isActive ? 'success' : 'error'} label={item.isActive ? 'فعال' : 'غیرفعال'} />
) )
@@ -90,10 +90,10 @@ export const getFoodTableColumns = ({ onDelete, isDeleting }: GetFoodTableColumn
{ {
key: 'view', key: 'view',
title: '', title: '',
render: (item: Food) => { render: (item: Product) => {
return ( return (
<div className='flex gap-2 items-center'> <div className='flex gap-2 items-center'>
<Link to={Pages.foods.update + item.id} className='flex gap-2 items-center'> <Link to={Pages.products.update + item.id} className='flex gap-2 items-center'>
<Eye size={20} color='#8C90A3' /> <Eye size={20} color='#8C90A3' />
</Link> </Link>
<TrashWithConfrim <TrashWithConfrim
@@ -106,4 +106,3 @@ export const getFoodTableColumns = ({ onDelete, isDeleting }: GetFoodTableColumn
}, },
] ]
} }
@@ -1,10 +1,10 @@
import { type FC } from 'react' import { type FC } from 'react'
import type { FormikProps } from 'formik' import type { FormikProps } from 'formik'
import { Checkbox } from '@/components/ui/checkbox' import { Checkbox } from '@/components/ui/checkbox'
import type { CreateFoodType } from '../types/Types' import type { CreateProductType } from '../types/Types'
type ServiceOptionsSectionProps = { type ServiceOptionsSectionProps = {
formik: FormikProps<CreateFoodType> formik: FormikProps<CreateProductType>
} }
const ServiceOptionsSection: FC<ServiceOptionsSectionProps> = ({ formik }) => { const ServiceOptionsSection: FC<ServiceOptionsSectionProps> = ({ formik }) => {
@@ -32,4 +32,3 @@ const ServiceOptionsSection: FC<ServiceOptionsSectionProps> = ({ formik }) => {
} }
export default ServiceOptionsSection export default ServiceOptionsSection
@@ -1,10 +1,10 @@
import { type FC } from 'react' import { type FC } from 'react'
import type { FormikProps } from 'formik' import type { FormikProps } from 'formik'
import { Checkbox } from '@/components/ui/checkbox' import { Checkbox } from '@/components/ui/checkbox'
import type { CreateFoodType } from '../types/Types' import type { CreateProductType } from '../types/Types'
type WeekDaysSectionProps = { type WeekDaysSectionProps = {
formik: FormikProps<CreateFoodType> formik: FormikProps<CreateProductType>
} }
const WeekDaysSection: FC<WeekDaysSectionProps> = ({ formik }) => { const WeekDaysSection: FC<WeekDaysSectionProps> = ({ formik }) => {
@@ -48,4 +48,3 @@ const WeekDaysSection: FC<WeekDaysSectionProps> = ({ formik }) => {
} }
export default WeekDaysSection export default WeekDaysSection
@@ -1,39 +1,39 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import * as api from "../service/FoodService"; import * as api from "../service/ProductService";
import type { import type {
CreateCategoryType, CreateCategoryType,
CreateFoodType, CreateProductType,
GetFoodsParams, GetProductsParams,
} from "../types/Types"; } from "../types/Types";
import type { FilterValues } from "@/components/Filters"; import type { FilterValues } from "@/components/Filters";
export const useCreateFood = () => { export const useCreateProduct = () => {
return useMutation({ return useMutation({
mutationFn: api.createFood, mutationFn: api.createProduct,
}); });
}; };
export const useGetFoods = ( export const useGetProducts = (
params?: GetFoodsParams, params?: GetProductsParams,
filters?: FilterValues, filters?: FilterValues,
) => { ) => {
return useQuery({ return useQuery({
queryKey: ["foods", params, filters], queryKey: ["products", params, filters],
queryFn: () => api.getFoods(params, filters), queryFn: () => api.getProducts(params, filters),
}); });
}; };
export const useGetFoodDetails = (id: string) => { export const useGetProductDetails = (id: string) => {
return useQuery({ return useQuery({
queryKey: ["food", id], queryKey: ["product", id],
queryFn: () => api.getFoodDetails(id), queryFn: () => api.getProductDetails(id),
}); });
}; };
export const useUpdateFood = () => { export const useUpdateProduct = () => {
return useMutation({ return useMutation({
mutationFn: ({ id, params }: { id: string; params: CreateFoodType }) => mutationFn: ({ id, params }: { id: string; params: CreateProductType }) =>
api.updateFood(id, params), api.updateProduct(id, params),
}); });
}; };
@@ -44,12 +44,12 @@ export const useGetCategories = () => {
}); });
}; };
export const useDeleteFood = () => { export const useDeleteProduct = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: api.deleteFood, mutationFn: api.deleteProduct,
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["foods"] }); queryClient.invalidateQueries({ queryKey: ["products"] });
}, },
}); });
}; };
@@ -1,17 +1,17 @@
import { useState, useMemo } from "react"; import { useState, useMemo } from "react";
import type { FilterValues } from "@/components/Filters"; import type { FilterValues } from "@/components/Filters";
import type { GetFoodsParams } from "../types/Types"; import type { GetProductsParams } from "../types/Types";
const DEFAULT_PAGE = 1; const DEFAULT_PAGE = 1;
const DEFAULT_LIMIT = 10; const DEFAULT_LIMIT = 10;
export const useFoodFilters = () => { export const useProductFilters = () => {
const [filters, setFilters] = useState<FilterValues>({}); const [filters, setFilters] = useState<FilterValues>({});
const [currentPage, setCurrentPage] = useState(DEFAULT_PAGE); const [currentPage, setCurrentPage] = useState(DEFAULT_PAGE);
const [limit] = useState(DEFAULT_LIMIT); const [limit] = useState(DEFAULT_LIMIT);
const apiParams = useMemo<GetFoodsParams>(() => { const apiParams = useMemo<GetProductsParams>(() => {
const params: GetFoodsParams = { const params: GetProductsParams = {
page: currentPage, page: currentPage,
limit: limit, limit: limit,
}; };
@@ -1,25 +1,25 @@
import axios from "@/config/axios"; import axios from "@/config/axios";
import type { import type {
CreateCategoryType, CreateCategoryType,
CreateFoodType, CreateProductType,
GetCategoriesResponseType, GetCategoriesResponseType,
GetFoodDetailsResponseType, GetProductDetailsResponseType,
GetFoodsParams, GetProductsParams,
GetFoodsResponseType, GetProductsResponseType,
GetIconsResponseType, GetIconsResponseType,
} from "../types/Types"; } from "../types/Types";
import type { FilterValues } from "@/components/Filters"; import type { FilterValues } from "@/components/Filters";
export const createFood = async (params: CreateFoodType) => { export const createProduct = async (params: CreateProductType) => {
const { data } = await axios.post(`/admin/foods`, params); const { data } = await axios.post(`/admin/foods`, params);
return data; return data;
}; };
export const getFoods = async ( export const getProducts = async (
params?: GetFoodsParams, params?: GetProductsParams,
filters?: FilterValues, filters?: FilterValues,
): Promise<GetFoodsResponseType> => { ): Promise<GetProductsResponseType> => {
const { data } = await axios.get<GetFoodsResponseType>(`/admin/foods`, { const { data } = await axios.get<GetProductsResponseType>(`/admin/foods`, {
params: { params: {
...params, ...params,
...filters, ...filters,
@@ -28,16 +28,16 @@ export const getFoods = async (
return data; return data;
}; };
export const getFoodDetails = async ( export const getProductDetails = async (
id: string, id: string,
): Promise<GetFoodDetailsResponseType> => { ): Promise<GetProductDetailsResponseType> => {
const { data } = await axios.get<GetFoodDetailsResponseType>( const { data } = await axios.get<GetProductDetailsResponseType>(
`/admin/foods/${id}`, `/admin/foods/${id}`,
); );
return data; return data;
}; };
export const updateFood = async (id: string, params: CreateFoodType) => { export const updateProduct = async (id: string, params: CreateProductType) => {
const { data } = await axios.patch(`/admin/foods/${id}`, params); const { data } = await axios.patch(`/admin/foods/${id}`, params);
return data; return data;
}; };
@@ -49,7 +49,7 @@ export const getCategories = async (): Promise<GetCategoriesResponseType> => {
return data; return data;
}; };
export const deleteFood = async (id: string) => { export const deleteProduct = async (id: string) => {
const { data } = await axios.delete(`/admin/foods/${id}`); const { data } = await axios.delete(`/admin/foods/${id}`);
return data; return data;
}; };
@@ -1,6 +1,6 @@
import type { IResponse } from "@/types/response.types"; import type { IResponse } from "@/types/response.types";
export type CreateFoodType = { export type CreateProductType = {
categoryId: string; categoryId: string;
title: string; title: string;
desc: string; desc: string;
@@ -32,7 +32,7 @@ export type Category = {
parent?: Category; parent?: Category;
}; };
export type FoodDetailsCategory = { export type ProductDetailsCategory = {
id: string; id: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
@@ -48,18 +48,19 @@ export type Inventory = {
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
deletedAt: string | null; deletedAt: string | null;
food: string; product?: string;
food?: string; // API compatibility
totalStock: number; totalStock: number;
availableStock: number; availableStock: number;
}; };
export type Food = { export type Product = {
id: string; id: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
deletedAt: string | null; deletedAt: string | null;
restaurant: string; restaurant: string;
category: FoodDetailsCategory; category: ProductDetailsCategory;
inventory: Inventory; inventory: Inventory;
title: string; title: string;
desc: string; desc: string;
@@ -78,7 +79,7 @@ export type Food = {
isSpecialOffer: boolean; isSpecialOffer: boolean;
}; };
export type GetFoodsParams = { export type GetProductsParams = {
page?: number; page?: number;
limit?: number; limit?: number;
search?: string; search?: string;
@@ -88,13 +89,13 @@ export type GetFoodsParams = {
isActive?: boolean; isActive?: boolean;
}; };
export type FoodDetails = { export type ProductDetails = {
id: string; id: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
deletedAt: string | null; deletedAt: string | null;
restaurant: string; restaurant: string;
category: FoodDetailsCategory; category: ProductDetailsCategory;
inventory: Inventory; inventory: Inventory;
title: string; title: string;
desc: string; desc: string;
@@ -121,8 +122,8 @@ export type PaginationMeta = {
}; };
export type GetCategoriesResponseType = IResponse<Category[]>; export type GetCategoriesResponseType = IResponse<Category[]>;
export type GetFoodsResponseType = IResponse<Food[]> & { meta: PaginationMeta }; export type GetProductsResponseType = IResponse<Product[]> & { meta: PaginationMeta };
export type GetFoodDetailsResponseType = IResponse<FoodDetails>; export type GetProductDetailsResponseType = IResponse<ProductDetails>;
export type CreateCategoryType = { export type CreateCategoryType = {
title: string; title: string;
+8 -8
View File
@@ -5,11 +5,11 @@ import { Route, Routes } from 'react-router-dom'
import Footer from '../shared/Footer' import Footer from '../shared/Footer'
import { clx } from '../helpers/utils' import { clx } from '../helpers/utils'
import { useSharedStore } from '../shared/store/sharedStore' import { useSharedStore } from '../shared/store/sharedStore'
import FoodsList from '@/pages/food/List' import ProductsList from '@/pages/product/List'
import { Pages } from '@/config/Pages' import { Pages } from '@/config/Pages'
import CreateFood from '@/pages/food/Create' import CreateProduct from '@/pages/product/Create'
import UpdateFood from '@/pages/food/Update' import UpdateProduct from '@/pages/product/Update'
import CategoryFood from '@/pages/food/Category' import CategoryProduct from '@/pages/product/Category'
import CustomersList from '@/pages/customers/List' import CustomersList from '@/pages/customers/List'
import CustomerDetails from '@/pages/customers/Details' import CustomerDetails from '@/pages/customers/Details'
import CreateCustomer from '@/pages/customers/Create' import CreateCustomer from '@/pages/customers/Create'
@@ -63,10 +63,10 @@ const MainRouter: FC = () => {
<Route path={'/'} element={<Statistics />} /> <Route path={'/'} element={<Statistics />} />
<Route path={Pages.dashboard} element={<Statistics />} /> <Route path={Pages.dashboard} element={<Statistics />} />
<Route path={Pages.foods.list} element={<FoodsList />} /> <Route path={Pages.products.list} element={<ProductsList />} />
<Route path={Pages.foods.add} element={<CreateFood />} /> <Route path={Pages.products.add} element={<CreateProduct />} />
<Route path={Pages.foods.update + ':id'} element={<UpdateFood />} /> <Route path={Pages.products.update + ':id'} element={<UpdateProduct />} />
<Route path={Pages.foods.category} element={<CategoryFood />} /> <Route path={Pages.products.category} element={<CategoryProduct />} />
<Route path={Pages.customers.list} element={<CustomersList />} /> <Route path={Pages.customers.list} element={<CustomersList />} />
<Route path={Pages.customers.detail + ':id'} element={<CustomerDetails />} /> <Route path={Pages.customers.detail + ':id'} element={<CustomerDetails />} />
<Route path={Pages.customers.add} element={<CreateCustomer />} /> <Route path={Pages.customers.add} element={<CreateCustomer />} />
+1 -1
View File
@@ -34,7 +34,7 @@ const Footer: FC = () => {
</div> </div>
</div> </div>
</Link> </Link>
<Link to={Pages.foods.list}> <Link to={Pages.products.list}>
<div className='text-description w-[70px] flex flex-col items-center gap-1.5'> <div className='text-description w-[70px] flex flex-col items-center gap-1.5'>
<div className='bg-white p-1 rounded-full -mt-7'> <div className='bg-white p-1 rounded-full -mt-7'>
<div className='bg-black flex justify-center items-center size-10 rounded-full'> <div className='bg-black flex justify-center items-center size-10 rounded-full'>
+7 -7
View File
@@ -8,7 +8,7 @@ import { useLocation } from 'react-router-dom'
import { Pages } from '../config/Pages' import { Pages } from '../config/Pages'
import { useSharedStore } from './store/sharedStore' import { useSharedStore } from './store/sharedStore'
import { clx } from '../helpers/utils' import { clx } from '../helpers/utils'
import FoodsSubMenu from './components/FoodsSubMenu' import ProductsSubMenu from './components/ProductsSubMenu'
import OrdersSubMenu from './components/OrdersSubMenu' import OrdersSubMenu from './components/OrdersSubMenu'
import CustomersSubMenu from './components/CustomersSubMenu' import CustomersSubMenu from './components/CustomersSubMenu'
import DiscountSubMenu from './components/DiscountSubMenu' import DiscountSubMenu from './components/DiscountSubMenu'
@@ -115,11 +115,11 @@ const SideBar: FC = () => {
{checkPermission(PermissionEnum.MANAGE_FOODS) && ( {checkPermission(PermissionEnum.MANAGE_FOODS) && (
<SideBarItem <SideBarItem
icon={<Element3 variant={isActive('foods') ? 'Bold' : 'Outline'} color={isActive('foods') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />} icon={<Element3 variant={isActive('products') ? 'Bold' : 'Outline'} color={isActive('products') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
title={t('sidebar.foods')} title={t('sidebar.foods')}
isActive={isActive('foods')} isActive={isActive('products')}
link={Pages.foods.list} link={Pages.products.list}
activeName='foods' activeName='products'
/> />
)} )}
@@ -353,8 +353,8 @@ const SideBar: FC = () => {
hasSubMenu && (openSidebar || window.innerWidth > 1139) && hasSubMenu && (openSidebar || window.innerWidth > 1139) &&
<div className='fixed xl:right-[112px] right-[96px] bg-white z-20 xl:top-4 xl:bottom-4 top-0 bottom-0 rounded-tl-[32px] rounded-bl-[32px] w-[190px] border-r border-border'> <div className='fixed xl:right-[112px] right-[96px] bg-white z-20 xl:top-4 xl:bottom-4 top-0 bottom-0 rounded-tl-[32px] rounded-bl-[32px] w-[190px] border-r border-border'>
{ {
subMenuName === 'foods' ? subMenuName === 'products' ?
<FoodsSubMenu /> <ProductsSubMenu />
: subMenuName === 'orders' ? : subMenuName === 'orders' ?
<OrdersSubMenu /> <OrdersSubMenu />
: subMenuName === 'customers' ? : subMenuName === 'customers' ?
@@ -5,7 +5,7 @@ import SubMenuItem from './SubMenuItem'
import { useLocation } from 'react-router-dom' import { useLocation } from 'react-router-dom'
import { Pages } from '../../config/Pages' import { Pages } from '../../config/Pages'
const FoodsSubMenu: FC = () => { const ProductsSubMenu: FC = () => {
const location = useLocation() const location = useLocation()
const { t } = useTranslation('global') const { t } = useTranslation('global')
@@ -31,22 +31,21 @@ const FoodsSubMenu: FC = () => {
<SubMenuItem <SubMenuItem
title={t('submenu.foods_list')} title={t('submenu.foods_list')}
isActive={isActive('list')} isActive={isActive('list')}
link={Pages.foods.list} link={Pages.products.list}
/> />
<SubMenuItem <SubMenuItem
title={t('submenu.add_food')} title={t('submenu.add_food')}
isActive={isActive('add')} isActive={isActive('add')}
link={Pages.foods.add} link={Pages.products.add}
/> />
<SubMenuItem <SubMenuItem
title={t('submenu.categories')} title={t('submenu.categories')}
isActive={isActive('category')} isActive={isActive('category')}
link={Pages.foods.category} link={Pages.products.category}
/> />
</div> </div>
</div> </div>
) )
} }
export default FoodsSubMenu export default ProductsSubMenu