import React, { useEffect, useState } from 'react'; import { ArticlePublishingConfig, getArticlePublishing, saveArticlePublishing, } from '../../service/articlePublishing/articlePublishing.service'; import Banner from '../widgets/Banner'; import Button from '../widgets/Button'; import ErrorWrapper from '../alert/ErrorWrapper'; /** * Sentinel select value meaning "publish as Pages under a parent path". */ const CUSTOM_VALUE: string = '__custom__'; /** * Settings section that lets the merchant choose where Recomaze publishes * AI-generated articles: an existing post type from a live dropdown, or a parent * "path" (e.g. guides) under which articles become child Pages at * /guides/article-name. It also picks the default categories the articles land * in, so large volumes stay organised instead of flooding /blog/. * * @return The article-publishing settings section. */ const ArticlePublishing = (): JSX.Element => { const [config, setConfig] = useState(null); const [postType, setPostType] = useState(''); const [isCustom, setIsCustom] = useState(false); // The parent page path used when publishing articles as child Pages. const [path, setPath] = useState(''); const [selectedTerms, setSelectedTerms] = useState>( {} ); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [saved, setSaved] = useState(false); const [error, setError] = useState(null); const applyConfig = (data: ArticlePublishingConfig): void => { setConfig(data); // A configured parent path means "publish as child Pages under it". const usingPath: boolean = Boolean(data.parent_path); setIsCustom(usingPath); setPostType(usingPath ? CUSTOM_VALUE : data.post_type); setPath(data.parent_path || ''); setSelectedTerms(data.selected_terms || {}); }; useEffect(() => { getArticlePublishing() .then(applyConfig) .catch((err: Error) => setError(err?.message || 'Failed to load settings') ) .finally(() => setIsLoading(false)); }, []); useEffect(() => { if (error || saved) { const timerId: ReturnType = setTimeout(() => { setError(null); setSaved(false); }, 4000); return () => clearTimeout(timerId); } return undefined; }, [error, saved]); const handleSelectPostType = (value: string): void => { if (value === CUSTOM_VALUE) { setIsCustom(true); setPostType(CUSTOM_VALUE); return; } setIsCustom(false); setPostType(value); // Refresh the taxonomies (and their terms) for the newly chosen type. getArticlePublishing(value) .then(data => { setConfig(data); setSelectedTerms(data.selected_terms || {}); }) .catch((err: Error) => setError(err?.message || 'Failed to load taxonomies') ); }; const toggleTerm = (taxonomy: string, termId: number): void => { setSelectedTerms(previous => { const current: number[] = previous[taxonomy] || []; const next: number[] = current.includes(termId) ? current.filter(id => id !== termId) : [...current, termId]; return { ...previous, [taxonomy]: next }; }); }; const handleSave = async (): Promise => { if (isCustom && !path.trim()) { setError('Type a path (e.g. informatii) for the pages.'); return; } try { setIsSaving(true); const response = await saveArticlePublishing( isCustom ? { post_type: 'page', parent_path: path.trim(), label: path.trim(), terms: selectedTerms, } : { post_type: postType, terms: selectedTerms } ); const refreshed = await getArticlePublishing(response.post_type); applyConfig(refreshed); setSaved(true); } catch (err: any) { setError(err?.message || 'Could not save. Please try again.'); } finally { setIsSaving(false); } }; if (isLoading) { return (
Loading publishing settings...
); } return (

Article Publishing

Choose where Recomaze publishes AI-generated articles and which categories they go into. Keep your refined posts in /blog/ and send automated articles to their own post type.

Publishing settings saved
{isCustom && (
setPath(e.target.value)} className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-gray-500 focus:outline-none" />

Articles become Pages under{' '} /{(path || 'guides').replace(/^\/+|\/+$/g, '')}/article-name . The parent page is created automatically.

)} {!isCustom && (config?.taxonomies?.length || 0) > 0 && (

Default categories

{(config?.taxonomies || []).map(taxonomy => (

{taxonomy.label}

{taxonomy.terms.length === 0 ? (

No {taxonomy.label.toLowerCase()} yet.

) : (
{taxonomy.terms.map(term => { const checked: boolean = ( selectedTerms[taxonomy.slug] || [] ).includes(term.id); return ( ); })}
)}
))}
)}
Current:{' '} {config?.post_type}
{error && (
)}
); }; export default ArticlePublishing;