diff --git a/LLM_MCP_GUIDE.md b/LLM_MCP_GUIDE.md index d9ecca1..2e02b95 100644 --- a/LLM_MCP_GUIDE.md +++ b/LLM_MCP_GUIDE.md @@ -110,6 +110,9 @@ Use one string only for URLs, slugs, IDs, and booleans/numbers as appropriate. - `ARTICLE_FACTS`: 1–8 concise facts or key takeaways - `ARTICLE_VIDEO`: HTTPS YouTube, Vimeo, or direct video URL; embed HTML is not accepted - `ARTICLE_REFERENCES`: 1–20 references with optional verified HTTPS URLs and notes +- `ARTICLE_TABLE`: responsive technical comparison with 2–6 columns +- `ARTICLE_DOWNLOAD`: 1–10 verified HTTPS documents +- `ARTICLE_CTA`: one primary action and an optional secondary action - Reuse `QUOTE_CALLOUT`, `ACCORDION`, and `PRODUCT_CAROUSEL` instead of creating duplicate article-only variants ### `SCENT_NOTES_BREAKDOWN` diff --git a/apps/admin/src/components/page-builder/ArticleContentForms.tsx b/apps/admin/src/components/page-builder/ArticleContentForms.tsx index 5f11189..e72c5c1 100644 --- a/apps/admin/src/components/page-builder/ArticleContentForms.tsx +++ b/apps/admin/src/components/page-builder/ArticleContentForms.tsx @@ -196,3 +196,87 @@ export function ArticleReferencesForm({ editingSection, setEditingSection, editi ); } + +export function ArticleTableForm({ editingSection, setEditingSection, editingLang, getLocalizedValue, updateLocalizedValue, updatePayloadValue }: BaseProps) { + const payload = editingSection.payload || {}; + const columns = Array.isArray(payload.columns) ? payload.columns as Record[] : []; + const rows = Array.isArray(payload.rows) ? payload.rows as Record[] : []; + const updateColumn = (index: number, value: string) => { + const next = [...columns]; + next[index] = { ...(next[index] || {}), [editingLang]: value }; + updatePayloadValue('columns', next); + }; + const setColumnCount = (count: number) => { + const nextColumns = Array.from({ length: count }, (_, index) => columns[index] || { [editingLang]: `Column ${index + 1}` }); + const nextRows = rows.map((row) => ({ + ...row, + cells: Array.from({ length: count }, (_, index) => (Array.isArray(row.cells) ? row.cells[index] : undefined) || { [editingLang]: '' }), + })); + setEditingSection((previous) => previous ? { ...previous, payload: { ...(previous.payload || {}), columns: nextColumns, rows: nextRows } } : null); + }; + return ( +
+ + +
+ {columns.map((column, index) => updateColumn(index, event.target.value)} className="admin-input w-full" placeholder={`Column ${index + 1}`} />)} +
+ {rows.map((row, rowIndex) => { + const cells = Array.isArray(row.cells) ? row.cells as Record[] : []; + return ( +
+ updateItemLocalized(setEditingSection, 'rows', rowIndex, 'label', editingLang, event.target.value)} className="admin-input w-full" placeholder="Row label" /> +
+ {columns.map((_, cellIndex) => ( + updateArrayItem(setEditingSection, 'rows', rowIndex, (record) => { + const nextCells = Array.isArray(record.cells) ? [...record.cells] as Record[] : []; + nextCells[cellIndex] = { ...(nextCells[cellIndex] || {}), [editingLang]: event.target.value }; + return { ...record, cells: nextCells }; + })} className="admin-input w-full" placeholder={`Cell ${cellIndex + 1}`} /> + ))} +
+ +
+ ); + })} + + +
+ ); +} + +export function ArticleDownloadForm({ editingSection, setEditingSection, editingLang, getLocalizedValue, updateLocalizedValue, updatePayloadValue }: BaseProps) { + const payload = editingSection.payload || {}; + const files = Array.isArray(payload.files) ? payload.files as Record[] : []; + return ( +
+ +