"use client"; import { useState, useRef } from "react"; import Image from "next/image"; import { useMutation, useQuery } from "@apollo/client"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Card } from "@/components/ui/card"; import { CREATE_PRODUCT, UPDATE_PRODUCT } from "@/graphql/mutations"; import { GET_CATEGORIES } from "@/graphql/queries"; import { X, Star, Upload } from "lucide-react"; import { toast } from "sonner"; interface Product { id: string; name: string; article: string; description: string; price: number; quantity: number; type: "PRODUCT" | "CONSUMABLE"; category: { id: string; name: string } | null; brand: string; color: string; size: string; weight: number; dimensions: string; material: string; images: string[]; mainImage: string; isActive: boolean; } interface ProductFormProps { product?: Product | null; onSave: () => void; onCancel: () => void; } export function ProductForm({ product, onSave, onCancel }: ProductFormProps) { const [formData, setFormData] = useState({ name: product?.name || "", article: product?.article || "", description: product?.description || "", price: product?.price || 0, quantity: product?.quantity || 0, type: product?.type || ("PRODUCT" as "PRODUCT" | "CONSUMABLE"), categoryId: product?.category?.id || "none", brand: product?.brand || "", color: product?.color || "", size: product?.size || "", weight: product?.weight || 0, dimensions: product?.dimensions || "", material: product?.material || "", images: product?.images || [], mainImage: product?.mainImage || "", isActive: product?.isActive ?? true, }); const [isUploading] = useState(false); const [uploadingImages, setUploadingImages] = useState>( new Set() ); const fileInputRef = useRef(null); const [createProduct, { loading: creating }] = useMutation(CREATE_PRODUCT); const [updateProduct, { loading: updating }] = useMutation(UPDATE_PRODUCT); // Загружаем категории const { data: categoriesData } = useQuery(GET_CATEGORIES); const loading = creating || updating; const handleInputChange = ( field: string, value: string | number | boolean ) => { setFormData((prev) => ({ ...prev, [field]: value, })); }; const handleImageUpload = async (files: FileList) => { const newUploadingIndexes = new Set(); const startIndex = formData.images.length; // Добавляем плейсхолдеры для загружаемых изображений const placeholders = Array.from(files).map((_, index) => { newUploadingIndexes.add(startIndex + index); return ""; // Пустой URL как плейсхолдер }); setUploadingImages((prev) => new Set([...prev, ...newUploadingIndexes])); setFormData((prev) => ({ ...prev, images: [...prev.images, ...placeholders], })); try { // Загружаем каждое изображение const uploadPromises = Array.from(files).map(async (file, index) => { const actualIndex = startIndex + index; const formData = new FormData(); formData.append("file", file); formData.append("type", "product"); const response = await fetch("/api/upload-file", { method: "POST", body: formData, }); if (!response.ok) { throw new Error("Ошибка загрузки изображения"); } const result = await response.json(); return { index: actualIndex, url: result.fileUrl }; }); const results = await Promise.all(uploadPromises); // Обновляем URLs загруженных изображений setFormData((prev) => { const newImages = [...prev.images]; results.forEach(({ index, url }) => { newImages[index] = url; }); return { ...prev, images: newImages, mainImage: prev.mainImage || results[0]?.url || "", // Устанавливаем первое изображение как главное }; }); toast.success("Изображения успешно загружены"); } catch (error) { console.error("Error uploading images:", error); toast.error("Ошибка загрузки изображений"); // Удаляем неудачные плейсхолдеры setFormData((prev) => ({ ...prev, images: prev.images.slice(0, startIndex), })); } finally { // Убираем индикаторы загрузки setUploadingImages((prev) => { const updated = new Set(prev); newUploadingIndexes.forEach((index) => updated.delete(index)); return updated; }); } }; const handleRemoveImage = (indexToRemove: number) => { setFormData((prev) => { const newImages = prev.images.filter( (_, index) => index !== indexToRemove ); const removedImageUrl = prev.images[indexToRemove]; return { ...prev, images: newImages, mainImage: prev.mainImage === removedImageUrl ? newImages[0] || "" : prev.mainImage, }; }); }; const handleSetMainImage = (imageUrl: string) => { setFormData((prev) => ({ ...prev, mainImage: imageUrl, })); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!formData.name || !formData.article || formData.price <= 0) { toast.error("Пожалуйста, заполните все обязательные поля"); return; } try { const input = { name: formData.name, article: formData.article, description: formData.description || undefined, price: formData.price, quantity: formData.quantity, type: formData.type, categoryId: formData.categoryId && formData.categoryId !== "none" ? formData.categoryId : undefined, brand: formData.brand || undefined, color: formData.color || undefined, size: formData.size || undefined, weight: formData.weight || undefined, dimensions: formData.dimensions || undefined, material: formData.material || undefined, images: formData.images.filter((img) => img), // Убираем пустые строки mainImage: formData.mainImage || undefined, isActive: formData.isActive, }; if (product) { await updateProduct({ variables: { id: product.id, input }, refetchQueries: ["GetMyProducts"], }); toast.success("Товар успешно обновлен"); } else { console.log("📝 СОЗДАНИЕ ТОВАРА - ОТПРАВКА ЗАПРОСА:", input); const result = await createProduct({ variables: { input }, refetchQueries: ["GetMyProducts"], }); console.log("📝 РЕЗУЛЬТАТ СОЗДАНИЯ ТОВАРА:", result); toast.success("Товар успешно создан"); } onSave(); } catch (error: unknown) { console.error("Error saving product:", error); toast.error((error as Error).message || "Ошибка при сохранении товара"); } }; return (
{/* Основная информация */}

Основная информация

handleInputChange("name", e.target.value)} placeholder="iPhone 15 Pro Max" className="glass-input text-white placeholder:text-white/40 h-10" required />
handleInputChange("article", e.target.value)} placeholder="IP15PM-256-BLU" className="glass-input text-white placeholder:text-white/40 h-10" required />