diff --git a/src/pages/products/product-form/ProductFormPage.tsx b/src/pages/products/product-form/ProductFormPage.tsx index f256b89..c132d24 100644 --- a/src/pages/products/product-form/ProductFormPage.tsx +++ b/src/pages/products/product-form/ProductFormPage.tsx @@ -28,9 +28,9 @@ const productSchema = yup.object({ .min(0, 'تعداد فروخته شده نمی‌تواند منفی باشد') .optional(), type: yup.number().oneOf([0, 1, 2, 3]).default(1), + variant_attribute_name: yup.string().optional(), category_ids: yup.array().of(yup.number()).default([]), product_option_id: yup.number().transform(createOptionalNumberTransform()).nullable(), - attributes: yup.object().default({}), file_ids: yup.array().of(yup.object()).default([]), variants: yup.array().default([]), }); @@ -41,9 +41,6 @@ const ProductFormPage = () => { const isEdit = !!id; const [uploadedImages, setUploadedImages] = useState([]); - const [attributes, setAttributes] = useState>({}); - const [newAttributeKey, setNewAttributeKey] = useState(''); - const [newAttributeValue, setNewAttributeValue] = useState(''); const [isUploading, setIsUploading] = useState(false); const { data: product, isLoading: isLoadingProduct } = useProduct(id || '', isEdit); @@ -74,9 +71,9 @@ const ProductFormPage = () => { enabled: true, total_sold: undefined, type: 1, + variant_attribute_name: '', category_ids: [], product_option_id: undefined, - attributes: {}, file_ids: [], variants: [] } @@ -119,6 +116,19 @@ const ProductFormPage = () => { console.log('✅ Successfully processed variants:', formVariants.length); + // استخراج نام ویژگی Variant از فیلد variant_attributes + let variantAttributeName = ''; + if ((product as any).variant_attributes && Array.isArray((product as any).variant_attributes) && (product as any).variant_attributes.length > 0) { + const firstAttribute = (product as any).variant_attributes[0]; + if (firstAttribute && firstAttribute.name) { + variantAttributeName = firstAttribute.name; + } + } + // Fallback to direct field if exists + if (!variantAttributeName && (product as any).variant_attribute_name) { + variantAttributeName = (product as any).variant_attribute_name; + } + reset({ name: product.name, description: product.description || '', @@ -126,32 +136,33 @@ const ProductFormPage = () => { enabled: product.enabled, total_sold: product.total_sold || 0, type: 1, + variant_attribute_name: variantAttributeName, category_ids: categoryIds, product_option_id: product.product_option_id || undefined, - attributes: product.attributes || {}, file_ids: (product.file_ids && product.file_ids.length > 0 ? product.file_ids : (product as any).files || []), variants: formVariants }); const initialImages = (product.file_ids && product.file_ids.length > 0 ? product.file_ids : (product as any).files || []); setUploadedImages(initialImages); setValue('file_ids', initialImages, { shouldValidate: true, shouldDirty: false }); - setAttributes(product.attributes || {}); } }, [isEdit, product, reset]); const handleFileUpload = async (file: File) => { try { const result = await uploadFile(file); - const newImage: ProductImage = { - id: result.id, - url: result.url, - alt: file.name, - order: uploadedImages.length - }; - const updatedImages = [...uploadedImages, newImage]; - setUploadedImages(updatedImages); - setValue('file_ids', updatedImages, { shouldValidate: true, shouldDirty: true }); + setUploadedImages(prev => { + const newImage: ProductImage = { + id: result.id, + url: result.url, + alt: file.name, + order: prev.length + }; + const updated = [...prev, newImage]; + setValue('file_ids', updated, { shouldValidate: true, shouldDirty: true }); + return updated; + }); return result; } catch (error) { @@ -167,25 +178,7 @@ const ProductFormPage = () => { deleteFile(fileId); }; - const handleAddAttribute = () => { - if (newAttributeKey.trim() && newAttributeValue.trim()) { - const updatedAttributes = { - ...attributes, - [newAttributeKey.trim()]: newAttributeValue.trim() - }; - setAttributes(updatedAttributes); - setValue('attributes', updatedAttributes, { shouldValidate: true, shouldDirty: true }); - setNewAttributeKey(''); - setNewAttributeValue(''); - } - }; - const handleRemoveAttribute = (key: string) => { - const updatedAttributes = { ...attributes }; - delete updatedAttributes[key]; - setAttributes(updatedAttributes); - setValue('attributes', updatedAttributes, { shouldValidate: true, shouldDirty: true }); - }; const onSubmit = (data: any) => { const convertedData = convertPersianNumbersInObject(data); @@ -204,7 +197,8 @@ const ProductFormPage = () => { enabled: convertedData.enabled, total_sold: convertedData.total_sold || 0, type: 1, - attributes: convertPersianNumbersInObject(attributes), + variant_attribute_name: convertedData.variant_attribute_name || '', + attributes: {}, category_ids: convertedData.category_ids.length > 0 ? convertedData.category_ids : [], product_option_id: convertedData.product_option_id || null, file_ids: validImageIds @@ -225,9 +219,8 @@ const ProductFormPage = () => { stock_managed: variant.stock_managed, stock_number: variant.stock_number, weight: variant.weight, - product_option_id: variant.product_option_id || null, file_ids: Array.isArray(variant.file_ids) ? variant.file_ids.map((file: any) => Number(typeof file === 'object' ? file.id : file)).filter((id: number) => !isNaN(id)) : [], - attributes: variant.attributes && Object.keys(variant.attributes).length > 0 ? variant.attributes : {}, + attributes: variant.attributes && convertedData.variant_attribute_name && variant.attributes[convertedData.variant_attribute_name] !== undefined ? { [convertedData.variant_attribute_name]: variant.attributes[convertedData.variant_attribute_name] } : {}, meta: variant.meta && Object.keys(variant.meta).length > 0 ? variant.meta : {} })) || []; @@ -250,9 +243,8 @@ const ProductFormPage = () => { stock_managed: variant.stock_managed, stock_number: variant.stock_number, weight: variant.weight, - product_option_id: variant.product_option_id || null, file_ids: Array.isArray(variant.file_ids) ? variant.file_ids.map((file: any) => Number(typeof file === 'object' ? file.id : file)).filter((id: number) => !isNaN(id)) : [], - attributes: variant.attributes && Object.keys(variant.attributes).length > 0 ? variant.attributes : {}, + attributes: variant.attributes && convertedData.variant_attribute_name && variant.attributes[convertedData.variant_attribute_name] !== undefined ? { [convertedData.variant_attribute_name]: variant.attributes[convertedData.variant_attribute_name] } : {}, meta: variant.meta && Object.keys(variant.meta).length > 0 ? variant.meta : {} })) || []; @@ -358,6 +350,13 @@ const ProductFormPage = () => { placeholder="مدرن، کلاسیک، مینیمال..." /> + +
- {/* Custom Attributes */} -
-

- ویژگی‌های سفارشی -

- {/* Add New Attribute */} -
- setNewAttributeKey(e.target.value)} - placeholder="نام ویژگی (مثل: رنگ، سایز)" - className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-1 focus:ring-primary-500 dark:bg-gray-700 dark:text-gray-100" - /> - setNewAttributeValue(e.target.value)} - placeholder="مقدار (مثل: قرمز، بزرگ)" - className="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:outline-none focus:ring-1 focus:ring-primary-500 dark:bg-gray-700 dark:text-gray-100" - /> - -
- - {/* Current Attributes */} - {Object.keys(attributes).length > 0 && ( -
-

- ویژگی‌های فعلی: -

-
- {Object.entries(attributes).map(([key, value]) => ( -
- - {key}: {String(value)} - - -
- ))} -
-
- )} -
{/* Preview */} {formValues.name && ( @@ -569,6 +512,11 @@ const ProductFormPage = () => { استایل: {formValues.design_style} )} + {formValues.variant_attribute_name && ( +
+ نام ویژگی Variant: {formValues.variant_attribute_name} +
+ )} {formValues.category_ids && formValues.category_ids.length > 0 && (
دسته‌بندی‌ها: { @@ -582,11 +530,7 @@ const ProductFormPage = () => { تعداد Variants: {formValues.variants.length} نوع
)} - {Object.keys(attributes).length > 0 && ( -
- ویژگی‌ها: {Object.keys(attributes).length} مورد -
- )} +
{formValues.enabled && ( @@ -634,7 +578,7 @@ const ProductFormPage = () => {
  • • نام محصول باید واضح و جذاب باشد
  • • میتوانید چندین دسته‌بندی برای محصول انتخاب کنید
  • • گزینه محصول برای محصولات متغیر (با رنگ، سایز و...) استفاده میشود
  • -
  • • ویژگی‌های سفارشی برای اطلاعات اضافی محصول مفید هستند
  • +
  • • نام ویژگی Variant برای تعیین کلید ویژگی در هر variant استفاده می‌شود
  • • Variants برای انواع مختلف محصول استفاده میشود
  • • اولین تصویر به عنوان تصویر اصلی محصول استفاده می‌شود