single order
This commit is contained in:
@@ -1,3 +1,3 @@
|
|||||||
VITE_TOKEN_NAME = 'negareh_t'
|
VITE_TOKEN_NAME = 'negareh_t'
|
||||||
VITE_REFRESH_TOKEN_NAME = 'negareh_rt'
|
VITE_REFRESH_TOKEN_NAME = 'negareh_rt'
|
||||||
VITE_API_BASE_URL = 'http://192.168.99.235:4000'
|
VITE_API_BASE_URL = 'http://192.168.99.226:4000'
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { type FC } from 'react'
|
import { type FC } from 'react'
|
||||||
import type { AttributeType } from '../type/Types'
|
import type { AttributeType, OrderType } from '../type/Types'
|
||||||
import { clx } from '@/helpers/utils'
|
import { clx } from '@/helpers/utils'
|
||||||
import { FieldTypeEnum } from '../enum/OrderEnum'
|
import { FieldTypeEnum } from '../enum/OrderEnum'
|
||||||
import Select from '@/components/Select'
|
import Select from '@/components/Select'
|
||||||
@@ -8,14 +8,28 @@ import RadioGroup from '@/components/RadioGroup'
|
|||||||
import DatePickerComponent from '@/components/DatePicker'
|
import DatePickerComponent from '@/components/DatePicker'
|
||||||
import Input from '@/components/Input'
|
import Input from '@/components/Input'
|
||||||
import Textarea from '@/components/Textarea'
|
import Textarea from '@/components/Textarea'
|
||||||
|
import type { FormikProps } from 'formik'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
attributes?: AttributeType[]
|
attributes?: AttributeType[],
|
||||||
|
formik: FormikProps<OrderType>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ManageAttribute: FC<Props> = (props) => {
|
const ManageAttribute: FC<Props> = (props) => {
|
||||||
|
|
||||||
const { attributes } = props
|
const { attributes, formik } = props
|
||||||
|
|
||||||
|
const handleChange = (attributeId: number, value: string) => {
|
||||||
|
const attribute = formik.values.attributes
|
||||||
|
const index: number = attribute.findIndex(o => o.attributeId === attributeId)
|
||||||
|
if (index > -1) {
|
||||||
|
attribute[index].value = value
|
||||||
|
} else {
|
||||||
|
attribute.push({ attributeId: attributeId, value: value })
|
||||||
|
}
|
||||||
|
formik.setFieldValue('attributes', attribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clx(
|
<div className={clx(
|
||||||
@@ -35,6 +49,7 @@ const ManageAttribute: FC<Props> = (props) => {
|
|||||||
value: value.value
|
value: value.value
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
|
onChange={(e) => handleChange(item.id, e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -45,13 +60,14 @@ const ManageAttribute: FC<Props> = (props) => {
|
|||||||
<div className='flex gap-5 flex-wrap'>
|
<div className='flex gap-5 flex-wrap'>
|
||||||
{
|
{
|
||||||
item.values?.map((value) => {
|
item.values?.map((value) => {
|
||||||
|
const object = formik.values.attributes.find(o => o.attributeId === item.id)
|
||||||
return (
|
return (
|
||||||
<div key={value.id} className='flex gap-2 items-center'>
|
<div key={value.id} className='flex gap-2 items-center'>
|
||||||
<div>{value.value}</div>
|
<div>{value.value}</div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
name={item.id + ''}
|
name={item.id + ''}
|
||||||
value={value.value}
|
checked={object?.value === value.value}
|
||||||
onCheckedChange={(c) => console.log(c)}
|
onCheckedChange={(checked) => handleChange(item.id, checked ? value.value : '')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -60,7 +76,8 @@ const ManageAttribute: FC<Props> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
else if (item.type === FieldTypeEnum.radio)
|
else if (item.type === FieldTypeEnum.radio) {
|
||||||
|
const object = formik.values.attributes.find(o => o.attributeId === item.id)
|
||||||
return (
|
return (
|
||||||
<div key={item.id} className='mt-6'>
|
<div key={item.id} className='mt-6'>
|
||||||
<div className='text-sm mb-3'>{item.name}</div>
|
<div className='text-sm mb-3'>{item.name}</div>
|
||||||
@@ -71,16 +88,18 @@ const ManageAttribute: FC<Props> = (props) => {
|
|||||||
value: value.value
|
value: value.value
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
onChange={(v) => console.log(v)}
|
onChange={(v) => handleChange(item.id, v)}
|
||||||
selected=''
|
selected={object?.value + '' || ''}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else if (item.type === FieldTypeEnum.date)
|
else if (item.type === FieldTypeEnum.date)
|
||||||
return (
|
return (
|
||||||
<div key={item.id} className='mt-6'>
|
<div key={item.id} className='mt-6'>
|
||||||
<DatePickerComponent
|
<DatePickerComponent
|
||||||
onChange={(date) => console.log(date)}
|
onChange={(date) => handleChange(item.id, date)}
|
||||||
placeholder=''
|
placeholder=''
|
||||||
label={item.name}
|
label={item.name}
|
||||||
/>
|
/>
|
||||||
@@ -92,6 +111,7 @@ const ManageAttribute: FC<Props> = (props) => {
|
|||||||
<Input
|
<Input
|
||||||
label={item.name}
|
label={item.name}
|
||||||
type={item.type}
|
type={item.type}
|
||||||
|
onChange={(e) => handleChange(item.id, e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -100,6 +120,7 @@ const ManageAttribute: FC<Props> = (props) => {
|
|||||||
<div key={item.id} className='mt-6'>
|
<div key={item.id} className='mt-6'>
|
||||||
<Textarea
|
<Textarea
|
||||||
label={item.name}
|
label={item.name}
|
||||||
|
onChange={(e) => handleChange(item.id, e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,15 +9,21 @@ import { AddSquare } from 'iconsax-react'
|
|||||||
import ProductsSelect from './ProductsSelect'
|
import ProductsSelect from './ProductsSelect'
|
||||||
import { useFormik } from 'formik'
|
import { useFormik } from 'formik'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import type { OrderType, ProductType } from '../type/Types'
|
import type { AttachmentsType, OrderType, ProductType } from '../type/Types'
|
||||||
import { useGetProducts } from '../hooks/useOrderData'
|
import { useGetProducts } from '../hooks/useOrderData'
|
||||||
import ManageAttribute from './ManageAttribute'
|
import ManageAttribute from './ManageAttribute'
|
||||||
|
import { useMultiUpload, useSingleUpload } from '@/pages/uploader/hooks/useUploader'
|
||||||
|
import { useAuthStore } from '../store/OrderStore'
|
||||||
|
|
||||||
const Order: FC = () => {
|
const Order: FC = () => {
|
||||||
|
|
||||||
const { data } = useGetProducts()
|
const { data } = useGetProducts()
|
||||||
|
const { setItems, items } = useAuthStore()
|
||||||
const [productSelected, setProductSelected] = useState<ProductType>()
|
const [productSelected, setProductSelected] = useState<ProductType>()
|
||||||
// const [attribute, setAttribute] = useState<AttributeType>()
|
const [voiceFile, setVoiceFile] = useState<File>()
|
||||||
|
const [files, setFiles] = useState<File[]>([])
|
||||||
|
const singleUpload = useSingleUpload()
|
||||||
|
const multiUpload = useMultiUpload()
|
||||||
|
|
||||||
const formik = useFormik<OrderType>({
|
const formik = useFormik<OrderType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
@@ -25,12 +31,37 @@ const Order: FC = () => {
|
|||||||
attachments: [],
|
attachments: [],
|
||||||
quantity: undefined,
|
quantity: undefined,
|
||||||
description: '',
|
description: '',
|
||||||
|
attributes: []
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
|
|
||||||
}),
|
}),
|
||||||
onSubmit: (values) => {
|
onSubmit: async (values) => {
|
||||||
|
const attachments: AttachmentsType[] = []
|
||||||
|
if (files.length) {
|
||||||
|
await multiUpload.mutateAsync(files, {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
data?.data?.map((item) => {
|
||||||
|
attachments.push({
|
||||||
|
type: 'uploads_attach',
|
||||||
|
url: item.url
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (voiceFile) {
|
||||||
|
await singleUpload.mutateAsync(voiceFile, {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
attachments.push({
|
||||||
|
type: 'voice',
|
||||||
|
url: data?.data?.url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
values.attachments = attachments
|
||||||
|
setItems([...items, values])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -42,19 +73,10 @@ const Order: FC = () => {
|
|||||||
product.quantities.unshift(0)
|
product.quantities.unshift(0)
|
||||||
}
|
}
|
||||||
setProductSelected(product)
|
setProductSelected(product)
|
||||||
|
formik.setFieldValue('productId', productId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(productSelected?.quantities);
|
|
||||||
|
|
||||||
|
|
||||||
// const handleChangeAttribute = (e: ChangeEvent<HTMLSelectElement>) => {
|
|
||||||
// const attributeId = e.target.value
|
|
||||||
// const attribute = productSelected?.attributes?.find(o => Number(o.id) === Number(attributeId))
|
|
||||||
// if (attribute) {
|
|
||||||
// setAttribute(attribute)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='bg-white rounded-3xl p-6 mt-8'>
|
<div className='bg-white rounded-3xl p-6 mt-8'>
|
||||||
@@ -81,6 +103,7 @@ const Order: FC = () => {
|
|||||||
<div>
|
<div>
|
||||||
<ManageAttribute
|
<ManageAttribute
|
||||||
attributes={productSelected?.attributes}
|
attributes={productSelected?.attributes}
|
||||||
|
formik={formik}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -107,22 +130,27 @@ const Order: FC = () => {
|
|||||||
|
|
||||||
<div className='mt-6'>
|
<div className='mt-6'>
|
||||||
<VoiceRecorder
|
<VoiceRecorder
|
||||||
|
onChange={(value) => formik.setFieldValue('description', value)}
|
||||||
label='پیام صوتی'
|
label='پیام صوتی'
|
||||||
onRecordingComplete={(blob) => {
|
onRecordingComplete={(blob) => {
|
||||||
console.log('ضبط صدا تکمیل شد:', blob)
|
const file = new File([blob], "recording.wav", { type: blob.type });
|
||||||
|
setVoiceFile(file);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6'>
|
<div className='mt-6'>
|
||||||
<UploadBox
|
<UploadBox
|
||||||
label='فایل های ضبط شده'
|
label='آپلود فایل'
|
||||||
|
isMultiple
|
||||||
|
onChange={setFiles}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='mt-6 flex justify-end'>
|
<div className='mt-6 flex justify-end'>
|
||||||
<Button
|
<Button
|
||||||
className='bg-transparent border border-primary text-primary w-fit px-6'
|
className='bg-transparent border border-primary text-primary w-fit px-6'
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
>
|
>
|
||||||
<div className='flex gap-1'>
|
<div className='flex gap-1'>
|
||||||
<AddSquare color={COLORS.primary} size={20} />
|
<AddSquare color={COLORS.primary} size={20} />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import * as api from "../service/OrderService";
|
import * as api from "../service/OrderService";
|
||||||
|
|
||||||
export const useGetProducts = () => {
|
export const useGetProducts = () => {
|
||||||
@@ -7,3 +7,9 @@ export const useGetProducts = () => {
|
|||||||
queryFn: api.getProducts,
|
queryFn: api.getProducts,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSubmitOrder = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.submitOrder,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import axios from "@/config/axios";
|
import axios from "@/config/axios";
|
||||||
import type { ProductsResponseType } from "../type/Types";
|
import type { OrderType, ProductsResponseType } from "../type/Types";
|
||||||
|
|
||||||
export const getProducts = async () => {
|
export const getProducts = async () => {
|
||||||
const { data } = await axios.get<ProductsResponseType>(`/public/products`);
|
const { data } = await axios.get<ProductsResponseType>(`/public/products`);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const submitOrder = async (params: OrderType[]) => {
|
||||||
|
const { data } = await axios.post(`/public/order`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
import type { StoreType } from "../type/Types";
|
||||||
|
|
||||||
export const useAuthStore = create((set) => ({
|
export const useAuthStore = create<StoreType>((set) => ({
|
||||||
phone: "",
|
items: [],
|
||||||
setPhone(value) {
|
setItems(value) {
|
||||||
set({ phone: value });
|
set({ items: value });
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -11,14 +11,19 @@ export type CategoryType = {
|
|||||||
title: string;
|
title: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AttachmentsType = {
|
||||||
|
url: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type OrderType = {
|
export type OrderType = {
|
||||||
productId: number;
|
productId: number;
|
||||||
quantity?: number;
|
quantity?: number;
|
||||||
// attributesValues: [],
|
attributes: {
|
||||||
attachments: {
|
attributeId: number;
|
||||||
url: string;
|
value: string | number;
|
||||||
type: string;
|
|
||||||
}[];
|
}[];
|
||||||
|
attachments: AttachmentsType[];
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -56,3 +61,8 @@ export type ProductType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ProductsResponseType = BaseResponse<ProductType[]>;
|
export type ProductsResponseType = BaseResponse<ProductType[]>;
|
||||||
|
|
||||||
|
export type StoreType = {
|
||||||
|
items: OrderType[];
|
||||||
|
setItems: (value: OrderType[]) => void;
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import * as api from "../service/UploaderService";
|
||||||
|
|
||||||
|
export const useSingleUpload = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.singleUpload,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMultiUpload = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.multiUpload,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import axios from "@/config/axios";
|
||||||
|
import type {
|
||||||
|
MultiUploadResponseType,
|
||||||
|
SingleUploadResponseType,
|
||||||
|
} from "../types/Types";
|
||||||
|
|
||||||
|
export const singleUpload = async (file: File) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
const { data } = await axios.post<SingleUploadResponseType>(
|
||||||
|
"/public/single-file",
|
||||||
|
formData,
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const multiUpload = async (files: File[]) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
files?.map((file) => {
|
||||||
|
formData.append("files", file);
|
||||||
|
});
|
||||||
|
const { data } = await axios.post<MultiUploadResponseType>(
|
||||||
|
"/public/multi-file",
|
||||||
|
formData,
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import type { BaseResponse } from "@/shared/types/Types";
|
||||||
|
|
||||||
|
export type SingleUploadType = {
|
||||||
|
url: string;
|
||||||
|
file: {
|
||||||
|
url: string;
|
||||||
|
key: string;
|
||||||
|
bucket: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SingleUploadResponseType = BaseResponse<SingleUploadType>;
|
||||||
|
export type MultiUploadResponseType = BaseResponse<SingleUploadType[]>;
|
||||||
Reference in New Issue
Block a user