Add website schema settings, SEO meta overrides, and resilient bulk SMS.

Business admins can configure site-wide Schema.org and optional per-item SEO title/description; mass SMS continues on failures with an end report.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-09 23:30:23 +03:30
co-authored by Cursor
parent 15aad9f9d8
commit c2ede07e73
37 changed files with 1217 additions and 89 deletions
@@ -4,6 +4,7 @@ import { SearchableSelect } from '../components/SearchableSelect'
import { ImageCropper } from '../components/ImageCropper'
import { TagInput } from '../components/TagInput'
import { RichTextEditor } from '../components/RichTextEditor'
import { SeoMetaFields } from '../components/SeoMetaFields'
import { Breadcrumbs } from '../components/Breadcrumbs'
import { useToast } from '../context/ToastContext'
import { useT } from '../i18n/useT'
@@ -26,6 +27,7 @@ import type { Category } from '../types/category'
import type { VideoProvider } from '../types/video'
import { VIDEO_PROVIDER_OPTIONS } from '../types/video'
import { flattenCategories } from '../utils/categories'
import { seoMetaPayload } from '../utils/seoMeta'
import pageStyles from '../components/PageContent.module.css'
import styles from './AddNewProductPage.module.css'
@@ -46,6 +48,8 @@ export function AddNewVideoPage() {
const [embedCode, setEmbedCode] = useState('')
const [coverImage, setCoverImage] = useState<string | null>(null)
const [tags, setTags] = useState<string[]>([])
const [seoMetaTitle, setSeoMetaTitle] = useState('')
const [seoMetaDescription, setSeoMetaDescription] = useState('')
const [featuredMediaId, setFeaturedMediaId] = useState<string | null>(null)
const [loaded, setLoaded] = useState(!isEdit)
const [isSubmitting, setIsSubmitting] = useState(false)
@@ -93,6 +97,8 @@ export function AddNewVideoPage() {
setEmbedCode(form.embedCode)
setCoverImage(form.coverImage)
setTags(form.tags)
setSeoMetaTitle(form.seoMetaTitle)
setSeoMetaDescription(form.seoMetaDescription)
setFeaturedMediaId(form.featuredMediaId)
setLoaded(true)
} catch (err) {
@@ -130,6 +136,7 @@ export function AddNewVideoPage() {
categoryId: categoryId || undefined,
featuredMediaId: nextFeaturedMediaId ?? undefined,
tags,
...seoMetaPayload(seoMetaTitle, seoMetaDescription),
status: 'published' as const,
}
@@ -322,6 +329,19 @@ export function AddNewVideoPage() {
<label>{t('video.form.tags')}</label>
<TagInput tags={tags} onChange={setTags} />
</div>
<SeoMetaFields
seoMetaTitle={seoMetaTitle}
seoMetaDescription={seoMetaDescription}
onTitleChange={setSeoMetaTitle}
onDescriptionChange={setSeoMetaDescription}
disabled={isSubmitting}
hint={t('seo.meta.hint')}
titleLabel={t('seo.meta.title')}
descriptionLabel={t('seo.meta.description')}
titlePlaceholder={t('seo.meta.titlePlaceholder')}
descriptionPlaceholder={t('seo.meta.descriptionPlaceholder')}
/>
</div>
<div className={styles.actions}>