import { SimpleEditor } from '@/components/tiptap-templates/simple/simple-editor'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Combobox, ComboboxChip, ComboboxChips, ComboboxChipsInput, ComboboxContent, ComboboxEmpty, ComboboxItem, ComboboxList, ComboboxValue, useComboboxAnchor, } from "@/components/ui/combobox"; import { Field, FieldError } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import productRoutes from '@/routes/product'; import type { Product, ProductPrice } from '@/types'; import type { Category } from '@/types/category'; import { Head, Link, useForm } from '@inertiajs/react'; import { ArrowLeft, ImagePlus, Loader, Save, Upload, X } from 'lucide-react'; import React, { useRef, useState } from 'react'; import { NumericFormat } from 'react-number-format'; import { toast } from 'sonner'; interface ExistingImage { kind: 'existing'; id: number; url: string; } interface NewImage { kind: 'new'; file: File; preview: string; } type GalleryItem = ExistingImage | NewImage; export default function ProductEdit({ product, categories }: { product: Product, categories: Category[] }) { const getPrice = (type: string) => { const priceObj = product.prices?.find((p: ProductPrice) => p.price_type === type); return priceObj ? priceObj.price.toString() : ''; }; const initialCategoryIds = product.categories?.map(c => c.id) || []; const initialGallery: GalleryItem[] = (product.images_data ?? []).map(img => ({ kind: 'existing', id: img.id, url: img.url, })); const { data, setData, post, processing, errors } = useForm<{ name: string; description: string; category_ids: number[]; prices: { purchase: string; distributor: string; agent: string; reseller: string; retail: string; }; thumbnail: File | null; images: File[]; delete_image_ids: number[]; stock: string; _method: string; }>({ name: product.name || '', description: product.description || '', category_ids: initialCategoryIds, prices: { purchase: getPrice('purchase'), distributor: getPrice('distributor'), agent: getPrice('agent'), reseller: getPrice('reseller'), retail: getPrice('retail'), }, thumbnail: null, images: [], delete_image_ids: [], stock: product.stock?.toString() || '0', _method: 'PATCH', }); const anchor = useComboboxAnchor(); const thumbnailInputRef = useRef(null); const imagesInputRef = useRef(null); const [thumbnailPreview, setThumbnailPreview] = useState(product.thumbnail_url ?? null); const [thumbnailCleared, setThumbnailCleared] = useState(false); const [gallery, setGallery] = useState(initialGallery); const handleThumbnailChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) { return; } setData('thumbnail', file); setThumbnailCleared(false); const reader = new FileReader(); reader.onloadend = () => setThumbnailPreview(reader.result as string); reader.readAsDataURL(file); }; const removeThumbnail = () => { setData('thumbnail', null); setThumbnailPreview(null); setThumbnailCleared(true); if (thumbnailInputRef.current) { thumbnailInputRef.current.value = ''; } }; const handleImagesChange = (e: React.ChangeEvent) => { const files = Array.from(e.target.files ?? []); if (!files.length) { return; } const newItems: NewImage[] = files.map(file => ({ kind: 'new', file, preview: URL.createObjectURL(file), })); setGallery(prev => [...prev, ...newItems]); setData('images', [...data.images, ...files]); if (imagesInputRef.current) { imagesInputRef.current.value = ''; } }; const removeGalleryItem = (index: number) => { const item = gallery[index]; const updatedGallery = gallery.filter((_, i) => i !== index); setGallery(updatedGallery); if (item.kind === 'existing') { setData(prev => ({ ...prev, delete_image_ids: [...prev.delete_image_ids, item.id], })); } else { const newFiles = updatedGallery .filter((g): g is NewImage => g.kind === 'new') .map(g => g.file); setData('images', newFiles); } }; const onSubmit = (e: React.FormEvent) => { e.preventDefault(); post(productRoutes.update(product.id).url, { forceFormData: true, onSuccess: (response: any) => { toast.success(response.props.flash.success); }, }); }; const filteredCategories = categories.filter((c) => !data.category_ids.includes(c.id)); return (

Ubah Produk

Informasi Produk
setData('name', e.target.value)} autoComplete='off' placeholder='Contoh: Gamis Wanita' maxLength={100} /> setData('stock', values.value)} placeholder="0" autoComplete='off' />
data.category_ids.includes(c.id))} onValueChange={(selected) => { setData( "category_ids", selected.map((item: Category) => item.id) ) }} > {(values: Category[]) => ( <> {values.map((value) => ( {value.name} ))} )} Data tidak ditemukan. {(item: Category) => ( {item.name} )} setData('description', val)} />
Harga Produk
{ setData('prices', { ...data.prices, purchase: values.value }) }} placeholder="Rp 0" autoComplete='off' /> { setData('prices', { ...data.prices, distributor: values.value }) }} placeholder="Rp 0" autoComplete='off' /> { setData('prices', { ...data.prices, agent: values.value }) }} placeholder="Rp 0" autoComplete='off' /> { setData('prices', { ...data.prices, reseller: values.value }) }} placeholder="Rp 0" autoComplete='off' /> { setData('prices', { ...data.prices, retail: values.value }) }} placeholder="Rp 0" autoComplete='off' />
Thumbnail {thumbnailPreview ? (
Thumbnail preview
) : ( )}
Galeri Gambar
{gallery.map((item, index) => (
{`Gambar
))}
); } ProductEdit.layout = { breadcrumbs: [ { title: 'Master', }, ], };