single order
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
VITE_TOKEN_NAME = 'negareh_t'
|
||||
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 { AttributeType } from '../type/Types'
|
||||
import type { AttributeType, OrderType } from '../type/Types'
|
||||
import { clx } from '@/helpers/utils'
|
||||
import { FieldTypeEnum } from '../enum/OrderEnum'
|
||||
import Select from '@/components/Select'
|
||||
@@ -8,14 +8,28 @@ import RadioGroup from '@/components/RadioGroup'
|
||||
import DatePickerComponent from '@/components/DatePicker'
|
||||
import Input from '@/components/Input'
|
||||
import Textarea from '@/components/Textarea'
|
||||
import type { FormikProps } from 'formik'
|
||||
|
||||
type Props = {
|
||||
attributes?: AttributeType[]
|
||||
attributes?: AttributeType[],
|
||||
formik: FormikProps<OrderType>
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className={clx(
|
||||
@@ -35,6 +49,7 @@ const ManageAttribute: FC<Props> = (props) => {
|
||||
value: value.value
|
||||
}
|
||||
})}
|
||||
onChange={(e) => handleChange(item.id, e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@@ -45,13 +60,14 @@ const ManageAttribute: FC<Props> = (props) => {
|
||||
<div className='flex gap-5 flex-wrap'>
|
||||
{
|
||||
item.values?.map((value) => {
|
||||
const object = formik.values.attributes.find(o => o.attributeId === item.id)
|
||||
return (
|
||||
<div key={value.id} className='flex gap-2 items-center'>
|
||||
<div>{value.value}</div>
|
||||
<Checkbox
|
||||
name={item.id + ''}
|
||||
value={value.value}
|
||||
onCheckedChange={(c) => console.log(c)}
|
||||
checked={object?.value === value.value}
|
||||
onCheckedChange={(checked) => handleChange(item.id, checked ? value.value : '')}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@@ -60,7 +76,8 @@ const ManageAttribute: FC<Props> = (props) => {
|
||||
</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 (
|
||||
<div key={item.id} className='mt-6'>
|
||||
<div className='text-sm mb-3'>{item.name}</div>
|
||||
@@ -71,16 +88,18 @@ const ManageAttribute: FC<Props> = (props) => {
|
||||
value: value.value
|
||||
}
|
||||
})}
|
||||
onChange={(v) => console.log(v)}
|
||||
selected=''
|
||||
onChange={(v) => handleChange(item.id, v)}
|
||||
selected={object?.value + '' || ''}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
else if (item.type === FieldTypeEnum.date)
|
||||
return (
|
||||
<div key={item.id} className='mt-6'>
|
||||
<DatePickerComponent
|
||||
onChange={(date) => console.log(date)}
|
||||
onChange={(date) => handleChange(item.id, date)}
|
||||
placeholder=''
|
||||
label={item.name}
|
||||
/>
|
||||
@@ -92,6 +111,7 @@ const ManageAttribute: FC<Props> = (props) => {
|
||||
<Input
|
||||
label={item.name}
|
||||
type={item.type}
|
||||
onChange={(e) => handleChange(item.id, e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@@ -100,6 +120,7 @@ const ManageAttribute: FC<Props> = (props) => {
|
||||
<div key={item.id} className='mt-6'>
|
||||
<Textarea
|
||||
label={item.name}
|
||||
onChange={(e) => handleChange(item.id, e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -9,15 +9,21 @@ import { AddSquare } from 'iconsax-react'
|
||||
import ProductsSelect from './ProductsSelect'
|
||||
import { useFormik } from 'formik'
|
||||
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 ManageAttribute from './ManageAttribute'
|
||||
import { useMultiUpload, useSingleUpload } from '@/pages/uploader/hooks/useUploader'
|
||||
import { useAuthStore } from '../store/OrderStore'
|
||||
|
||||
const Order: FC = () => {
|
||||
|
||||
const { data } = useGetProducts()
|
||||
const { setItems, items } = useAuthStore()
|
||||
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>({
|
||||
initialValues: {
|
||||
@@ -25,12 +31,37 @@ const Order: FC = () => {
|
||||
attachments: [],
|
||||
quantity: undefined,
|
||||
description: '',
|
||||
attributes: []
|
||||
},
|
||||
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)
|
||||
}
|
||||
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 (
|
||||
<div className='bg-white rounded-3xl p-6 mt-8'>
|
||||
@@ -81,6 +103,7 @@ const Order: FC = () => {
|
||||
<div>
|
||||
<ManageAttribute
|
||||
attributes={productSelected?.attributes}
|
||||
formik={formik}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -107,22 +130,27 @@ const Order: FC = () => {
|
||||
|
||||
<div className='mt-6'>
|
||||
<VoiceRecorder
|
||||
onChange={(value) => formik.setFieldValue('description', value)}
|
||||
label='پیام صوتی'
|
||||
onRecordingComplete={(blob) => {
|
||||
console.log('ضبط صدا تکمیل شد:', blob)
|
||||
const file = new File([blob], "recording.wav", { type: blob.type });
|
||||
setVoiceFile(file);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<UploadBox
|
||||
label='فایل های ضبط شده'
|
||||
label='آپلود فایل'
|
||||
isMultiple
|
||||
onChange={setFiles}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-6 flex justify-end'>
|
||||
<Button
|
||||
className='bg-transparent border border-primary text-primary w-fit px-6'
|
||||
onClick={() => formik.handleSubmit()}
|
||||
>
|
||||
<div className='flex gap-1'>
|
||||
<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";
|
||||
|
||||
export const useGetProducts = () => {
|
||||
@@ -7,3 +7,9 @@ export const useGetProducts = () => {
|
||||
queryFn: api.getProducts,
|
||||
});
|
||||
};
|
||||
|
||||
export const useSubmitOrder = () => {
|
||||
return useMutation({
|
||||
mutationFn: api.submitOrder,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import axios from "@/config/axios";
|
||||
import type { ProductsResponseType } from "../type/Types";
|
||||
import type { OrderType, ProductsResponseType } from "../type/Types";
|
||||
|
||||
export const getProducts = async () => {
|
||||
const { data } = await axios.get<ProductsResponseType>(`/public/products`);
|
||||
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 type { StoreType } from "../type/Types";
|
||||
|
||||
export const useAuthStore = create((set) => ({
|
||||
phone: "",
|
||||
setPhone(value) {
|
||||
set({ phone: value });
|
||||
export const useAuthStore = create<StoreType>((set) => ({
|
||||
items: [],
|
||||
setItems(value) {
|
||||
set({ items: value });
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -11,14 +11,19 @@ export type CategoryType = {
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type AttachmentsType = {
|
||||
url: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type OrderType = {
|
||||
productId: number;
|
||||
quantity?: number;
|
||||
// attributesValues: [],
|
||||
attachments: {
|
||||
url: string;
|
||||
type: string;
|
||||
attributes: {
|
||||
attributeId: number;
|
||||
value: string | number;
|
||||
}[];
|
||||
attachments: AttachmentsType[];
|
||||
description?: string;
|
||||
};
|
||||
|
||||
@@ -56,3 +61,8 @@ export type 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