@canvas-digital/blog-sdk 2.0.2 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blog-client.iife.js +1 -1
- package/dist/components/admin/AdminPostDetail.js +5 -5
- package/dist/components/admin/BrandSettings.js +5 -5
- package/dist/components/admin/BrandSwitcher.js +1 -1
- package/dist/components/admin/Chips.js +5 -5
- package/dist/components/admin/CommentsManager.js +6 -6
- package/dist/components/admin/Dashboard.js +1 -1
- package/dist/components/admin/MediaLibrary.js +5 -5
- package/dist/components/admin/MediaPicker.js +4 -4
- package/dist/components/admin/PostList.d.ts.map +1 -1
- package/dist/components/admin/PostList.js +13 -4
- package/dist/components/admin/PostsTable.d.ts +6 -1
- package/dist/components/admin/PostsTable.d.ts.map +1 -1
- package/dist/components/admin/PostsTable.js +15 -9
- package/dist/components/admin/Shell.js +3 -3
- package/dist/components/admin/TermManager.js +4 -4
- package/dist/components/admin/UsersManager.js +3 -3
- package/dist/components/admin/charts.js +7 -7
- package/dist/components/admin/editor/PostEditor.js +16 -16
- package/dist/components/admin/editor/RichTextEditor.js +8 -8
- package/dist/components/admin/editor/YoastCollapsible.js +2 -2
- package/dist/components/admin/editor/YoastInsightsPanel.js +1 -1
- package/dist/components/admin/editor/YoastInternalLinksPanel.js +1 -1
- package/dist/components/admin/editor/YoastSeoAnalysisPanel.js +6 -6
- package/dist/components/admin/editor/YoastSeoMetabox.js +8 -8
- package/dist/components/admin/editor/YoastSnippetField.js +2 -2
- package/dist/components/admin/internal/States.js +3 -3
- package/dist/components/admin/mock/store.d.ts.map +1 -1
- package/dist/components/admin/mock/store.js +3 -2
- package/dist/components/admin/seo/Ga4Settings.js +6 -6
- package/dist/components/admin/seo/RedirectAnalyticsDashboard.js +1 -1
- package/dist/components/admin/seo/RedirectDetail.js +6 -6
- package/dist/components/admin/seo/RedirectForm.js +1 -1
- package/dist/components/admin/seo/RedirectList.js +4 -4
- package/dist/components/admin/seo/RobotsEditor.js +1 -1
- package/dist/components/admin/seo/shared.js +2 -2
- package/dist/components/admin/tools/ToolsHub.js +4 -4
- package/dist/components/user/BlogCard.js +3 -3
- package/dist/components/user/BlogCategories.js +1 -1
- package/dist/components/user/BlogDetail.js +1 -1
- package/dist/components/user/BlogList.js +1 -1
- package/dist/components/user/BlogSearch.js +2 -2
- package/dist/components/user/BlogTags.js +1 -1
- package/dist/components/user/FeaturedBlogs.js +1 -1
- package/dist/components/user/RelatedBlogs.js +1 -1
- package/dist/components/user/SiteMediaHero.js +1 -1
- package/dist/components/user/detail/ArticleDetailHero.js +5 -5
- package/dist/components/user/detail/ArticleRailDetails.js +2 -2
- package/dist/components/user/detail/CommentSection.js +2 -2
- package/dist/components/user/detail/HeroFadeSlide.js +1 -1
- package/dist/components/user/detail/ReadingProgress.js +1 -1
- package/dist/components/user/detail/ShareRow.js +1 -1
- package/dist/components/user/internal/States.js +4 -4
- package/dist/lib/lexical-doc.js +1 -1
- package/dist/pages/admin/BlogAdmin.d.ts.map +1 -1
- package/dist/pages/admin/BlogAdmin.js +23 -11
- package/dist/services/admin-sdk-client.d.ts +9 -0
- package/dist/services/admin-sdk-client.d.ts.map +1 -1
- package/dist/services/admin-sdk-client.js +17 -0
- package/dist/style.css +1023 -1016
- package/package.json +2 -1
|
@@ -14,17 +14,17 @@ const toLocalInput = (iso) => {
|
|
|
14
14
|
};
|
|
15
15
|
const StateChip = ({ row }) => {
|
|
16
16
|
if (row.trashed)
|
|
17
|
-
return _jsx("span", { className: "
|
|
17
|
+
return _jsx("span", { className: "blog_chip blog_chip-blocked", children: "Trashed" });
|
|
18
18
|
if (row.status === "published")
|
|
19
|
-
return _jsx("span", { className: "
|
|
19
|
+
return _jsx("span", { className: "blog_chip blog_chip-live", children: "Live" });
|
|
20
20
|
if (row.scheduledFor && row.status === "draft") {
|
|
21
|
-
return _jsx("span", { className: "
|
|
21
|
+
return _jsx("span", { className: "blog_chip blog_chip-approved", children: "Scheduled" });
|
|
22
22
|
}
|
|
23
23
|
const rs = row.reviewState;
|
|
24
|
-
const cls = rs === "in-review" ? "
|
|
25
|
-
return _jsx("span", { className: `
|
|
24
|
+
const cls = rs === "in-review" ? "blog_chip-review" : rs === "approved" ? "blog_chip-approved" : "blog_chip-writing";
|
|
25
|
+
return _jsx("span", { className: `blog_chip ${cls}`, children: REVIEW_LABELS[rs] ?? "Draft" });
|
|
26
26
|
};
|
|
27
|
-
export const PostsTable = ({ rows, categories, state, showBrand, onEdit, onView }) => {
|
|
27
|
+
export const PostsTable = ({ rows, minRows = 0, totalCount, page, pageSize, categories, state, showBrand, onEdit, onView, }) => {
|
|
28
28
|
const store = useAdminMock();
|
|
29
29
|
const [pending, start] = useTransition();
|
|
30
30
|
const [selected, setSelected] = useState([]);
|
|
@@ -86,7 +86,11 @@ export const PostsTable = ({ rows, categories, state, showBrand, onEdit, onView
|
|
|
86
86
|
}
|
|
87
87
|
run(() => store.bulkPostAction(selected, bulk), `${selected.length} post(s) updated.`);
|
|
88
88
|
};
|
|
89
|
-
return (_jsxs("div", { className: "
|
|
89
|
+
return (_jsxs("div", { className: "blog_list", children: [_jsxs("div", { className: "blog_list-bar", children: [_jsxs("div", { className: "blog_bulk", children: [_jsxs("select", { className: "blog_fld blog_fld-sm", value: bulk, onChange: (e) => setBulk(e.target.value), "aria-label": "Bulk action", children: [_jsx("option", { value: "", children: "Bulk actions\u2026" }), isTrash ? (_jsxs(_Fragment, { children: [_jsx("option", { value: "restore", children: "Restore" }), _jsx("option", { value: "delete", children: "Delete permanently" })] })) : (_jsxs(_Fragment, { children: [_jsx("option", { value: "submit", children: "Submit for review" }), _jsx("option", { value: "approve", children: "Approve" }), _jsx("option", { value: "send-back", children: "Send back to writing" }), _jsx("option", { value: "publish", children: "Publish" }), _jsx("option", { value: "draft", children: "Switch to draft" }), _jsx("option", { value: "trash", children: "Move to trash" })] }))] }), _jsx("button", { type: "button", className: "blog_btn", disabled: !bulk || !selected.length || pending, onClick: applyBulk, children: "Apply" }), _jsx("span", { className: "blog_note", children: selected.length
|
|
90
|
+
? `${selected.length} selected`
|
|
91
|
+
: totalCount != null && page != null && pageSize != null
|
|
92
|
+
? `${rows.length} on this page · ${totalCount} total`
|
|
93
|
+
: `${rows.length} shown` })] }), pending ? _jsx("span", { className: "blog_note", children: "Working\u2026" }) : null] }), error ? (_jsx("p", { className: "blog_form-error", role: "alert", children: error })) : null, flash ? (_jsx("p", { className: "blog_form-ok", role: "status", children: flash })) : null, _jsx("div", { className: "blog_tw", children: _jsxs("table", { className: "blog_tbl blog_tbl-list", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { className: "blog_col-check", children: _jsx("input", { type: "checkbox", checked: allChecked, "aria-label": "Select all posts", onChange: (e) => setSelected(e.target.checked ? rows.map((r) => r.id) : []) }) }), _jsx("th", { children: "Title" }), showBrand ? _jsx("th", { className: "blog_col-narrow", children: "Brand" }) : null, _jsx("th", { className: "blog_col-narrow", children: "Byline" }), _jsx("th", { className: "blog_col-narrow", children: "Category" }), _jsx("th", { className: "blog_col-narrow", children: "Date" }), _jsx("th", { className: "blog_col-narrow blog_num", children: "Views 30d" }), _jsx("th", { className: "blog_col-narrow", children: "Status" })] }) }), _jsxs("tbody", { children: [rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: showBrand ? 8 : 7, children: _jsx("p", { className: "blog_d-empty", children: "No posts match this view." }) }) })) : null, rows.map((row) => (_jsxs(Fragment, { children: [_jsxs("tr", { className: selected.includes(row.id) ? "blog_is-selected" : undefined, children: [_jsx("td", { className: "blog_col-check", children: _jsx("input", { type: "checkbox", checked: selected.includes(row.id), "aria-label": `Select ${row.title}`, onChange: (e) => setSelected((list) => e.target.checked ? [...list, row.id] : list.filter((id) => id !== row.id)) }) }), _jsxs("td", { className: "blog_col-title", children: [_jsx("button", { type: "button", className: "blog_row-title", onClick: () => onEdit(row.id), children: row.title || "(no title)" }), row.blockers.length && row.status !== "published" && !row.trashed ? (_jsx("ul", { className: "blog_blockers", children: row.blockers.map((b) => (_jsxs("li", { children: ["needs ", b] }, b))) })) : null, _jsx("div", { className: "blog_row-actions", children: isTrash ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => run(() => store.bulkPostAction([row.id], "restore"), "Restored."), children: "Restore" }), _jsx("button", { type: "button", className: "blog_danger", onClick: () => {
|
|
90
94
|
if (window.confirm("Delete this post permanently? This cannot be undone.")) {
|
|
91
95
|
run(() => store.bulkPostAction([row.id], "delete"), "Deleted.");
|
|
92
96
|
}
|
|
@@ -94,7 +98,7 @@ export const PostsTable = ({ rows, categories, state, showBrand, onEdit, onView
|
|
|
94
98
|
setScheduling(row.id);
|
|
95
99
|
setWhen(toLocalInput(row.scheduledFor) ||
|
|
96
100
|
toLocalInput(new Date(Date.now() + 3600000).toISOString()));
|
|
97
|
-
}, children: "Schedule" })] })) : null, row.status === "published" && row.canPublish ? (_jsx("button", { type: "button", onClick: () => run(() => store.unpublish(row.id), "Switched to draft."), children: "Unpublish" })) : null, _jsx("button", { type: "button", onClick: () => run(() => store.duplicatePost(row.id), "Copied as a new draft."), children: "Duplicate" }), _jsx("button", { type: "button", className: "
|
|
101
|
+
}, children: "Schedule" })] })) : null, row.status === "published" && row.canPublish ? (_jsx("button", { type: "button", onClick: () => run(() => store.unpublish(row.id), "Switched to draft."), children: "Unpublish" })) : null, _jsx("button", { type: "button", onClick: () => run(() => store.duplicatePost(row.id), "Copied as a new draft."), children: "Duplicate" }), _jsx("button", { type: "button", className: "blog_danger", onClick: () => run(() => store.bulkPostAction([row.id], "trash"), "Moved to trash."), children: "Trash" }), row.status === "published" && onView ? (_jsx("button", { type: "button", onClick: () => onView(row.id), children: "View" })) : null] })) }), scheduling === row.id ? (_jsxs("div", { className: "blog_inline-form", children: [_jsx("input", { type: "datetime-local", className: "blog_fld blog_fld-sm", value: when, onChange: (e) => setWhen(e.target.value), "aria-label": "Publish date and time" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-primary blog_btn-tiny", onClick: () => run(() => store.schedulePost(row.id, new Date(when).toISOString()), "Scheduled."), children: "Confirm" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", onClick: () => setScheduling(null), children: "Cancel" })] })) : null] }), showBrand ? _jsx("td", { className: "blog_col-narrow blog_muted", children: row.brandName ?? "—" }) : null, _jsx("td", { className: "blog_col-narrow blog_muted", children: row.authorNames.join(", ") || "—" }), _jsx("td", { className: "blog_col-narrow blog_muted", children: row.categoryNames.join(", ") || "—" }), _jsx("td", { className: "blog_col-narrow blog_muted", children: row.status === "published" ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_cell-lbl", children: "Published" }), fmtDate(row.publishedAt)] })) : row.scheduledFor ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_cell-lbl", children: "Scheduled" }), fmtDateTime(row.scheduledFor)] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_cell-lbl", children: "Edited" }), fmtDate(row.updatedAt)] })) }), _jsx("td", { className: "blog_col-narrow blog_num", children: row.views == null ? "—" : row.views }), _jsx("td", { className: "blog_col-narrow", children: _jsx(StateChip, { row: row }) })] }), editing === row.id && editRow ? (_jsx("tr", { className: "blog_quick-row", children: _jsx("td", { colSpan: showBrand ? 8 : 7, children: _jsxs("div", { className: "blog_quick", children: [_jsx("p", { className: "blog_quick-head", children: "Quick edit \u2014 metadata only, the body stays untouched" }), _jsxs("div", { className: "blog_quick-grid", children: [_jsxs("label", { children: [_jsx("span", { children: "Title" }), _jsx("input", { className: "blog_fld", value: draft.title, onChange: (e) => setDraft((d) => ({ ...d, title: e.target.value })) })] }), _jsxs("label", { children: [_jsx("span", { children: "Slug" }), _jsx("input", { className: "blog_fld blog_mono", value: draft.slug, onChange: (e) => setDraft((d) => ({ ...d, slug: e.target.value })) })] }), _jsxs("div", { className: "blog_span-2", children: [_jsx("span", { className: "blog_fld-lbl", style: { display: "block", marginBottom: 6 }, children: "Categories" }), _jsx("div", { className: "blog_pick-list blog_pick-list-compact", children: categories.length === 0 ? (_jsx("p", { className: "blog_note", children: "No categories." })) : (categories.map((c) => (_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "checkbox", checked: draft.category.includes(c.id), onChange: () => toggleDraftCategory(c.id) }), c.name] }, c.id)))) })] }), _jsxs("label", { children: [_jsx("span", { children: "Published date" }), _jsx("input", { type: "datetime-local", className: "blog_fld", value: draft.date, onChange: (e) => setDraft((d) => ({ ...d, date: e.target.value })) })] }), _jsxs("label", { children: [_jsx("span", { children: "Status" }), _jsxs("select", { className: "blog_fld", value: draft.status, disabled: !row.canPublish, onChange: (e) => setDraft((d) => ({ ...d, status: e.target.value })), children: [_jsx("option", { value: "draft", children: "Draft" }), _jsx("option", { value: "published", children: "Published" })] })] })] }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-primary", disabled: pending, onClick: () => run(() => store.quickEditPost({
|
|
98
102
|
id: row.id,
|
|
99
103
|
title: draft.title,
|
|
100
104
|
slug: draft.slug,
|
|
@@ -102,5 +106,7 @@ export const PostsTable = ({ rows, categories, state, showBrand, onEdit, onView
|
|
|
102
106
|
publishedAt: draft.date ? new Date(draft.date).toISOString() : null,
|
|
103
107
|
scheduledFor: row.scheduledFor,
|
|
104
108
|
status: draft.status,
|
|
105
|
-
}), "Saved."), children: "Save" }), _jsx("button", { type: "button", className: "
|
|
109
|
+
}), "Saved."), children: "Save" }), _jsx("button", { type: "button", className: "blog_btn", onClick: () => setEditing(null), children: "Cancel" }), _jsx("span", { className: "blog_note", children: "Changing the slug of a live post writes a 301 from the old URL automatically." })] })] }) }) })) : null] }, row.id))), minRows > 0 && rows.length > 0 && rows.length < minRows
|
|
110
|
+
? Array.from({ length: minRows - rows.length }).map((_, i) => (_jsx("tr", { className: "blog_tbl-row-pad", "aria-hidden": "true", children: _jsx("td", { colSpan: showBrand ? 8 : 7 }) }, `pad-${i}`)))
|
|
111
|
+
: null] })] }) })] }));
|
|
106
112
|
};
|
|
@@ -92,8 +92,8 @@ const NAV = [
|
|
|
92
92
|
},
|
|
93
93
|
];
|
|
94
94
|
export const Shell = ({ children, active, onNavigate, title, subtitle, actions, wide, branding, className, userName = "Platform Admin", userRole = "Platform admin", onProfile, }) => {
|
|
95
|
-
return (_jsxs("div", { className: cx("
|
|
95
|
+
return (_jsxs("div", { className: cx("blog_shell", className), children: [_jsxs("aside", { className: "blog_side", children: [_jsxs("div", { className: "blog_side-brand", children: [_jsx("div", { className: "blog_side-mark", "aria-hidden": "true", style: branding?.logo ? { background: 'transparent', padding: 0 } : undefined, children: branding?.logo ? _jsx("img", { src: branding.logo, alt: branding.name ?? "Brand", style: { height: 24 } }) : (branding?.name || "B")[0] }), _jsxs("div", { children: [_jsx("div", { className: "blog_side-name", children: branding?.name || "Blog Portal" }), _jsx("div", { className: "blog_side-role", children: branding?.subtitle || "2 brands" })] })] }), _jsxs("button", { type: "button", className: "blog_side-new", onClick: () => onNavigate("posts-new"), children: [_jsx("span", { "aria-hidden": "true", children: "\uFF0B" }), "New post"] }), _jsxs("div", { className: "blog_side-scroll", children: [NAV.map((section, idx) => (_jsxs("div", { className: "blog_side-group", children: [_jsx("p", { className: "blog_side-sect", children: section.title }), _jsx("nav", { className: "blog_side-nav", children: section.items.map((item) => {
|
|
96
96
|
const isActive = active === item.key || item.children?.some(c => c.key === active);
|
|
97
|
-
return (_jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => onNavigate(item.key), "aria-current": isActive ? "page" : undefined, children: [item.icon && _jsx(Icon, { path: ICONS[item.icon] }), item.label] }), item.children && isActive && (_jsx("div", { className: "
|
|
98
|
-
}) })] }, idx))), _jsx("div", { className: "
|
|
97
|
+
return (_jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => onNavigate(item.key), "aria-current": isActive ? "page" : undefined, children: [item.icon && _jsx(Icon, { path: ICONS[item.icon] }), item.label] }), item.children && isActive && (_jsx("div", { className: "blog_side-sub", children: item.children.map((child) => (_jsx("button", { type: "button", onClick: () => onNavigate(child.key), "aria-current": active === child.key ? "page" : undefined, style: { cursor: "pointer", width: "100%", textAlign: "left", background: "none", border: "none", padding: "4px 8px", color: active === child.key ? "var(--d-surface)" : "var(--d-side-ink-2)" }, children: child.label }, child.key))) }))] }, item.key));
|
|
98
|
+
}) })] }, idx))), _jsx("div", { className: "blog_side-group", children: _jsx("nav", { className: "blog_side-nav", children: _jsx("button", { type: "button", children: "Log out" }) }) })] }), _jsx("div", { className: "blog_side-foot", children: _jsxs("button", { type: "button", className: "blog_side-user-card", style: { width: "100%", background: "none", border: "none", textAlign: "left", cursor: "pointer" }, onClick: () => (onProfile ? onProfile() : onNavigate("profile")), children: [_jsx("div", { className: "blog_side-user-avatar-fallback blog_side-user-avatar", children: (userName || "P").slice(0, 1).toUpperCase() }), _jsxs("div", { className: "blog_side-user-meta", children: [_jsx("div", { className: "blog_side-user-name", style: { color: "#fff" }, children: userName }), _jsx("div", { className: "blog_side-user-role", children: userRole })] })] }) })] }), _jsxs("main", { className: "blog_main", children: [_jsxs("header", { className: "blog_top", children: [_jsxs("div", { className: "blog_top-title", children: [_jsx("h1", { children: title }), subtitle ? _jsx("p", { className: "blog_sub", children: subtitle }) : null] }), _jsx("div", { className: "blog_top-actions", children: actions })] }), _jsx("div", { className: wide ? 'blog_body blog_body-wide' : 'blog_body', children: children })] })] }));
|
|
99
99
|
};
|
|
@@ -74,12 +74,12 @@ export function TermManager({ collection, onOpenPosts, }) {
|
|
|
74
74
|
bio: row.bio,
|
|
75
75
|
});
|
|
76
76
|
};
|
|
77
|
-
return (_jsxs("div", { className: "
|
|
77
|
+
return (_jsxs("div", { className: "blog_split", children: [_jsxs("section", { className: "blog_panel blog_panel-form", children: [_jsx("div", { className: "blog_panel-head", children: _jsxs("h2", { children: ["Add ", labels.one] }) }), _jsxs("div", { className: "blog_panel-body blog_stack", children: [_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Name" }), _jsx("input", { className: "blog_fld", value: fresh.name, onChange: (e) => setFresh((f) => ({ ...f, name: e.target.value })), onKeyDown: (e) => {
|
|
78
78
|
if (e.key === "Enter") {
|
|
79
79
|
e.preventDefault();
|
|
80
80
|
create();
|
|
81
81
|
}
|
|
82
|
-
} })] }), _jsxs("label", { className: "
|
|
82
|
+
} })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsxs("span", { className: "blog_fld-lbl", children: ["Slug ", _jsx("em", { children: "optional \u2014 derived from the name" })] }), _jsx("input", { className: "blog_fld blog_mono", value: fresh.slug, onChange: (e) => setFresh((f) => ({ ...f, slug: e.target.value })) })] }), isCategories ? (_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Description" }), _jsx("textarea", { className: "blog_fld", rows: 3, value: fresh.description, onChange: (e) => setFresh((f) => ({ ...f, description: e.target.value })) })] })) : null, isAuthors ? (_jsxs(_Fragment, { children: [_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Job title" }), _jsx("input", { className: "blog_fld", value: fresh.jobTitle, onChange: (e) => setFresh((f) => ({ ...f, jobTitle: e.target.value })) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Bio" }), _jsx("textarea", { className: "blog_fld", rows: 3, value: fresh.bio, onChange: (e) => setFresh((f) => ({ ...f, bio: e.target.value })) })] })] })) : null, _jsxs("button", { type: "button", className: "blog_btn blog_btn-primary", disabled: pending || !fresh.name.trim(), onClick: create, children: ["Add ", labels.one] }), _jsx("p", { className: "blog_note", children: labels.blurb }), !store.activeBrandId ? _jsx("p", { className: "blog_note", children: "Currently viewing all brands \u2014 choose one to add." }) : null] })] }), _jsxs("section", { className: "blog_panel", children: [_jsxs("div", { className: "blog_panel-head", children: [_jsxs("h2", { children: [labels.many, " ", _jsx("span", { className: "blog_hint", children: tenantName })] }), _jsx("span", { className: "blog_hint", children: rows.length })] }), error ? (_jsx("p", { className: "blog_form-error", role: "alert", children: error })) : null, flash ? (_jsx("p", { className: "blog_form-ok", role: "status", children: flash })) : null, _jsx("div", { className: "blog_tw", children: _jsxs("table", { className: "blog_tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Name" }), _jsx("th", { children: "Slug" }), _jsx("th", { className: "blog_num", children: "Posts" }), _jsx("th", {})] }) }), _jsxs("tbody", { children: [rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: 4, children: _jsx("p", { className: "blog_d-empty", children: "Nothing here yet." }) }) })) : null, rows.map((row) => editing === row.id ? (_jsx("tr", { className: "blog_quick-row", children: _jsx("td", { colSpan: 4, children: _jsxs("div", { className: "blog_quick", children: [_jsxs("div", { className: "blog_quick-grid", children: [_jsxs("label", { children: [_jsx("span", { children: "Name" }), _jsx("input", { className: "blog_fld", value: draft.name, onChange: (e) => setDraft((d) => ({ ...d, name: e.target.value })) })] }), _jsxs("label", { children: [_jsx("span", { children: "Slug" }), _jsx("input", { className: "blog_fld blog_mono", value: draft.slug, onChange: (e) => setDraft((d) => ({ ...d, slug: e.target.value })) })] }), isCategories ? (_jsxs("label", { className: "blog_span-2", children: [_jsx("span", { children: "Description" }), _jsx("textarea", { className: "blog_fld", rows: 2, value: draft.description, onChange: (e) => setDraft((d) => ({ ...d, description: e.target.value })) })] })) : null, isAuthors ? (_jsxs(_Fragment, { children: [_jsxs("label", { children: [_jsx("span", { children: "Job title" }), _jsx("input", { className: "blog_fld", value: draft.jobTitle, onChange: (e) => setDraft((d) => ({ ...d, jobTitle: e.target.value })) })] }), _jsxs("label", { className: "blog_span-2", children: [_jsx("span", { children: "Bio" }), _jsx("textarea", { className: "blog_fld", rows: 2, value: draft.bio, onChange: (e) => setDraft((d) => ({ ...d, bio: e.target.value })) })] })] })) : null] }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-primary", disabled: pending, onClick: () => run(() => store.saveTerm({
|
|
83
83
|
collection,
|
|
84
84
|
id: row.id,
|
|
85
85
|
brandId: row.brandId,
|
|
@@ -88,11 +88,11 @@ export function TermManager({ collection, onOpenPosts, }) {
|
|
|
88
88
|
description: draft.description,
|
|
89
89
|
jobTitle: draft.jobTitle,
|
|
90
90
|
bio: draft.bio,
|
|
91
|
-
}), "Saved."), children: "Save" }), _jsx("button", { type: "button", className: "
|
|
91
|
+
}), "Saved."), children: "Save" }), _jsx("button", { type: "button", className: "blog_btn", onClick: () => setEditing(null), children: "Cancel" })] })] }) }) }, row.id)) : (_jsxs("tr", { children: [_jsxs("td", { children: [_jsx("span", { className: "blog_row-title", children: row.name }), row.jobTitle ? _jsx("span", { className: "blog_cell-sub", children: row.jobTitle }) : null, row.description ? _jsx("span", { className: "blog_cell-sub", children: row.description }) : null, _jsxs("div", { className: "blog_row-actions", children: [_jsx("button", { type: "button", onClick: () => startEdit(row), children: "Edit" }), _jsx("button", { type: "button", className: "blog_danger", onClick: () => {
|
|
92
92
|
if (window.confirm(`Delete “${row.name}”?`)) {
|
|
93
93
|
run(() => store.deleteTerm(collection, row.id), `${row.name} deleted.`);
|
|
94
94
|
}
|
|
95
|
-
}, children: "Delete" })] })] }), _jsx("td", { className: "
|
|
95
|
+
}, children: "Delete" })] })] }), _jsx("td", { className: "blog_mono blog_muted", children: row.slug }), _jsx("td", { className: "blog_num", children: row.count ? (_jsx("button", { type: "button", className: "blog_btn blog_btn-ghost blog_btn-sm", onClick: () => onOpenPosts?.(row.id, row.name), children: row.count })) : (0) }), _jsx("td", {})] }, row.id)))] })] }) })] })] }));
|
|
96
96
|
}
|
|
97
97
|
export function CategoryManager() {
|
|
98
98
|
return _jsx(TermManager, { collection: "categories" });
|
|
@@ -89,8 +89,8 @@ export function UsersManager() {
|
|
|
89
89
|
: d.roles.filter((r) => r.tenant !== tenantId),
|
|
90
90
|
}));
|
|
91
91
|
};
|
|
92
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "
|
|
92
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "blog_filterbar", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-primary", onClick: create, children: "\uFF0B Add person" }), _jsxs("span", { className: "blog_note", children: [rows.length, " accounts"] })] }), error && !open ? (_jsx("p", { className: "blog_form-error", role: "alert", children: error })) : null, flash ? (_jsx("p", { className: "blog_form-ok", role: "status", children: flash })) : null, _jsx("section", { className: "blog_panel", children: _jsx("div", { className: "blog_tw", children: _jsxs("table", { className: "blog_tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Person" }), _jsx("th", { children: "Platform role" }), _jsx("th", { children: "Brand access" }), _jsx("th", {})] }) }), _jsx("tbody", { children: rows.map((row) => (_jsxs("tr", { children: [_jsxs("td", { children: [_jsxs("span", { className: "blog_row-title", children: [row.name, row.isSelf ? _jsx("span", { className: "blog_chip blog_chip-brand", children: "you" }) : null] }), _jsx("span", { className: "blog_cell-sub blog_mono", children: row.email }), _jsxs("div", { className: "blog_row-actions", children: [_jsx("button", { type: "button", onClick: () => edit(row), children: "Edit" }), row.isSelf ? null : (_jsx("button", { type: "button", className: "blog_danger", onClick: () => remove(row), children: "Delete" }))] })] }), _jsx("td", { className: "blog_muted", children: GLOBAL_LABEL[row.globalRole] }), _jsx("td", { children: row.roles.length === 0 ? (_jsx("span", { className: "blog_muted", children: "\u2014" })) : (_jsx("div", { className: "blog_chips", children: row.roles.map((r) => (_jsxs("span", { className: "blog_chip blog_chip-brand", children: [r.tenantName, ": ", r.role.replace("-", " ")] }, `${row.id}-${r.tenant}`))) })) }), _jsx("td", {})] }, row.id))) })] }) }) }), open ? (_jsx("div", { className: "blog_modal-backdrop", onMouseDown: () => setOpen(false), children: _jsxs("div", { className: "blog_modal", role: "dialog", "aria-modal": "true", "aria-label": "Account", onMouseDown: (e) => e.stopPropagation(), children: [_jsxs("header", { className: "blog_modal-head", children: [_jsx("h2", { children: draft.id ? "Edit account" : "Add person" }), _jsx("button", { type: "button", className: "blog_icon-btn", onClick: () => setOpen(false), "aria-label": "Close", children: "\u2715" })] }), _jsxs("div", { className: "blog_modal-body blog_stack", children: [error ? (_jsx("p", { className: "blog_form-error", role: "alert", children: error })) : null, _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Name" }), _jsx("input", { className: "blog_fld", value: draft.name, onChange: (e) => setDraft((d) => ({ ...d, name: e.target.value })) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Email" }), _jsx("input", { className: "blog_fld blog_mono", type: "email", value: draft.email, onChange: (e) => setDraft((d) => ({ ...d, email: e.target.value })) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsxs("span", { className: "blog_fld-lbl", children: [draft.id ? "New password" : "Starting password", " ", _jsx("em", { children: "at least 12 characters" })] }), _jsx("input", { className: "blog_fld", type: "password", autoComplete: "new-password", placeholder: draft.id ? "Leave blank to keep the current one" : "", value: draft.password, onChange: (e) => setDraft((d) => ({ ...d, password: e.target.value })) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Platform role" }), _jsxs("select", { className: "blog_fld", value: draft.globalRole, onChange: (e) => setDraft((d) => ({ ...d, globalRole: e.target.value })), children: [_jsx("option", { value: "none", children: "Brand roles only" }), _jsx("option", { value: "analyst", children: "Analyst \u2014 read-only across every brand" }), _jsx("option", { value: "platform-admin", children: "Platform admin \u2014 full control" })] })] }), _jsxs("div", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Brand access" }), _jsx("div", { className: "blog_role-grid", children: tenants.map((t) => {
|
|
93
93
|
const current = draft.roles.find((r) => r.tenant === t.id)?.role ?? "";
|
|
94
|
-
return (_jsxs("div", { className: "
|
|
95
|
-
}) })] })] }), _jsxs("footer", { className: "
|
|
94
|
+
return (_jsxs("div", { className: "blog_role-row", children: [_jsx("span", { children: t.name }), _jsxs("select", { className: "blog_fld blog_fld-sm", value: current, onChange: (e) => setRole(t.id, e.target.value), "aria-label": `Role in ${t.name}`, children: [_jsx("option", { value: "", children: "No access" }), TENANT_ROLES.map((role) => (_jsx("option", { value: role, children: role.replace("-", " ") }, role)))] }), _jsx("span", { className: "blog_note", children: current ? ROLE_HELP[current] : "" })] }, t.id));
|
|
95
|
+
}) })] })] }), _jsxs("footer", { className: "blog_modal-foot", children: [_jsx("span", { className: "blog_note", children: "Publishing rights come from the brand role, never from the platform role." }), _jsxs("div", { className: "blog_modal-foot-actions", children: [_jsx("button", { type: "button", className: "blog_btn", onClick: () => setOpen(false), children: "Cancel" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-primary", onClick: submit, disabled: pending, children: pending ? "Saving…" : draft.id ? "Save account" : "Create account" })] })] })] }) })) : null] }));
|
|
96
96
|
}
|
|
@@ -39,7 +39,7 @@ export const TrendArea = ({ data, height = 220 }) => {
|
|
|
39
39
|
setHover(i >= 0 && i < data.length ? i : null);
|
|
40
40
|
};
|
|
41
41
|
const active = hover != null ? data[hover] : null;
|
|
42
|
-
return (_jsxs("div", { className: "
|
|
42
|
+
return (_jsxs("div", { className: "blog_chart-holder", children: [_jsxs("svg", { viewBox: `0 0 ${W} ${H}`, className: "blog_chart-svg", role: "img", "aria-label": `Daily views over the last ${data.length} days`, onMouseMove: onMove, onMouseLeave: () => setHover(null), children: [_jsx("defs", { children: _jsxs("linearGradient", { id: gradId, x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: "var(--viz-1)", stopOpacity: "0.26" }), _jsx("stop", { offset: "100%", stopColor: "var(--viz-1)", stopOpacity: "0.02" })] }) }), ticks.map((t) => (_jsxs("g", { children: [_jsx("line", { x1: pad.l, x2: W - pad.r, y1: y(t), y2: y(t), stroke: "var(--viz-grid)", strokeWidth: "1" }), _jsx("text", { x: pad.l - 9, y: y(t) + 4, textAnchor: "end", className: "blog_axis-label", fill: "var(--viz-muted)", children: fmt.format(Math.round(t)) })] }, t))), _jsx("path", { d: area, fill: `url(#${gradId})` }), _jsx("path", { d: line, fill: "none", stroke: "var(--viz-1)", strokeWidth: "2", strokeLinejoin: "round" }), [0, Math.floor(data.length / 2), data.length - 1].map((i) => (_jsx("text", { x: x(i), y: H - 8, textAnchor: i === 0 ? 'start' : i === data.length - 1 ? 'end' : 'middle', className: "blog_axis-label", fill: "var(--viz-muted)", children: data[i] ? shortDate(data[i].date) : '' }, i))), active && hover != null ? (_jsxs("g", { children: [_jsx("line", { x1: x(hover), x2: x(hover), y1: pad.t, y2: pad.t + ih, stroke: "var(--viz-axis)", strokeWidth: "1" }), _jsx("circle", { cx: x(hover), cy: y(active.views), r: "5", fill: "var(--viz-1)", stroke: "var(--d-surface)", strokeWidth: "2" })] })) : null] }), active && hover != null ? (_jsxs("div", { className: "blog_chart-tip", style: { left: `${(x(hover) / W) * 100}%` }, role: "status", children: [_jsx("div", { className: "blog_tip-date", children: shortDate(active.date) }), _jsxs("div", { className: "blog_tip-row", children: [_jsx("span", { className: "blog_tip-swatch", style: { background: 'var(--viz-1)' } }), fmt.format(active.views), " views"] }), _jsxs("div", { className: "blog_tip-sub", children: [fmt.format(active.uniques), " unique"] })] })) : null] }));
|
|
43
43
|
};
|
|
44
44
|
/* ------------------------------------------------------------------ */
|
|
45
45
|
/* Sparkline for stat cards */
|
|
@@ -51,7 +51,7 @@ export const Sparkline = ({ data }) => {
|
|
|
51
51
|
const pts = data
|
|
52
52
|
.map((v, i) => `${(i / Math.max(1, data.length - 1)) * W},${H - (v / max) * (H - 4) - 2}`)
|
|
53
53
|
.join(' ');
|
|
54
|
-
return (_jsxs("svg", { viewBox: `0 0 ${W} ${H}`, className: "
|
|
54
|
+
return (_jsxs("svg", { viewBox: `0 0 ${W} ${H}`, className: "blog_spark", "aria-hidden": "true", children: [_jsx("polyline", { points: pts, fill: "none", stroke: "var(--viz-1)", strokeWidth: "2", strokeLinejoin: "round", strokeLinecap: "round" }), data.length ? (_jsx("circle", { cx: W, cy: H - (data[data.length - 1] / max) * (H - 4) - 2, r: "2.5", fill: "var(--viz-1)" })) : null] }));
|
|
55
55
|
};
|
|
56
56
|
/* ------------------------------------------------------------------ */
|
|
57
57
|
/* Horizontal bar list — direct-labelled, which is also the contrast */
|
|
@@ -60,11 +60,11 @@ export const Sparkline = ({ data }) => {
|
|
|
60
60
|
export const BarList = ({ items, colorVar = 'var(--viz-1)', unit = '', hrefFor, }) => {
|
|
61
61
|
const max = Math.max(1, ...items.map((i) => i.value));
|
|
62
62
|
if (items.length === 0)
|
|
63
|
-
return _jsx("p", { className: "
|
|
64
|
-
return (_jsx("ul", { className: "
|
|
63
|
+
return _jsx("p", { className: "blog_d-empty", children: "No data yet." });
|
|
64
|
+
return (_jsx("ul", { className: "blog_barlist", children: items.map((item) => {
|
|
65
65
|
const href = hrefFor?.(item.label);
|
|
66
|
-
const label = (_jsxs(_Fragment, { children: [_jsx("span", { className: "
|
|
67
|
-
return (_jsxs("li", { className: "
|
|
66
|
+
const label = (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_bl-label", title: item.label, children: item.label }), _jsxs("span", { className: "blog_bl-value", children: [fmt.format(item.value), unit] })] }));
|
|
67
|
+
return (_jsxs("li", { className: "blog_bl-row", children: [_jsx("div", { className: "blog_bl-head", children: href ? (_jsx("a", { href: href, className: "blog_bl-link", children: label })) : (label) }), _jsx("div", { className: "blog_bl-track", children: _jsx("div", { className: "blog_bl-fill", style: { width: `${(item.value / max) * 100}%`, background: colorVar } }) })] }, item.label));
|
|
68
68
|
}) }));
|
|
69
69
|
};
|
|
70
70
|
/* ------------------------------------------------------------------ */
|
|
@@ -72,7 +72,7 @@ export const BarList = ({ items, colorVar = 'var(--viz-1)', unit = '', hrefFor,
|
|
|
72
72
|
/* ------------------------------------------------------------------ */
|
|
73
73
|
export const Funnel = ({ stages, }) => {
|
|
74
74
|
const max = Math.max(1, ...stages.map((s) => s.value));
|
|
75
|
-
return (_jsx("ul", { className: "
|
|
75
|
+
return (_jsx("ul", { className: "blog_funnel", children: stages.map((s) => (_jsxs("li", { children: [_jsxs("div", { className: "blog_fn-head", children: [_jsx("span", { className: "blog_fn-label", children: s.label }), _jsx("span", { className: "blog_fn-value", children: fmt.format(s.value) })] }), _jsx("div", { className: "blog_fn-track", children: _jsx("div", { className: "blog_fn-fill", style: {
|
|
76
76
|
width: `${Math.max(2, (s.value / max) * 100)}%`,
|
|
77
77
|
background: `var(--ord-${s.tone})`,
|
|
78
78
|
} }) })] }, s.label))) }));
|
|
@@ -7,7 +7,7 @@ import { analyseReadability, analyseSeo, summariseTone, toneLabel, } from "../..
|
|
|
7
7
|
import { YoastSeoMetabox } from "./YoastSeoMetabox";
|
|
8
8
|
import { RichTextEditor } from "./RichTextEditor";
|
|
9
9
|
import { MediaPicker } from "../MediaPicker";
|
|
10
|
-
const ScoreDot = ({ tone }) => (_jsx("span", { className: `
|
|
10
|
+
const ScoreDot = ({ tone }) => (_jsx("span", { className: `blog_seo-dot blog_seo-dot-${tone}`, "aria-hidden": "true" }));
|
|
11
11
|
export function PostEditor({ postId, onDone, }) {
|
|
12
12
|
const store = useAdminMock();
|
|
13
13
|
const existing = postId ? store.getPost(postId) : undefined;
|
|
@@ -359,23 +359,23 @@ export function PostEditor({ postId, onDone, }) {
|
|
|
359
359
|
: "Immediately";
|
|
360
360
|
const permalink = `/blog/${slug || slugify(title) || "…"}`;
|
|
361
361
|
const categoryOptions = categoryTab === "popular" ? categories.slice(0, 8) : categories;
|
|
362
|
-
return (_jsxs("div", { className: "
|
|
362
|
+
return (_jsxs("div", { className: "blog_ed-layout", children: [_jsxs("div", { className: "blog_ed-main", children: [error ? (_jsx("p", { className: "blog_form-error blog_ed-error", role: "alert", children: error })) : null, flash ? (_jsx("p", { className: "blog_form-ok", role: "status", children: flash })) : null, _jsx("div", { className: "blog_ed-title-wrap", children: _jsx("input", { className: "blog_fld blog_ed-title", value: title, onChange: (e) => {
|
|
363
363
|
setTitle(e.target.value);
|
|
364
364
|
if (!existing && !slugEditing)
|
|
365
365
|
setSlug(slugify(e.target.value));
|
|
366
|
-
}, placeholder: "Add title", "aria-label": "Post title" }) }), _jsxs("div", { className: "
|
|
366
|
+
}, placeholder: "Add title", "aria-label": "Post title" }) }), _jsxs("div", { className: "blog_ed-permalink", children: [_jsx("span", { className: "blog_lbl", children: "Permalink:" }), slugEditing ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_mono", children: "/blog/" }), _jsx("input", { className: "blog_fld blog_mono", style: { maxWidth: 220 }, value: slug, onChange: (e) => setSlug(e.target.value), onBlur: () => {
|
|
367
367
|
setSlug(slugify(slug || title));
|
|
368
368
|
setSlugEditing(false);
|
|
369
|
-
} })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "
|
|
369
|
+
} })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "blog_mono blog_ed-permalink-value", children: permalink }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", onClick: () => setSlugEditing(true), children: "Edit" })] })), _jsxs("span", { className: "blog_ed-bar-meta", style: { marginLeft: "auto" }, children: [wordCount, " words"] })] }), _jsx(RichTextEditor, { initialHtml: existing?.contentHtml ?? contentHtml, onChange: onContentChange, onPickImage: onPickImage, searchTargets: searchTargets }), _jsxs("div", { className: "blog_box", children: [_jsxs("div", { className: "blog_box-head", children: [_jsx("h3", { children: "Excerpt" }), _jsxs("span", { className: "blog_box-hint", children: [excerpt.length, "/300"] })] }), _jsx("div", { className: "blog_box-body", children: _jsx("textarea", { className: "blog_fld", rows: 3, maxLength: 300, value: excerpt, placeholder: "One or two sentences. Used on index pages and as the meta description fallback.", onChange: (e) => setExcerpt(e.target.value) }) })] }), _jsx(YoastSeoMetabox, { title: title, slug: slug, excerpt: excerpt, visibility: visibility, featuredImage: featuredImage, seo: seo, seoInput: seoInput, siteName: brand?.name ?? "My Site", categoryName: categoryName, contentText: contentText, contentHtml: contentHtml, publishedAt: publishedAt, scheduledFor: scheduledFor, fieldErrors: fieldErrors, onSeoChange: onSeoChange, onSlugChange: setSlug, onSlugBlur: () => setSlug(slugify(slug || title)), onPickOgImage: () => setPickerFor("og"), onClearFieldError: (field) => setFieldErrors((current) => {
|
|
370
370
|
const next = { ...current };
|
|
371
371
|
delete next[field];
|
|
372
372
|
return next;
|
|
373
|
-
}), onSetFieldError: (field, message) => setFieldErrors((current) => ({ ...current, [field]: message })) })] }), _jsxs("aside", { className: "
|
|
373
|
+
}), onSetFieldError: (field, message) => setFieldErrors((current) => ({ ...current, [field]: message })) })] }), _jsxs("aside", { className: "blog_ed-side", "aria-label": "Post settings", children: [_jsxs("div", { className: "blog_box blog_box-publish", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Publish" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("div", { className: "blog_btn-row", children: [_jsx("button", { type: "button", className: "blog_btn", disabled: pending, onClick: () => persist(isLive ? "publish" : "draft"), children: pending ? "Saving…" : isLive ? "Update" : "Save Draft" }), _jsx("button", { type: "button", className: "blog_btn", disabled: pending, title: "Preview", children: "Preview" })] }), _jsxs("div", { className: "blog_facts blog_wp-facts", children: [_jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: "Status:" }), _jsxs("span", { className: "blog_fact-val", children: [isLive ? "Published" : "Draft", " ", _jsx("button", { type: "button", className: "blog_linkish", onClick: () => setShowStatus((v) => !v), children: "Edit" })] })] }), showStatus ? (_jsx("div", { className: "blog_fact-edit", children: _jsxs("select", { className: "blog_fld blog_fld-sm", value: isLive ? "published" : "draft", onChange: (e) => {
|
|
374
374
|
if (e.target.value === "published")
|
|
375
375
|
persist("publish");
|
|
376
376
|
else if (isLive)
|
|
377
377
|
persist("unpublish");
|
|
378
|
-
}, children: [_jsx("option", { value: "draft", children: "Draft" }), _jsx("option", { value: "published", children: "Published" })] }) })) : null, _jsxs("div", { className: "
|
|
378
|
+
}, children: [_jsx("option", { value: "draft", children: "Draft" }), _jsx("option", { value: "published", children: "Published" })] }) })) : null, _jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: "Visibility:" }), _jsxs("span", { className: "blog_fact-val", children: [visibility === "private" ? "Private" : "Public", " ", _jsx("button", { type: "button", className: "blog_linkish", onClick: () => setShowVisibility((v) => !v), children: "Edit" })] })] }), showVisibility ? (_jsx("div", { className: "blog_fact-edit", children: _jsxs("select", { className: "blog_fld blog_fld-sm", value: visibility, onChange: (e) => setVisibility(e.target.value), children: [_jsx("option", { value: "public", children: "Public" }), _jsx("option", { value: "private", children: "Private" })] }) })) : null, _jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: scheduledFor && !isLive ? "Schedule:" : "Publish:" }), _jsxs("span", { className: "blog_fact-val", children: [publishWhenLabel, " ", _jsx("button", { type: "button", className: "blog_linkish", onClick: () => setShowSchedule((v) => !v), children: "Edit" })] })] })] }), showSchedule ? (_jsxs("div", { className: "blog_stack", children: [_jsx("input", { type: "datetime-local", className: "blog_fld", value: scheduleWhen, onChange: (e) => setScheduleWhen(e.target.value), "aria-label": "Publish date and time" }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-primary blog_btn-tiny", disabled: pending, onClick: () => persist("schedule"), children: "OK" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", onClick: () => setShowSchedule(false), children: "Cancel" }), scheduledFor ? (_jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", disabled: pending, onClick: () => persist("unschedule"), children: "Clear" })) : null] })] })) : null, _jsxs("div", { className: "blog_seo-summary blog_publish-seo-summary blog_wp-publish-scores", children: [_jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: "SEO analysis:" }), _jsxs("span", { className: "blog_fact-val", children: [_jsx(ScoreDot, { tone: seoTone }), " ", toneLabel(seoTone)] })] }), _jsxs("div", { className: "blog_fact-row", children: [_jsx("span", { className: "blog_fact-lbl", children: "Readability:" }), _jsxs("span", { className: "blog_fact-val", children: [_jsx(ScoreDot, { tone: readTone }), " ", toneLabel(readTone)] })] })] }), _jsxs("div", { className: "blog_publish-foot", children: [_jsxs("div", { className: "blog_publish-foot-left", children: [postIdCurrent ? (_jsx("button", { type: "button", className: "blog_linkish", disabled: pending, onClick: copyToDraft, children: "Copy to a new draft" })) : null, postIdCurrent ? (_jsx("button", { type: "button", className: "blog_btn-danger-link", disabled: pending, onClick: moveToTrash, children: "Move to Trash" })) : null] }), !isLive ? (_jsx("button", { type: "button", className: "blog_btn blog_btn-primary", disabled: pending, onClick: () => persist("publish"), children: pending ? "Publishing…" : "Publish" })) : null] })] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Format" }) }), _jsx("div", { className: "blog_box-body", children: _jsx("div", { className: "blog_pick-list blog_pick-list-auto", children: [
|
|
379
379
|
["standard", "Standard"],
|
|
380
380
|
["video", "Video"],
|
|
381
381
|
["audio", "Audio"],
|
|
@@ -383,23 +383,23 @@ export function PostEditor({ postId, onDone, }) {
|
|
|
383
383
|
["gallery", "Gallery"],
|
|
384
384
|
["image", "Image"],
|
|
385
385
|
["link", "Link"],
|
|
386
|
-
].map(([value, label]) => (_jsxs("label", { className: "
|
|
386
|
+
].map(([value, label]) => (_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "radio", name: "postFormat", checked: postFormat === value, onChange: () => setPostFormat(value) }), label] }, value))) }) })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Categories" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("div", { className: "blog_yoast-preview-modes blog_wp-cat-tabs", role: "tablist", "aria-label": "Category list", children: [_jsx("button", { type: "button", role: "tab", className: `blog_yoast-preview-mode${categoryTab === "all" ? " blog_is-active" : ""}`, "aria-selected": categoryTab === "all", onClick: () => setCategoryTab("all"), children: "All Categories" }), _jsx("button", { type: "button", role: "tab", className: `blog_yoast-preview-mode${categoryTab === "popular" ? " blog_is-active" : ""}`, "aria-selected": categoryTab === "popular", onClick: () => setCategoryTab("popular"), children: "Most Used" })] }), _jsx("div", { className: "blog_pick-list", children: categoryOptions.length === 0 ? (_jsx("p", { className: "blog_note", children: "No categories in this brand yet." })) : (categoryOptions.map((c) => (_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "checkbox", checked: categoryIds.includes(c.id), onChange: () => setCategoryIds((ids) => ids.includes(c.id) ? ids.filter((x) => x !== c.id) : [...ids, c.id]) }), c.name] }, c.id)))) }), categoryIds.length > 1 ? (_jsxs("p", { className: "blog_note", children: [categoryIds.length, " selected \u00B7 first selected is used for breadcrumbs."] })) : null, _jsx("button", { type: "button", className: "blog_yoast-link-btn", onClick: () => document.getElementById("new-category-input")?.focus(), children: "+ Add New Category" }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("input", { id: "new-category-input", className: "blog_fld", placeholder: "New category", value: newCategory, onChange: (e) => setNewCategory(e.target.value), onKeyDown: (e) => {
|
|
387
387
|
if (e.key === "Enter") {
|
|
388
388
|
e.preventDefault();
|
|
389
389
|
addCategory();
|
|
390
390
|
}
|
|
391
|
-
} }), _jsx("button", { type: "button", className: "
|
|
391
|
+
} }), _jsx("button", { type: "button", className: "blog_btn", onClick: addCategory, disabled: !newCategory.trim(), children: "Add" })] })] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Tags" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("div", { className: "blog_tokens", children: [tagIds.map((id) => {
|
|
392
392
|
const tag = tags.find((t) => t.id === id);
|
|
393
|
-
return (_jsxs("span", { className: "
|
|
394
|
-
}), newTags.map((name) => (_jsxs("span", { className: "
|
|
393
|
+
return (_jsxs("span", { className: "blog_token", children: [tag?.name ?? "tag", _jsx("button", { type: "button", "aria-label": `Remove ${tag?.name ?? "tag"}`, onClick: () => setTagIds((list) => list.filter((t) => t !== id)), children: "\u2715" })] }, id));
|
|
394
|
+
}), newTags.map((name) => (_jsxs("span", { className: "blog_token blog_token-new", children: [name, _jsx("button", { type: "button", "aria-label": `Remove ${name}`, onClick: () => setNewTags((list) => list.filter((t) => t !== name)), children: "\u2715" })] }, name)))] }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("input", { className: "blog_fld", placeholder: "Add a tag and press Enter", value: tagDraft, list: "tag-suggestions", onChange: (e) => setTagDraft(e.target.value), onKeyDown: (e) => {
|
|
395
395
|
if (e.key === "Enter" || e.key === ",") {
|
|
396
396
|
e.preventDefault();
|
|
397
397
|
addTagFromDraft();
|
|
398
398
|
}
|
|
399
|
-
} }), _jsx("datalist", { id: "tag-suggestions", children: tags.map((t) => (_jsx("option", { value: t.name }, t.id))) }), _jsx("button", { type: "button", className: "
|
|
399
|
+
} }), _jsx("datalist", { id: "tag-suggestions", children: tags.map((t) => (_jsx("option", { value: t.name }, t.id))) }), _jsx("button", { type: "button", className: "blog_btn", onClick: addTagFromDraft, disabled: !tagDraft.trim(), children: "Add" })] }), tags.filter((t) => !tagIds.includes(t.id)).length ? (_jsxs("div", { className: "blog_tag-most-used", children: [_jsx("p", { className: "blog_note", children: "Most used" }), _jsx("div", { className: "blog_tag-cloud", children: tags
|
|
400
400
|
.filter((t) => !tagIds.includes(t.id))
|
|
401
401
|
.slice(0, 12)
|
|
402
|
-
.map((t) => (_jsx("button", { type: "button", className: "
|
|
402
|
+
.map((t) => (_jsx("button", { type: "button", className: "blog_tag-cloud-item", onClick: () => setTagIds((ids) => [...ids, t.id]), children: t.name }, t.id))) })] })) : null, _jsx("p", { className: "blog_note", children: "New tags are created when the post is saved." })] })] }), _jsxs("div", { className: "blog_box", children: [_jsxs("div", { className: "blog_box-head", children: [_jsx("h3", { children: "Featured image" }), _jsx("span", { className: "blog_box-hint", children: "optional" })] }), _jsxs("div", { className: `blog_box-body blog_drop-target${featuredDrag ? " blog_is-dragging" : ""}`, onDragOver: (e) => {
|
|
403
403
|
e.preventDefault();
|
|
404
404
|
setFeaturedDrag(true);
|
|
405
405
|
}, onDragLeave: () => setFeaturedDrag(false), onDrop: (e) => {
|
|
@@ -411,18 +411,18 @@ export function PostEditor({ postId, onDone, }) {
|
|
|
411
411
|
if (e.target.files?.length)
|
|
412
412
|
void uploadFeatured(e.target.files);
|
|
413
413
|
e.target.value = "";
|
|
414
|
-
} }), featuredImage ? (_jsxs("div", { className: "
|
|
414
|
+
} }), featuredImage ? (_jsxs("div", { className: "blog_stack", children: [_jsx("img", { className: "blog_feat", src: featuredImage.url, alt: featuredImage.alt || "" }), _jsxs("label", { className: "blog_fld-row blog_feat-alt", children: [_jsxs("span", { className: "blog_fld-lbl", children: ["Alt text ", _jsx("em", { className: "blog_fld-em", children: "recommended for accessibility" })] }), _jsx("textarea", { className: "blog_fld", rows: 2, value: featuredImage.alt, placeholder: "Describe the image for screen readers and SEO\u2026", onChange: (e) => {
|
|
415
415
|
setFeaturedImage((img) => (img ? { ...img, alt: e.target.value } : img));
|
|
416
416
|
setFeaturedAltSaved(false);
|
|
417
417
|
}, onBlur: () => {
|
|
418
418
|
if (featuredImage)
|
|
419
419
|
saveFeaturedAlt();
|
|
420
|
-
}, "aria-describedby": "feat-alt-hint" }), _jsx("span", { id: "feat-alt-hint", className: "
|
|
420
|
+
}, "aria-describedby": "feat-alt-hint" }), _jsx("span", { id: "feat-alt-hint", className: "blog_note blog_feat-alt-foot", children: featuredAltBusy
|
|
421
421
|
? "Saving alt text…"
|
|
422
422
|
: featuredAltSaved
|
|
423
423
|
? "Alt text saved to the media library."
|
|
424
|
-
: "Saved on the image itself — used on the public post and in SEO." })] }), _jsxs("div", { className: "
|
|
424
|
+
: "Saved on the image itself — used on the public post and in SEO." })] }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", disabled: featuredAltBusy, onClick: () => saveFeaturedAlt(), children: featuredAltBusy ? "Saving…" : "Save alt text" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", onClick: () => setPickerFor("featured"), children: "Replace image" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", disabled: featuredBusy, onClick: () => featuredFileRef.current?.click(), children: featuredBusy ? "Uploading…" : "Upload" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-tiny", onClick: () => {
|
|
425
425
|
setFeaturedImage(null);
|
|
426
426
|
setFeaturedAltSaved(false);
|
|
427
|
-
}, children: "Remove featured image" })] })] })) : (_jsxs("div", { className: "
|
|
427
|
+
}, children: "Remove featured image" })] })] })) : (_jsxs("div", { className: "blog_stack", children: [_jsx("button", { type: "button", className: "blog_btn blog_btn-block", onClick: () => setPickerFor("featured"), children: "Set featured image" }), _jsxs("button", { type: "button", className: "blog_dropzone-cta", disabled: featuredBusy, onClick: () => featuredFileRef.current?.click(), children: [_jsx("span", { className: "blog_dropzone-icon", "aria-hidden": "true", children: "\u2B06" }), _jsx("span", { children: featuredBusy ? "Uploading…" : "Or drop an image here to upload" }), _jsx("span", { className: "blog_note", children: "JPEG, PNG, WebP or AVIF \u00B7 up to 12 MB \u00B7 add alt text after upload" })] })] }))] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Post Attributes" }) }), _jsx("div", { className: "blog_box-body blog_stack", children: _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Template" }), _jsx("select", { className: "blog_fld", value: template, onChange: (e) => setTemplate(e.target.value), children: _jsx("option", { value: "default", children: "Default template" }) })] }) })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Authors" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsx("div", { className: "blog_pick-list", children: authors.length === 0 ? (_jsxs("p", { className: "blog_note", children: ["No authors for ", brand?.name ?? "this brand", " yet \u2014 create them on the Authors screen."] })) : (authors.map((a) => (_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "checkbox", checked: authorIds.includes(a.id), onChange: (e) => setAuthorIds((ids) => e.target.checked ? [...ids, a.id] : ids.filter((x) => x !== a.id)) }), _jsxs("span", { children: [a.name, a.name === profile.name ? _jsx("span", { className: "blog_box-hint", children: " \u00B7 you" }) : null] })] }, a.id)))) }), _jsx("p", { className: "blog_note", children: "You are selected by default. Add or remove others anytime." }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Author Box" }), _jsx("select", { className: "blog_fld", value: authorBox, onChange: (e) => setAuthorBox(e.target.value), children: _jsx("option", { value: "default", children: "Default Author Box" }) })] })] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Discussion" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "checkbox", checked: allowComments, onChange: (e) => setAllowComments(e.target.checked) }), _jsx("span", { children: "Allow comments on this post" })] }), _jsx("p", { className: "blog_note", children: "When checked, readers can leave comments on the published article. Uncheck to close the discussion without deleting existing comments." })] })] }), _jsxs("div", { className: "blog_box", children: [_jsx("div", { className: "blog_box-head", children: _jsx("h3", { children: "Table of Contents" }) }), _jsxs("div", { className: "blog_box-body blog_stack", children: [_jsxs("label", { className: "blog_pick-row", children: [_jsx("input", { type: "checkbox", checked: showToc, onChange: (e) => setShowToc(e.target.checked) }), _jsx("span", { children: "Show Table of Contents on this post" })] }), _jsx("p", { className: "blog_note", children: "When checked, the published article shows a right-side Table of Contents listing other published posts from this brand. Leave unchecked to hide the card." })] })] })] }), _jsx(MediaPicker, { open: pickerFor !== null, brandId: brandId || null, onClose: () => setPickerFor(null), onSelect: choosePicked, title: pickerFor === "og" ? "Social share image" : "Featured image" })] }));
|
|
428
428
|
}
|