update food

This commit is contained in:
hamid zarghami
2025-11-15 14:45:04 +03:30
parent 0fd87e99f2
commit eaa9ef2a9a
18 changed files with 609 additions and 61 deletions
+3 -4
View File
@@ -1,5 +1,4 @@
import { type FC, Fragment } from 'react'
import Td from './Td'
import Skeleton from 'react-loading-skeleton'
type Props = {
@@ -15,9 +14,9 @@ const DefaultTableSkeleton: FC<Props> = ({ tdCount, trCount = 5 }) => {
<tr className="w-full h-[64px] md:h-[74px] bg-white border-t border-[#EAEDF5]" key={rowIndex}>
{
Array.from({ length: tdCount }).map((_, colIndex) => (
<Td text={''} key={colIndex}>
<Skeleton />
</Td>
<td key={colIndex} className="px-3 md:px-6 py-3 md:py-4 whitespace-nowrap text-xs">
<Skeleton height={20} width="100%" />
</td>
))
}
</tr>
+3 -44
View File
@@ -1,4 +1,4 @@
import React, { Fragment, useState, memo } from 'react';
import React, { useState, memo } from 'react';
import DefaultTableSkeleton from '../components/DefaultTableSkeleton';
import Td from '../components/Td';
import type { TableProps, RowDataType, ColumnType } from '@/components/types/TableTypes';
@@ -147,9 +147,7 @@ const Table = <T extends RowDataType>({
const renderTableBody = () => {
if (isLoading) {
return (
<Fragment>
<DefaultTableSkeleton tdCount={columns.length + (selectable ? 1 : 0) + (rowActions ? 1 : 0)} trCount={emptyRowsCount} />
</Fragment>
<DefaultTableSkeleton tdCount={columns.length + (selectable ? 1 : 0) + (rowActions ? 1 : 0)} trCount={emptyRowsCount} />
);
}
@@ -321,43 +319,4 @@ const Table = <T extends RowDataType>({
);
};
export default memo(Table) as <T extends RowDataType>(props: TableProps<T>) => React.ReactElement;
/*
مثال استفاده با pagination:
import Table from '@/components/Table';
import { useState } from 'react';
const MyComponent = () => {
const [currentPage, setCurrentPage] = useState(1);
const totalPages = 10;
const columns = [
{ title: 'نام', key: 'name' },
{ title: 'ایمیل', key: 'email' },
];
const data = [
{ id: 1, name: 'علی', email: 'ali@example.com' },
{ id: 2, name: 'فاطمه', email: 'fateme@example.com' },
];
const handlePageChange = (page: number) => {
setCurrentPage(page);
// اینجا می‌توانید API call جدید برای دریافت داده‌های صفحه جدید بزنید
};
return (
<Table
columns={columns}
data={data}
pagination={{
currentPage,
totalPages,
onPageChange: handlePageChange
}}
/>
);
};
*/
export default memo(Table) as <T extends RowDataType>(props: TableProps<T>) => React.ReactElement;
+24 -8
View File
@@ -12,7 +12,8 @@ type Props = {
preview?: string[],
onChangePreview?: (preview: string[]) => void,
getCover?: (url: string) => void,
coverUrl?: string
coverUrl?: string,
imageSize?: 'small' | 'medium' | 'large'
}
const UploadBoxDraggble: FC<Props> = (props: Props) => {
@@ -92,9 +93,17 @@ const UploadBoxDraggble: FC<Props> = (props: Props) => {
{
files.length > 0 || perviews ? (
<div className='mt-4 flex gap-4 items-center'>
<div className={clx(
'mt-4',
props.imageSize === 'large' ? 'grid grid-cols-2 gap-4' : 'flex gap-4 items-center flex-wrap'
)}>
{
perviews && perviews.map((item, index) => {
const imageSizeClass = props.imageSize === 'large'
? 'w-full h-32 rounded-lg'
: props.imageSize === 'medium'
? 'size-20 rounded-lg'
: 'size-10 rounded-full'
return (
<div key={index} className='flex items-center gap-2'>
<div key={index} className='flex relative items-center gap-2'>
@@ -105,10 +114,11 @@ const UploadBoxDraggble: FC<Props> = (props: Props) => {
}
}} src={item}
className={clx(
'size-10 rounded-full object-cover',
imageSizeClass,
'object-cover cursor-pointer',
cover === item ? 'border-2 border-red-400' : ''
)} />
<div onClick={() => handleDeletePreview(index)} className='absolute -left-2 -top-2 shadow-md bg-white size-5 rounded-full flex justify-center items-center'>
<div onClick={() => handleDeletePreview(index)} className='absolute -left-2 -top-2 shadow-md bg-white size-5 rounded-full flex justify-center items-center cursor-pointer z-10'>
<CloseCircle className='size-4 ' color='red' />
</div>
</div>
@@ -123,24 +133,30 @@ const UploadBoxDraggble: FC<Props> = (props: Props) => {
<div key={index} className='flex border p-2 rounded-lg items-center gap-2'>
<div className='flex relative items-center gap-2'>
<div className='text-xs'>{file.name}</div>
<div className='absolute -left-4 -top-4 shadow-md bg-white size-5 rounded-full flex justify-center items-center'>
<div className='absolute -left-4 -top-4 shadow-md bg-white size-5 rounded-full flex justify-center items-center cursor-pointer'>
<CloseCircle onClick={() => handleDelete(index)} className='size-4 ' color='red' />
</div>
</div>
</div>
)
}
else
else {
const imageSizeClass = props.imageSize === 'large'
? 'w-full h-32 rounded-lg'
: props.imageSize === 'medium'
? 'size-20 rounded-lg'
: 'size-10 rounded-full'
return (
<div key={index} className='flex items-center gap-2'>
<div key={index} className='flex relative items-center gap-2'>
<img src={URL.createObjectURL(file)} alt={file.name} className='size-10 rounded-full object-cover' />
<div className='absolute -left-2 -top-2 shadow-md bg-white size-5 rounded-full flex justify-center items-center'>
<img src={URL.createObjectURL(file)} alt={file.name} className={clx(imageSizeClass, 'object-cover')} />
<div className='absolute -left-2 -top-2 shadow-md bg-white size-5 rounded-full flex justify-center items-center cursor-pointer z-10'>
<CloseCircle onClick={() => handleDelete(index)} className='size-4 ' color='red' />
</div>
</div>
</div>
)
}
})
}
</div>
+20
View File
@@ -52,3 +52,23 @@ export const timeAgo = (date: string | Date): string => {
return "همین الان";
};
export interface IGeneralError {
status: number;
success: boolean;
error: {
message: string[];
};
}
export const extractErrorMessage = (
error: Error | unknown,
fallbackMessage: string = "خطایی رخ داده است"
): string => {
try {
const axiosError = error as { response?: { data: IGeneralError } };
return axiosError?.response?.data?.error?.message?.[0] || fallbackMessage;
} catch {
return fallbackMessage;
}
};
+1
View File
@@ -1,4 +1,5 @@
@import "tailwindcss";
@import "react-loading-skeleton/dist/skeleton.css";
html {
direction: rtl;
+2 -1
View File
@@ -75,7 +75,8 @@ const CreateFood: FC = () => {
const foodData: CreateFoodType = {
...values,
isActive,
images: imageUrls
images: imageUrls,
content: values.content.filter(item => item && item.trim().length > 0)
}
createFood(foodData, {
+219
View File
@@ -0,0 +1,219 @@
import { type FC, useState, useEffect } from 'react'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { TickCircle } from 'iconsax-react'
import { toast } from 'react-toastify'
import { useParams } from 'react-router-dom'
import Button from '@/components/Button'
import CreateFoodSidebar from './components/CreateFoodSidebar'
import BasicInfoSection from './components/BasicInfoSection'
import FoodContentSection from './components/FoodContentSection'
import MealTimesSection from './components/MealTimesSection'
import WeekDaysSection from './components/WeekDaysSection'
import ServiceOptionsSection from './components/ServiceOptionsSection'
import type { CreateFoodType } from './types/Types'
import { useUpdateFood, useGetFoodDetails, useGetCategories } from './hooks/useFoodData'
import { useMultipleUpload } from '../uploader/hooks/useUploaderData'
import { Pages } from '@/config/Pages'
import type { ErrorType } from '@/helpers/types'
import { extractErrorMessage } from '@/config/func'
const UpdateFood: FC = () => {
const { id } = useParams<{ id: string }>()
const { data: foodData, isLoading } = useGetFoodDetails(id || '')
const { data: categoriesData } = useGetCategories()
const [isActive, setIsActive] = useState<boolean>(true)
const [isSpecial, setIsSpecial] = useState<boolean>(false)
const [imageFiles, setImageFiles] = useState<File[]>([])
const [existingImages, setExistingImages] = useState<string[]>([])
const { mutate: updateFood } = useUpdateFood()
const { mutate: multipleUpload } = useMultipleUpload()
const categories = categoriesData?.data?.map(category => ({
label: category.title,
value: category.id
})) || []
const food = foodData?.data
const formik = useFormik<CreateFoodType>({
initialValues: {
title: '',
desc: '',
content: [],
categoryIds: [],
price: 0,
discount: 0,
points: 0,
prepareTime: 0,
stock: 0,
stockDefault: 0,
breakfast: false,
noon: false,
dinner: false,
mon: false,
tue: false,
wed: false,
thu: false,
fri: false,
sat: false,
sun: false,
pickupServe: false,
inPlaceServe: false,
isActive: true,
images: []
},
validationSchema: Yup.object().shape({
title: Yup.string().required('نام غذا الزامی است'),
desc: Yup.string().required('توضیحات غذا الزامی است'),
categoryIds: Yup.array().min(1, 'حداقل یک دسته‌بندی الزامی است'),
price: Yup.number().required('قیمت الزامی است').min(0, 'قیمت باید مثبت باشد'),
prepareTime: Yup.number().required('زمان آماده‌سازی الزامی است').min(0, 'زمان آماده‌سازی باید مثبت باشد'),
stock: Yup.number().required('موجودی الزامی است').min(0, 'موجودی باید مثبت باشد'),
stockDefault: Yup.number().required('موجودی پیش‌فرض الزامی است').min(0, 'موجودی پیش‌فرض باید مثبت باشد'),
points: Yup.number().min(0, 'امتیاز باید مثبت باشد'),
discount: Yup.number().min(0, 'تخفیف باید مثبت باشد'),
content: Yup.array().of(Yup.string())
}),
onSubmit: (values) => {
if (!id) {
toast.error('شناسه غذا یافت نشد')
return
}
const submitFood = (newImageUrls: string[] = []) => {
const allImages = [...values.images, ...newImageUrls]
const foodData: CreateFoodType = {
...values,
isActive,
images: allImages,
content: values.content.filter(item => item && item.trim().length > 0)
}
updateFood({ id, params: foodData }, {
onSuccess: () => {
toast.success('غذا با موفقیت به‌روزرسانی شد')
window.location.href = Pages.foods.list
},
onError: (error: ErrorType) => {
toast.error(extractErrorMessage(error))
}
})
}
if (imageFiles.length > 0) {
multipleUpload(imageFiles, {
onSuccess: (response) => {
const imageUrls = response?.data?.map(item => item.url) || []
submitFood(imageUrls)
},
onError: (error: ErrorType) => {
toast.error(error?.response?.data?.error?.message[0] || 'خطا در آپلود تصاویر')
}
})
} else {
submitFood()
}
}
})
useEffect(() => {
if (food) {
formik.setValues({
title: food.title || '',
desc: food.desc || '',
content: food.content || [],
categoryIds: food.categories?.map(cat => cat.id) || [],
price: food.price || 0,
discount: food.discount || 0,
points: food.points || 0,
prepareTime: food.prepareTime || 0,
stock: food.stock || 0,
stockDefault: food.stockDefault || 0,
breakfast: food.breakfast || false,
noon: food.noon || false,
dinner: food.dinner || false,
mon: food.mon || false,
tue: food.tue || false,
wed: food.wed || false,
thu: food.thu || false,
fri: food.fri || false,
sat: food.sat || false,
sun: food.sun || false,
pickupServe: food.pickupServe || false,
inPlaceServe: food.inPlaceServe || false,
isActive: food.isActive || false,
images: food.images || []
})
setIsActive(food.isActive || false)
setExistingImages(food.images || [])
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [food])
useEffect(() => {
formik.setFieldValue('images', existingImages)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [existingImages])
if (isLoading) {
return (
<div className='w-full mt-4 flex justify-center items-center'>
<div>در حال بارگذاری...</div>
</div>
)
}
if (!food) {
return (
<div className='w-full mt-4 flex justify-center items-center'>
<div>غذا یافت نشد</div>
</div>
)
}
return (
<div className='w-full mt-4'>
<div className='flex w-full justify-between items-center'>
<div className='text-lg font-light'>
ویرایش غذا
</div>
<div>
<Button
className='px-5'
onClick={() => formik.handleSubmit()}
>
<div className='flex gap-2 items-center'>
<TickCircle className='size-5' color='white' />
<div>ذخیره تغییرات</div>
</div>
</Button>
</div>
</div>
<div className='flex gap-6 mt-6'>
<div className='flex-1'>
<div className='bg-white py-8 xl:px-10 px-4 rounded-3xl'>
<BasicInfoSection formik={formik} categories={categories} />
<FoodContentSection formik={formik} />
<MealTimesSection formik={formik} />
<WeekDaysSection formik={formik} />
<ServiceOptionsSection formik={formik} />
</div>
</div>
<CreateFoodSidebar
isActive={isActive}
isSpecial={isSpecial}
onChangeActive={setIsActive}
onChangeSpecial={setIsSpecial}
onChangeImages={setImageFiles}
existingImages={existingImages}
onChangeExistingImages={setExistingImages}
/>
</div>
</div>
)
}
export default UpdateFood
@@ -0,0 +1,121 @@
import { type FC } from 'react'
import type { FormikProps } from 'formik'
import Input from '@/components/Input'
import Select from '@/components/Select'
import Textarea from '@/components/Textarea'
import type { CreateFoodType } from '../types/Types'
type BasicInfoSectionProps = {
formik: FormikProps<CreateFoodType>
categories: Array<{ label: string; value: string }>
}
const BasicInfoSection: FC<BasicInfoSectionProps> = ({ formik, categories }) => {
return (
<>
<Input
label='نام غذا'
name='title'
placeholder=''
value={formik.values.title}
onChange={formik.handleChange}
error_text={formik.touched.title && formik.errors.title ? formik.errors.title : ''}
/>
<div className='mt-5'>
<Select
items={categories}
label='دسته بندی'
placeholder='انتخاب کنید'
name='categoryIds'
value={formik.values.categoryIds[0] || ''}
onChange={(e) => {
const value = e.target.value
formik.setFieldValue('categoryIds', value ? [value] : [])
}}
error_text={formik.touched.categoryIds && formik.errors.categoryIds ? String(formik.errors.categoryIds) : ''}
/>
</div>
<div className='grid grid-cols-3 gap-4 mt-5'>
<Input
name='price'
label='قیمت'
placeholder='تومان'
type='number'
seprator={true}
value={formik.values.price}
onChange={(e) => formik.setFieldValue('price', Number(e.target.value))}
error_text={formik.touched.price && formik.errors.price ? formik.errors.price : ''}
/>
<Input
name='discount'
label='تخفیف'
placeholder='تومان'
type='number'
seprator={true}
value={formik.values.discount}
onChange={(e) => formik.setFieldValue('discount', Number(e.target.value))}
error_text={formik.touched.discount && formik.errors.discount ? formik.errors.discount : ''}
/>
<Input
name='points'
label='امتیاز'
placeholder=''
type='number'
value={formik.values.points}
onChange={(e) => formik.setFieldValue('points', Number(e.target.value))}
error_text={formik.touched.points && formik.errors.points ? formik.errors.points : ''}
/>
</div>
<div className='grid grid-cols-3 gap-4 mt-5'>
<Input
name='prepareTime'
label='زمان آماده سازی'
placeholder='دقیقه'
type='number'
value={formik.values.prepareTime}
onChange={(e) => formik.setFieldValue('prepareTime', Number(e.target.value))}
error_text={formik.touched.prepareTime && formik.errors.prepareTime ? formik.errors.prepareTime : ''}
/>
<Input
name='stock'
label='موجودی'
placeholder=''
type='number'
value={formik.values.stock}
onChange={(e) => formik.setFieldValue('stock', Number(e.target.value))}
error_text={formik.touched.stock && formik.errors.stock ? formik.errors.stock : ''}
/>
<Input
name='stockDefault'
label='موجودی پیش‌فرض'
placeholder=''
type='number'
value={formik.values.stockDefault}
onChange={(e) => formik.setFieldValue('stockDefault', Number(e.target.value))}
error_text={formik.touched.stockDefault && formik.errors.stockDefault ? formik.errors.stockDefault : ''}
/>
</div>
<div className='mt-5'>
<Textarea
name='desc'
label='توضیحات غذا'
placeholder=''
value={formik.values.desc}
onChange={formik.handleChange}
error_text={formik.touched.desc && formik.errors.desc ? formik.errors.desc : ''}
/>
</div>
</>
)
}
export default BasicInfoSection
@@ -9,6 +9,8 @@ type Props = {
onChangeActive: (value: boolean) => void
onChangeSpecial: (value: boolean) => void
onChangeImages: (files: File[]) => void
existingImages?: string[]
onChangeExistingImages?: (images: string[]) => void
}
const CreateFoodSidebar: FC<Props> = (props: Props) => {
@@ -43,6 +45,9 @@ const CreateFoodSidebar: FC<Props> = (props: Props) => {
label='تصویر غذا را آپلود کنید'
isMultiple={true}
onChange={(files: File[]) => props.onChangeImages(files)}
preview={props.existingImages}
onChangePreview={props.onChangeExistingImages}
imageSize='medium'
/>
</div>
</div>
@@ -0,0 +1,54 @@
import { type FC } from 'react'
import type { FormikProps } from 'formik'
import Input from '@/components/Input'
import Button from '@/components/Button'
import type { CreateFoodType } from '../types/Types'
type FoodContentSectionProps = {
formik: FormikProps<CreateFoodType>
}
const FoodContentSection: FC<FoodContentSectionProps> = ({ formik }) => {
return (
<div className='mt-5'>
<div className='text-sm mb-2'>محتوای غذا</div>
<div className='space-y-2'>
{formik.values.content.map((item, index) => (
<div key={index} className='flex gap-2 items-center'>
<Input
placeholder='متن محتوا'
value={item}
onChange={(e) => {
const newContent = [...formik.values.content]
newContent[index] = e.target.value
formik.setFieldValue('content', newContent)
}}
/>
<Button
type='button'
className='px-3 bg-red-500 hover:bg-red-600 w-auto'
onClick={() => {
const newContent = formik.values.content.filter((_, i) => i !== index)
formik.setFieldValue('content', newContent)
}}
>
حذف
</Button>
</div>
))}
<Button
type='button'
className='w-full bg-gray-200 text-gray-700 hover:bg-gray-300'
onClick={() => {
formik.setFieldValue('content', [...formik.values.content, ''])
}}
>
افزودن محتوا
</Button>
</div>
</div>
)
}
export default FoodContentSection
@@ -3,6 +3,8 @@ import type { ColumnType } from '@/components/types/TableTypes'
import type { Food } from '../types/Types'
import { formatPrice, formatTime } from '../utils/formatters'
import Status from '@/components/Status'
import { Link } from 'react-router-dom'
import { Pages } from '@/config/Pages'
export const getFoodTableColumns = (): ColumnType<Food>[] => {
return [
@@ -63,11 +65,11 @@ export const getFoodTableColumns = (): ColumnType<Food>[] => {
{
key: 'view',
title: '',
render: () => {
render: (item: Food) => {
return (
<div className='flex gap-2 items-center'>
<Link to={Pages.foods.update + item.id} className='flex gap-2 items-center'>
<Eye size={20} color='#8C90A3' />
</div>
</Link>
)
}
},
@@ -0,0 +1,42 @@
import { type FC } from 'react'
import type { FormikProps } from 'formik'
import { Checkbox } from '@/components/ui/checkbox'
import type { CreateFoodType } from '../types/Types'
type MealTimesSectionProps = {
formik: FormikProps<CreateFoodType>
}
const MealTimesSection: FC<MealTimesSectionProps> = ({ formik }) => {
return (
<div className='mt-6'>
<div className='text-sm mb-3'>وعدههای غذایی</div>
<div className='flex gap-6 flex-wrap'>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.breakfast}
onCheckedChange={(checked) => formik.setFieldValue('breakfast', checked)}
/>
<label className='text-xs cursor-pointer'>صبحانه</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.noon}
onCheckedChange={(checked) => formik.setFieldValue('noon', checked)}
/>
<label className='text-xs cursor-pointer'>ناهار</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.dinner}
onCheckedChange={(checked) => formik.setFieldValue('dinner', checked)}
/>
<label className='text-xs cursor-pointer'>شام</label>
</div>
</div>
</div>
)
}
export default MealTimesSection
@@ -0,0 +1,35 @@
import { type FC } from 'react'
import type { FormikProps } from 'formik'
import { Checkbox } from '@/components/ui/checkbox'
import type { CreateFoodType } from '../types/Types'
type ServiceOptionsSectionProps = {
formik: FormikProps<CreateFoodType>
}
const ServiceOptionsSection: FC<ServiceOptionsSectionProps> = ({ formik }) => {
return (
<div className='mt-6'>
<div className='text-sm mb-3'>گزینههای سرویس</div>
<div className='flex gap-6 flex-wrap'>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.pickupServe}
onCheckedChange={(checked) => formik.setFieldValue('pickupServe', checked)}
/>
<label className='text-xs cursor-pointer'> بیرونبر</label>
</div>
<div className='flex items-center gap-2'>
<Checkbox
checked={formik.values.inPlaceServe}
onCheckedChange={(checked) => formik.setFieldValue('inPlaceServe', checked)}
/>
<label className='text-xs cursor-pointer'>سرو در محل</label>
</div>
</div>
</div>
)
}
export default ServiceOptionsSection
@@ -0,0 +1,40 @@
import { type FC } from 'react'
import type { FormikProps } from 'formik'
import { Checkbox } from '@/components/ui/checkbox'
import type { CreateFoodType } from '../types/Types'
type WeekDaysSectionProps = {
formik: FormikProps<CreateFoodType>
}
const WeekDaysSection: FC<WeekDaysSectionProps> = ({ formik }) => {
const weekDays = [
{ key: 'sat', label: 'شنبه' },
{ key: 'sun', label: 'یکشنبه' },
{ key: 'mon', label: 'دوشنبه' },
{ key: 'tue', label: 'سه‌شنبه' },
{ key: 'wed', label: 'چهارشنبه' },
{ key: 'thu', label: 'پنج‌شنبه' },
{ key: 'fri', label: 'جمعه' }
] as const
return (
<div className='mt-6'>
<div className='text-sm mb-3'>روزهای هفته</div>
<div className='flex gap-6 flex-wrap'>
{weekDays.map((day) => (
<div key={day.key} className='flex items-center gap-2'>
<Checkbox
checked={formik.values[day.key]}
onCheckedChange={(checked) => formik.setFieldValue(day.key, checked)}
/>
<label className='text-xs cursor-pointer'>{day.label}</label>
</div>
))}
</div>
</div>
)
}
export default WeekDaysSection
+15 -1
View File
@@ -1,6 +1,6 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import * as api from "../service/FoodService";
import type { GetFoodsParams } from "../types/Types";
import type { CreateFoodType, GetFoodsParams } from "../types/Types";
export const useCreateFood = () => {
return useMutation({
@@ -15,6 +15,20 @@ export const useGetFoods = (params?: GetFoodsParams) => {
});
};
export const useGetFoodDetails = (id: string) => {
return useQuery({
queryKey: ["food", id],
queryFn: () => api.getFoodDetails(id),
});
};
export const useUpdateFood = () => {
return useMutation({
mutationFn: ({ id, params }: { id: string; params: CreateFoodType }) =>
api.updateFood(id, params),
});
};
export const useGetCategories = () => {
return useQuery({
queryKey: ["categories"],
+13
View File
@@ -2,6 +2,7 @@ import axios from "@/config/axios";
import type {
CreateFoodType,
GetCategoriesResponseType,
GetFoodDetailsResponseType,
GetFoodsParams,
GetFoodsResponseType,
} from "../types/Types";
@@ -18,6 +19,18 @@ export const getFoods = async (
return data;
};
export const getFoodDetails = async (
id: string
): Promise<GetFoodDetailsResponseType> => {
const { data } = await axios.get<GetFoodDetailsResponseType>(`/foods/${id}`);
return data;
};
export const updateFood = async (id: string, params: CreateFoodType) => {
const { data } = await axios.patch(`/foods/${id}`, params);
return data;
};
export const getCategories = async (): Promise<GetCategoriesResponseType> => {
const { data } = await axios.get<GetCategoriesResponseType>(`/categories`);
return data;
+5
View File
@@ -53,6 +53,10 @@ export type Food = {
sat: boolean;
sun: boolean;
mon: boolean;
tue: boolean;
wed: boolean;
thu: boolean;
fri: boolean;
breakfast: boolean;
noon: boolean;
dinner: boolean;
@@ -79,3 +83,4 @@ export type GetFoodsParams = {
export type GetCategoriesResponseType = IResponse<Category[]>;
export type GetFoodsResponseType = IResponse<Food[]>;
export type GetFoodDetailsResponseType = IResponse<Food>;
+2
View File
@@ -8,6 +8,7 @@ import { useSharedStore } from '../shared/store/sharedStore'
import FoodsList from '@/pages/food/List'
import { Pages } from '@/config/Pages'
import CreateFood from '@/pages/food/Create'
import UpdateFood from '@/pages/food/Update'
import CategoryFood from '@/pages/food/Category'
import CustomersList from '@/pages/customers/List'
import CustomerDetails from '@/pages/customers/Details'
@@ -35,6 +36,7 @@ const MainRouter: FC = () => {
<Routes>
<Route path={Pages.foods.list} element={<FoodsList />} />
<Route path={Pages.foods.add} element={<CreateFood />} />
<Route path={Pages.foods.update + ':id'} element={<UpdateFood />} />
<Route path={Pages.foods.category} element={<CategoryFood />} />
<Route path={Pages.customers.list} element={<CustomersList />} />
<Route path={Pages.customers.detail + ':id'} element={<CustomerDetails />} />