import { useEffect, useMemo, useState } from 'react' import { createPortal } from 'react-dom' import { Sparkles, X } from 'lucide-react' import { useToast } from '../context/ToastContext' import { ApiError } from '../lib/api' import { listProductCategories, mapProductCategoryToUi } from '../services/productCategoryService' import { createProductByAi, type ProductAiLanguage } from '../services/productAiService' import type { Category } from '../types/category' import { flattenCategories } from '../utils/categories' import { SearchableSelect } from './SearchableSelect' import modalStyles from './CategoryModal.module.css' import aiStyles from '../styles/ai.module.css' import styles from './AddProductByAiModal.module.css' import fieldStyles from '../pages/AddNewProductPage.module.css' interface AddProductByAiModalProps { open: boolean onClose: () => void onCreated: (productId: string) => void } const ANIMATION_MS = 220 export function AddProductByAiModal({ open, onClose, onCreated, }: AddProductByAiModalProps) { const { showToast } = useToast() const [mounted, setMounted] = useState(open) const [closing, setClosing] = useState(false) const [categories, setCategories] = useState([]) const [categoryId, setCategoryId] = useState('') const [name, setName] = useState('') const [language, setLanguage] = useState('en') const [isLoadingCategories, setIsLoadingCategories] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false) const [error, setError] = useState('') const categoryOptions = useMemo(() => flattenCategories(categories), [categories]) useEffect(() => { if (open) { setMounted(true) setClosing(false) setCategoryId('') setName('') setLanguage('en') setError('') } else if (mounted) { setClosing(true) const timer = setTimeout(() => { setMounted(false) setClosing(false) }, ANIMATION_MS) return () => clearTimeout(timer) } }, [open, mounted]) useEffect(() => { if (!open) return const controller = new AbortController() void loadCategories(controller.signal) return () => controller.abort() }, [open]) useEffect(() => { if (!mounted || closing) return const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape' && !isSubmitting) onClose() } document.addEventListener('keydown', onKey) return () => document.removeEventListener('keydown', onKey) }, [mounted, closing, isSubmitting, onClose]) async function loadCategories(signal?: AbortSignal) { setIsLoadingCategories(true) setError('') try { const items = await listProductCategories(signal) setCategories(items.map(mapProductCategoryToUi)) } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') return if (err instanceof ApiError) { setError(err.message) } else { setError('Unable to load categories.') } } finally { setIsLoadingCategories(false) } } async function handleSubmit(e: React.FormEvent) { e.preventDefault() if (!categoryId || !name.trim()) return setIsSubmitting(true) setError('') try { const result = await createProductByAi({ categoryId, name: name.trim(), language, }) showToast(result.message, 'success') onCreated(result.product.id) onClose() } catch (err) { if (err instanceof ApiError) { setError(err.message) } else if (err instanceof Error) { setError(err.message) } else { setError('Unable to create product with AI.') } } finally { setIsSubmitting(false) } } if (!mounted) return null return createPortal(
e.target === e.currentTarget && !isSubmitting && onClose()} role="presentation" >

Add Product by AI

AI will generate product content and technical data. You can add images and variations afterward.

void handleSubmit(e)}>
setName(e.target.value)} placeholder={ language === 'fa' ? 'نام محصول را وارد کنید' : 'Enter product name' } dir={language === 'fa' ? 'rtl' : 'ltr'} className={language === 'fa' ? 'faText' : undefined} disabled={isSubmitting} required />
{error &&

{error}

}
, document.body, ) }