change variant crud

This commit is contained in:
hamid zarghami
2025-11-29 12:09:42 +03:30
parent 7cf27823ee
commit 9646f76001
4 changed files with 42 additions and 69 deletions
+3 -16
View File
@@ -1,6 +1,6 @@
import { type FC } from 'react' import { type FC } from 'react'
import PageTitle from '../../components/PageTitle' import PageTitle from '../../components/PageTitle'
import { useGetProductVariants, useGetProductById } from './hooks/useProductData' import { useGetProductVariants } from './hooks/useProductData'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import PageLoading from '@/components/PageLoading' import PageLoading from '@/components/PageLoading'
import type { ProductVariantDetailType } from './types/Types' import type { ProductVariantDetailType } from './types/Types'
@@ -13,26 +13,13 @@ const ProductVariant: FC = () => {
const { id } = useParams() const { id } = useParams()
const { data, isLoading, refetch } = useGetProductVariants(id!) const { data, isLoading, refetch } = useGetProductVariants(id!)
const { data: productData } = useGetProductById(id!)
const categoryTheme = productData?.results?.product?.category?.theme
const getVariantColumnHeader = () => { const getVariantColumnHeader = () => {
switch (categoryTheme) { return 'تنوع'
case 'Size': return 'سایز'
case 'Color': return 'رنگ'
case 'Meterage': return 'متریاژ'
default: return 'تنوع'
}
} }
const getVariantValue = (variant: ProductVariantDetailType) => { const getVariantValue = (variant: ProductVariantDetailType) => {
switch (categoryTheme) { return variant.themeValue?.name || variant.themeValue?.value?.toString() || '-'
case 'Size': return variant.size?.value || '-'
case 'Color': return variant.color?.name || '-'
case 'Meterage': return variant.meterage?.value || '-'
default: return '-'
}
} }
const getMarketStatusVariant = (status: string) => { const getMarketStatusVariant = (status: string) => {
+24 -48
View File
@@ -6,8 +6,8 @@ import DatePickerComponent from '@/components/DatePicker'
import { useState, type FC } from 'react' import { useState, type FC } from 'react'
import { useParams } from 'react-router-dom' import { useParams } from 'react-router-dom'
import { useCreateProductVariant, useGetProductById, useGetProductVariants } from '../hooks/useProductData' import { useCreateProductVariant, useGetProductById, useGetProductVariants } from '../hooks/useProductData'
import { useGetCategoryVariants } from '@/pages/category'
import type { CreateProductVariantRequestType } from '../types/Types' import type { CreateProductVariantRequestType } from '../types/Types'
import { useGetThemeValues } from '@/pages/theme/hooks/useThemeData'
import { useGetWarranties } from '@/pages/warranty/hooks/useWarrantyData' import { useGetWarranties } from '@/pages/warranty/hooks/useWarrantyData'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { extractErrorMessage } from '@/helpers/utils' import { extractErrorMessage } from '@/helpers/utils'
@@ -26,7 +26,7 @@ const CreateVariant: FC = () => {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const { data: productData } = useGetProductById(id!) const { data: productData } = useGetProductById(id!)
const { data: categoryData } = useGetCategoryVariants(productData?.results?.product?.category?._id ?? '') const { data: themeValues } = useGetThemeValues(productData?.results?.product?.category?.theme as string)
const { data: variantsData, refetch: refetchVariants } = useGetProductVariants(id!) const { data: variantsData, refetch: refetchVariants } = useGetProductVariants(id!)
const createProductVariant = useCreateProductVariant() const createProductVariant = useCreateProductVariant()
const { data: warrantiesData } = useGetWarranties() const { data: warrantiesData } = useGetWarranties()
@@ -34,13 +34,15 @@ const CreateVariant: FC = () => {
const [showModal, setShowModal] = useState<boolean>(false) const [showModal, setShowModal] = useState<boolean>(false)
const categoryTheme = productData?.results?.product?.category?.theme const categoryTheme = productData?.results?.product?.category?.theme
const categoryVariants = categoryData?.results?.data?.category?.categoryVariants
const isNoVariantTheme = categoryTheme === 'noColor_noSize' const isNoVariantTheme = categoryTheme === 'noColor_noSize'
const isThemeNull = !categoryTheme
const existingVariantsCount = variantsData?.results?.count || 0 const existingVariantsCount = variantsData?.results?.count || 0
const canCreateVariant = !isNoVariantTheme || existingVariantsCount === 0 const canCreateVariant = (isThemeNull || isNoVariantTheme)
? existingVariantsCount === 0
: true
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
variantId: isNoVariantTheme themeValueId: (isNoVariantTheme || isThemeNull)
? Yup.string().optional() ? Yup.string().optional()
: Yup.string().required('انتخاب تنوع الزامی است'), : Yup.string().required('انتخاب تنوع الزامی است'),
warrantyId: Yup.string().required('انتخاب گارانتی الزامی است'), warrantyId: Yup.string().required('انتخاب گارانتی الزامی است'),
@@ -59,7 +61,7 @@ const CreateVariant: FC = () => {
}) })
const initialValues = { const initialValues = {
variantId: '', themeValueId: '',
warrantyId: '', warrantyId: '',
order_limit: '', order_limit: '',
sellerSpecialCode: '' as string | undefined, sellerSpecialCode: '' as string | undefined,
@@ -76,34 +78,14 @@ const CreateVariant: FC = () => {
} }
const getVariantOptions = () => { const getVariantOptions = () => {
switch (categoryTheme) { return themeValues?.results?.data?.map(themeValue => ({
case 'Size': value: themeValue._id,
return categoryVariants?.sizes?.map(size => ({ label: themeValue.name || themeValue.value?.toString() || '-'
value: size._id.toString(), })) || []
label: size.value
})) || []
case 'Color':
return categoryVariants?.colors?.map((color, index) => ({
value: index.toString(), // استفاده از index به عنوان ID موقت
label: color.name
})) || []
case 'Meterage':
return categoryVariants?.meterages?.map(meterage => ({
value: meterage._id.toString(),
label: meterage.value
})) || []
default:
return []
}
} }
const getVariantLabel = () => { const getVariantLabel = () => {
switch (categoryTheme) { return categoryTheme ? 'تنوع' : 'تنوع'
case 'Size': return 'سایز'
case 'Color': return 'رنگ'
case 'Meterage': return 'متریاژ'
default: return 'تنوع'
}
} }
const getWarrantyOptions = () => { const getWarrantyOptions = () => {
@@ -131,18 +113,11 @@ const CreateVariant: FC = () => {
// اضافه کردن variant بر اساس theme // اضافه کردن variant بر اساس theme
let variantData: CreateProductVariantRequestType let variantData: CreateProductVariantRequestType
if (isNoVariantTheme) { if (isNoVariantTheme || isThemeNull) {
// در حالت noColor_noSize هیچ variantId ارسال نمی‌شود // در حالت noColor_noSize یا theme null هیچ themeValueId ارسال نمی‌شود
variantData = baseData variantData = baseData
} else if (categoryTheme === 'Size') {
variantData = { ...baseData, sizeId: parseInt(values.variantId) }
} else if (categoryTheme === 'Color') {
// برای رنگ‌ها از index به عنوان ID استفاده می‌کنیم
variantData = { ...baseData, colorId: parseInt(values.variantId) + 1 } // +1 برای اینکه ID از 1 شروع شود
} else if (categoryTheme === 'Meterage') {
variantData = { ...baseData, meterageId: parseInt(values.variantId) }
} else { } else {
variantData = baseData variantData = { ...baseData, themeValueId: values.themeValueId }
} }
if (!values.sellerSpecialCode) { if (!values.sellerSpecialCode) {
@@ -160,8 +135,9 @@ const CreateVariant: FC = () => {
toast.success('تنوع جدید با موفقیت ایجاد شد') toast.success('تنوع جدید با موفقیت ایجاد شد')
// Refetch لیست تنوع‌ها // Refetch لیست تنوع‌ها
refetchVariants() refetchVariants()
// یا می‌توانید از invalidateQueries استفاده کنید // Invalidate لیست تنوع‌ها و جزئیات محصول
queryClient.invalidateQueries({ queryKey: ['product-variants', id] }) queryClient.invalidateQueries({ queryKey: ['product-variants', id] })
queryClient.invalidateQueries({ queryKey: ['product-by-id', id] })
}, },
onError: (error) => { onError: (error) => {
toast.error(extractErrorMessage(error)) toast.error(extractErrorMessage(error))
@@ -199,29 +175,29 @@ const CreateVariant: FC = () => {
> >
{({ setFieldValue }) => ( {({ setFieldValue }) => (
<Form className="space-y-4 w-[500px] mt-4"> <Form className="space-y-4 w-[500px] mt-4">
{/* انتخاب variant - فقط در صورتی که theme نوع noColor_noSize نباشد */} {/* انتخاب variant - فقط در صورتی که theme معتبر باشد و نوع noColor_noSize نباشد */}
{!isNoVariantTheme && ( {!isNoVariantTheme && !isThemeNull && (
<div> <div>
<label className="text-sm font-medium text-gray-700 mb-1 block"> <label className="text-sm font-medium text-gray-700 mb-1 block">
{getVariantLabel()} {getVariantLabel()}
</label> </label>
<Field name="variantId"> <Field name="themeValueId">
{({ field }: FormikFieldProps) => ( {({ field }: FormikFieldProps) => (
<Select <Select
{...field} {...field}
placeholder={`انتخاب ${getVariantLabel().toLowerCase()}`} placeholder={`انتخاب ${getVariantLabel().toLowerCase()}`}
value={field.value} value={field.value}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setFieldValue('variantId', e.target.value)} onChange={(e: React.ChangeEvent<HTMLSelectElement>) => setFieldValue('themeValueId', e.target.value)}
items={getVariantOptions()} items={getVariantOptions()}
/> />
)} )}
</Field> </Field>
<ErrorMessage name="variantId" component="div" className="text-red-500 text-sm mt-1" /> <ErrorMessage name="themeValueId" component="div" className="text-red-500 text-sm mt-1" />
</div> </div>
)} )}
{/* پیام اطلاع‌رسانی برای محصولات بدون تنوع */} {/* پیام اطلاع‌رسانی برای محصولات بدون تنوع */}
{isNoVariantTheme && ( {(isNoVariantTheme || isThemeNull) && (
<div className="p-3 bg-blue-50 border border-blue-200 rounded-lg"> <div className="p-3 bg-blue-50 border border-blue-200 rounded-lg">
<p className="text-sm text-blue-800"> <p className="text-sm text-blue-800">
این محصول تنوع ندارد. فقط میتوانید یک نسخه از محصول ایجاد کنید. این محصول تنوع ندارد. فقط میتوانید یک نسخه از محصول ایجاد کنید.
@@ -12,6 +12,7 @@ import { extractErrorMessage } from '@/helpers/utils'
import { Formik, Form, Field, ErrorMessage } from 'formik' import { Formik, Form, Field, ErrorMessage } from 'formik'
import type { FieldInputProps } from 'formik' import type { FieldInputProps } from 'formik'
import * as Yup from 'yup' import * as Yup from 'yup'
import { useQueryClient } from '@tanstack/react-query'
interface FormikFieldProps { interface FormikFieldProps {
field: FieldInputProps<string | number | undefined> field: FieldInputProps<string | number | undefined>
@@ -26,6 +27,7 @@ interface UpdateVariantProps {
const UpdateVariant: FC<UpdateVariantProps> = ({ variant, onUpdate }) => { const UpdateVariant: FC<UpdateVariantProps> = ({ variant, onUpdate }) => {
const { id } = useParams() const { id } = useParams()
const queryClient = useQueryClient()
const { data: variantData, isLoading, error } = useGetVariantById(id!, variant._id) const { data: variantData, isLoading, error } = useGetVariantById(id!, variant._id)
const updateVariant = useUpdateVariant() const updateVariant = useUpdateVariant()
@@ -104,7 +106,12 @@ const UpdateVariant: FC<UpdateVariantProps> = ({ variant, onUpdate }) => {
onSuccess: () => { onSuccess: () => {
setShowModal(false) setShowModal(false)
toast.success('تنوع با موفقیت بروزرسانی شد') toast.success('تنوع با موفقیت بروزرسانی شد')
// Refetch لیست تنوع‌ها
onUpdate?.() onUpdate?.()
// Invalidate لیست تنوع‌ها و جزئیات محصول
queryClient.invalidateQueries({ queryKey: ['product-variants', id] })
queryClient.invalidateQueries({ queryKey: ['product-by-id', id] })
queryClient.invalidateQueries({ queryKey: ['variant-by-id', id, variant._id] })
}, },
onError: (error) => { onError: (error) => {
toast.error(extractErrorMessage(error)) toast.error(extractErrorMessage(error))
+8 -5
View File
@@ -242,6 +242,11 @@ export type ProductVariantDetailType = {
isFreeShip: boolean; isFreeShip: boolean;
isWholeSale: boolean; isWholeSale: boolean;
saleFormat: SaleFormatType; saleFormat: SaleFormatType;
themeValue?: {
_id: string;
name: string;
value: number;
};
size?: SizeType; size?: SizeType;
color?: ColorType; color?: ColorType;
meterage?: MeterageType; meterage?: MeterageType;
@@ -266,9 +271,7 @@ export type GetVariantByIdResponseType = {
export type CreateProductVariantRequestType = { export type CreateProductVariantRequestType = {
productId: string; productId: string;
colorId?: number; themeValueId?: string;
sizeId?: number;
meterageId?: number;
warrantyId: number; warrantyId: number;
order_limit: number; order_limit: number;
sellerSpecialCode?: string; sellerSpecialCode?: string;
@@ -305,14 +308,14 @@ export type ProductCategoryType = {
icon: string; icon: string;
imageUrl: string; imageUrl: string;
description: string; description: string;
theme: string;
variants: number[]; variants: number[];
hierarchy: string[]; hierarchy: string[];
leaf: boolean; leaf: boolean;
parent: string; parent: string | null;
deleted: boolean; deleted: boolean;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
theme: string;
url: string; url: string;
children: ProductCategoryType[]; children: ProductCategoryType[];
}; };