import { useEffect, useState } from 'react' import { Link, useParams, useNavigate } from 'react-router-dom' import { SearchableSelect } from '../components/SearchableSelect' import { ImageCropper } from '../components/ImageCropper' import { TagInput } from '../components/TagInput' import { RichTextEditor } from '../components/RichTextEditor' import { Breadcrumbs } from '../components/Breadcrumbs' import { useToast } from '../context/ToastContext' import { useT } from '../i18n/useT' import { ApiError } from '../lib/api' import { listInstructionCategories, mapInstructionCategoryToUi, } from '../services/instructionCategoryService' import { resolveDataUrlToMediaId, resolveHtmlEmbeddedDataUrls, } from '../services/mediaService' import { createInstruction, getInstruction, mapInstructionApiToFormState, updateInstruction, } from '../services/instructionService' import type { Category } from '../types/category' import type { VideoProvider } from '../types/instruction' import { VIDEO_PROVIDER_OPTIONS } from '../types/instruction' import { flattenCategories } from '../utils/categories' import pageStyles from '../components/PageContent.module.css' import styles from './AddNewProductPage.module.css' export function AddNewInstructionPage() { const { id } = useParams() const navigate = useNavigate() const { showToast } = useToast() const t = useT() const isEdit = Boolean(id) const [categories, setCategories] = useState([]) const [categoryId, setCategoryId] = useState('') const [titleFa, setTitleFa] = useState('') const [titleEn, setTitleEn] = useState('') const [abstract, setAbstract] = useState('') const [descriptionHtml, setDescriptionHtml] = useState('') const [videoProvider, setInstructionProvider] = useState('youtube') const [embedCode, setEmbedCode] = useState('') const [coverImage, setCoverImage] = useState(null) const [tags, setTags] = useState([]) const [featuredMediaId, setFeaturedMediaId] = useState(null) const [loaded, setLoaded] = useState(!isEdit) const [isSubmitting, setIsSubmitting] = useState(false) const [error, setError] = useState('') const categoryOptions = flattenCategories(categories) useEffect(() => { const controller = new AbortController() async function loadCategories() { try { const items = await listInstructionCategories(controller.signal) setCategories(items.map(mapInstructionCategoryToUi)) } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') return if (err instanceof ApiError) { setError(err.message) } else { setError(t('instruction.categories.errorLoad')) } } } void loadCategories() return () => controller.abort() }, []) useEffect(() => { if (!isEdit || !id) return const controller = new AbortController() async function loadVideo() { setError('') try { const video = await getInstruction(id!, controller.signal) const form = mapInstructionApiToFormState(video) setCategoryId(form.categoryId) setTitleFa(form.titleFa) setTitleEn(form.titleEn) setAbstract(form.abstract) setDescriptionHtml(form.descriptionHtml) setInstructionProvider(form.videoProvider) setEmbedCode(form.embedCode) setCoverImage(form.coverImage) setTags(form.tags) setFeaturedMediaId(form.featuredMediaId) setLoaded(true) } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') return navigate('/instructions/list', { replace: true }) } } void loadVideo() return () => controller.abort() }, [isEdit, id, navigate]) if (!loaded) return null async function handleSubmit(e: React.FormEvent) { e.preventDefault() setIsSubmitting(true) setError('') try { const nextFeaturedMediaId = await resolveDataUrlToMediaId( coverImage, 'instruction-cover-image.jpg', featuredMediaId, ) const resolvedDescriptionHtml = await resolveHtmlEmbeddedDataUrls(descriptionHtml) const trimmedEmbed = embedCode.trim() const payload = { titleFa: titleFa.trim(), titleEn: titleEn.trim() || undefined, abstract: abstract.trim(), descriptionHtml: resolvedDescriptionHtml, videoProvider: trimmedEmbed ? videoProvider : undefined, embedCode: trimmedEmbed || undefined, categoryId: categoryId || undefined, featuredMediaId: nextFeaturedMediaId ?? undefined, tags, status: 'published' as const, } if (isEdit && id) { await updateInstruction(id, { ...payload, categoryId: categoryId || null, featuredMediaId: nextFeaturedMediaId, }) showToast(t('instruction.form.toast.updated'), 'success') } else { await createInstruction(payload) showToast(t('instruction.form.toast.created'), 'success') } navigate('/instructions/list') } catch (err) { if (err instanceof ApiError) { setError(err.message) } else if (err instanceof Error) { setError(err.message) } else { setError(t('instruction.form.errorSave')) } } finally { setIsSubmitting(false) } } return (

{isEdit ? t('title.editInstruction') : t('title.addInstruction')}

{isEdit ? t('instruction.form.edit.subtitle') : t('instruction.form.add.subtitle')}

{error && (
{error}
)}
setTitleFa(e.target.value)} required disabled={isSubmitting} />
setTitleEn(e.target.value)} disabled={isSubmitting} />