@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.
Files changed (61) hide show
  1. package/dist/blog-client.iife.js +1 -1
  2. package/dist/components/admin/AdminPostDetail.js +5 -5
  3. package/dist/components/admin/BrandSettings.js +5 -5
  4. package/dist/components/admin/BrandSwitcher.js +1 -1
  5. package/dist/components/admin/Chips.js +5 -5
  6. package/dist/components/admin/CommentsManager.js +6 -6
  7. package/dist/components/admin/Dashboard.js +1 -1
  8. package/dist/components/admin/MediaLibrary.js +5 -5
  9. package/dist/components/admin/MediaPicker.js +4 -4
  10. package/dist/components/admin/PostList.d.ts.map +1 -1
  11. package/dist/components/admin/PostList.js +13 -4
  12. package/dist/components/admin/PostsTable.d.ts +6 -1
  13. package/dist/components/admin/PostsTable.d.ts.map +1 -1
  14. package/dist/components/admin/PostsTable.js +15 -9
  15. package/dist/components/admin/Shell.js +3 -3
  16. package/dist/components/admin/TermManager.js +4 -4
  17. package/dist/components/admin/UsersManager.js +3 -3
  18. package/dist/components/admin/charts.js +7 -7
  19. package/dist/components/admin/editor/PostEditor.js +16 -16
  20. package/dist/components/admin/editor/RichTextEditor.js +8 -8
  21. package/dist/components/admin/editor/YoastCollapsible.js +2 -2
  22. package/dist/components/admin/editor/YoastInsightsPanel.js +1 -1
  23. package/dist/components/admin/editor/YoastInternalLinksPanel.js +1 -1
  24. package/dist/components/admin/editor/YoastSeoAnalysisPanel.js +6 -6
  25. package/dist/components/admin/editor/YoastSeoMetabox.js +8 -8
  26. package/dist/components/admin/editor/YoastSnippetField.js +2 -2
  27. package/dist/components/admin/internal/States.js +3 -3
  28. package/dist/components/admin/mock/store.d.ts.map +1 -1
  29. package/dist/components/admin/mock/store.js +3 -2
  30. package/dist/components/admin/seo/Ga4Settings.js +6 -6
  31. package/dist/components/admin/seo/RedirectAnalyticsDashboard.js +1 -1
  32. package/dist/components/admin/seo/RedirectDetail.js +6 -6
  33. package/dist/components/admin/seo/RedirectForm.js +1 -1
  34. package/dist/components/admin/seo/RedirectList.js +4 -4
  35. package/dist/components/admin/seo/RobotsEditor.js +1 -1
  36. package/dist/components/admin/seo/shared.js +2 -2
  37. package/dist/components/admin/tools/ToolsHub.js +4 -4
  38. package/dist/components/user/BlogCard.js +3 -3
  39. package/dist/components/user/BlogCategories.js +1 -1
  40. package/dist/components/user/BlogDetail.js +1 -1
  41. package/dist/components/user/BlogList.js +1 -1
  42. package/dist/components/user/BlogSearch.js +2 -2
  43. package/dist/components/user/BlogTags.js +1 -1
  44. package/dist/components/user/FeaturedBlogs.js +1 -1
  45. package/dist/components/user/RelatedBlogs.js +1 -1
  46. package/dist/components/user/SiteMediaHero.js +1 -1
  47. package/dist/components/user/detail/ArticleDetailHero.js +5 -5
  48. package/dist/components/user/detail/ArticleRailDetails.js +2 -2
  49. package/dist/components/user/detail/CommentSection.js +2 -2
  50. package/dist/components/user/detail/HeroFadeSlide.js +1 -1
  51. package/dist/components/user/detail/ReadingProgress.js +1 -1
  52. package/dist/components/user/detail/ShareRow.js +1 -1
  53. package/dist/components/user/internal/States.js +4 -4
  54. package/dist/lib/lexical-doc.js +1 -1
  55. package/dist/pages/admin/BlogAdmin.d.ts.map +1 -1
  56. package/dist/pages/admin/BlogAdmin.js +23 -11
  57. package/dist/services/admin-sdk-client.d.ts +9 -0
  58. package/dist/services/admin-sdk-client.d.ts.map +1 -1
  59. package/dist/services/admin-sdk-client.js +17 -0
  60. package/dist/style.css +1023 -1016
  61. package/package.json +2 -1
@@ -1 +1 @@
1
- (function(a){"use strict";class c extends Error{constructor(e,t={status:0}){super(e),this.name="ApiError",this.status=t.status,this.code=t.code??"API_ERROR",this.details=t.details??null}}function p(o){return o instanceof c}function U(o){return p(o)&&(o.status===401||o.status===403)}function B(o){return p(o)||o instanceof Error?o.message:"Unexpected error"}function _(o,e){const t=o.replace(/\/+$/,""),n=e.startsWith("/")?e:`/${e}`;return`${t}${n}`}function r(o){if(!o)return"";const e=new URLSearchParams;for(const[n,s]of Object.entries(o))s==null||s===""||e.set(n,String(s));const t=e.toString();return t?`?${t}`:""}function O(o,e,t){if(e&&typeof e=="object"){const n=e;if(typeof n.message=="string"&&n.message)return new c(n.message,{status:o,code:typeof n.code=="string"?n.code:"API_ERROR",details:n.details??null});if(typeof n.detail=="string"&&n.detail)return new c(n.detail,{status:o,code:"API_ERROR"});if(Array.isArray(n.detail)){const s=n.detail.map(d=>d&&typeof d=="object"&&"msg"in d?String(d.msg):JSON.stringify(d)).join("; ");return new c(s||t,{status:o,code:"VALIDATION_ERROR",details:n.detail})}}return new c(t,{status:o,code:"API_ERROR"})}function C(o){return o&&typeof o=="object"&&"success"in o&&"data"in o?o.data:o}async function f(o,e,t,n={}){var w,A,$,S,P;if(typeof fetch!="function")throw new c("fetch is not available in this environment",{status:0,code:"FETCH_UNAVAILABLE"});const s=n.timeoutMs??3e4,d=new AbortController,k=setTimeout(()=>d.abort(),s),E=()=>d.abort();(w=n.signal)==null||w.addEventListener("abort",E);const l={Accept:"application/json",...n.headers};t&&(l.Authorization=t);const I=(A=n.email)==null?void 0:A.trim();I&&(l.email=I.toLowerCase());const R=($=n.apiKey)==null?void 0:$.trim();R&&(l.apiKey=R);const b=(S=n.domain)==null?void 0:S.trim();b&&(l.domain=b,l["X-Client-Domain"]=b);let q;typeof FormData<"u"&&n.body instanceof FormData?q=n.body:n.body!==void 0&&(l["Content-Type"]=l["Content-Type"]??"application/json",q=JSON.stringify(n.body));try{const u=await fetch(_(o,e),{method:n.method??"GET",headers:l,body:q,signal:d.signal,credentials:"omit"}),h=await u.text(),N=(u.headers.get("content-type")??"").includes("application/json")||h.startsWith("{")||h.startsWith("[");let m=h||null;if(h&&N)try{m=JSON.parse(h)}catch{m=h}if(!u.ok)throw O(u.status,m,`Blog API error: ${u.status}`);return m===null||m===""?void 0:C(m)}catch(u){throw u instanceof c?u:u instanceof Error&&u.name==="AbortError"?new c("Request timed out",{status:408,code:"REQUEST_TIMEOUT"}):new c(u instanceof Error?u.message:"Network error",{status:0,code:"NETWORK_ERROR"})}finally{clearTimeout(k),(P=n.signal)==null||P.removeEventListener("abort",E)}}async function T(o,e,t,n={}){return f(o,e,null,{...n,email:t.email,apiKey:t.apiKey,domain:t.domain??n.domain})}function i(o,e,t){if(Array.isArray(o))return{items:o,page:e,pageSize:t,total:o.length};if(o&&typeof o=="object"){const n=o,s=n.items??n.results??n.data??[],d=Array.isArray(s)?s:[];return{items:d,page:Number(n.page??e)||e,pageSize:Number(n.page_size??n.pageSize??t)||t,total:Number(n.total??n.count??d.length)||d.length}}return{items:[],page:e,pageSize:t,total:0}}function z(o){return o.toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function v(o,e,t){return r({page:e,page_size:t,search:o.search,status:o.status,category_id:o.category_id,tag_id:o.tag_id,published_after:o.published_after,published_before:o.published_before,sort:o.sort,order:o.order})}class L{constructor(e){this.config=e}request(e,t={}){return f(this.config.apiUrl,e,null,{timeoutMs:this.config.timeoutMs,email:this.config.email,apiKey:this.config.publicApiKey,domain:this.config.domain,...t})}async getBlogs(e={}){const t=e.page??1,n=e.page_size??12,s=await this.request(`/blogs${v(e,t,n)}`);return i(s,t,n)}getBlogById(e){return this.request(`/blogs/${encodeURIComponent(e)}`)}getBlogBySlug(e){return this.request(`/blogs/slug/${encodeURIComponent(e)}`)}getBlog(e){return this.getBlogBySlug(e)}async searchBlogs(e,t={}){return this.getBlogs({...t,search:e})}async getFeaturedBlogs(e=3){return(await this.getBlogs({page:1,page_size:e,sort:"published_at",order:"desc"})).items}async getRelatedBlogs(e,t=3){return e.category_id?(await this.getBlogs({category_id:e.category_id,page:1,page_size:t+1,sort:"published_at",order:"desc"})).items.filter(s=>s.id!==e.id).slice(0,t):[]}async getCategories(){const e=await this.request("/categories");return Array.isArray(e)?e:i(e,1,100).items}async getTags(){const e=await this.request("/tags");return Array.isArray(e)?e:i(e,1,100).items}trackView(e,t){return this.request("/analytics/views",{method:"POST",body:{blog_id:e,referrer:t??void 0}})}getPopularBlogs(e=10){return this.request(`/analytics/popular${r({limit:e})}`)}}function M(o){return`Bearer ${o.adminApiKey}`}function D(o,e,t){return r({page:e,page_size:t,search:o.search,status:o.status,category_id:o.category_id,tag_id:o.tag_id,published_after:o.published_after,published_before:o.published_before,sort:o.sort,order:o.order})}function y(o){const e={};for(const[t,n]of Object.entries(o))n===void 0||n===""||(e[t]=n);return e}class j{constructor(e){this.config=e}request(e,t={}){return f(this.config.apiUrl,e,M(this.config),{timeoutMs:this.config.timeoutMs,domain:this.config.domain,...t})}async getBlogs(e={}){const t=e.page??1,n=e.page_size??20,s=await this.request(`/blogs${D(e,t,n)}`);return i(s,t,n)}getBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}`)}createBlog(e){return this.request("/blogs",{method:"POST",body:y({...e})})}updateBlog(e,t){return this.request(`/blogs/${encodeURIComponent(e)}`,{method:"PUT",body:y({...t})})}patchBlog(e,t){return this.request(`/blogs/${encodeURIComponent(e)}`,{method:"PATCH",body:y({...t})})}deleteBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}`,{method:"DELETE"})}publishBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}/publish`,{method:"POST"})}unpublishBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}/unpublish`,{method:"POST"})}scheduleBlog(e,t){return this.request(`/blogs/${encodeURIComponent(e)}/schedule`,{method:"POST",body:{scheduled_at:t}})}archiveBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}/archive`,{method:"POST"})}async getCategories(){const e=await this.request("/categories");return Array.isArray(e)?e:i(e,1,100).items}createCategory(e){return this.request("/categories",{method:"POST",body:y({...e})})}updateCategory(e,t){return this.request(`/categories/${encodeURIComponent(e)}`,{method:"PUT",body:t})}deleteCategory(e){return this.request(`/categories/${encodeURIComponent(e)}`,{method:"DELETE"})}async getTags(){const e=await this.request("/tags");return Array.isArray(e)?e:i(e,1,100).items}createTag(e){return this.request("/tags",{method:"POST",body:y({...e})})}updateTag(e,t){return this.request(`/tags/${encodeURIComponent(e)}`,{method:"PUT",body:t})}deleteTag(e){return this.request(`/tags/${encodeURIComponent(e)}`,{method:"DELETE"})}async getMedia(e={}){const t=e.page??1,n=e.page_size??20,s=await this.request(`/media${r({page:t,page_size:n})}`);return i(s,t,n)}async listMediaAssets(e={}){return(await this.getMedia(e)).items}uploadMedia(e){const t=new FormData;return t.append("file",e),this.request("/media",{method:"POST",body:t})}deleteMedia(e){return this.request(`/media/${encodeURIComponent(e)}`,{method:"DELETE"})}}function g(o){const e={};for(const[t,n]of Object.entries(o))n!==void 0&&(e[t]=n);return e}class K{constructor(e){this.config=e}credentials(){return{email:this.config.email,apiKey:this.config.adminApiKey,domain:this.config.domain}}request(e,t={}){return T(this.config.apiUrl,e,this.credentials(),{timeoutMs:this.config.timeoutMs,...t})}me(){return this.request("/admin/admins/me")}async listBrands(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/brands${r({page:t,pageSize:n,status:e.status})}`);return i(s,t,n)}createBrand(e){return this.request("/admin/brands",{method:"POST",body:g(e)})}async listPosts(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/posts${r({page:t,pageSize:n,brandId:e.brandId,status:e.status,q:e.q,categoryId:e.categoryId,includeTrash:e.includeTrash?"true":void 0})}`);return i(s,t,n)}getPost(e){return this.request(`/admin/posts/${encodeURIComponent(e)}`)}createPost(e){return this.request("/admin/posts",{method:"POST",body:g({...e})})}updatePost(e,t){return this.request(`/admin/posts/${encodeURIComponent(e)}`,{method:"PATCH",body:g({...t})})}deletePost(e,t=!1){return this.request(`/admin/posts/${encodeURIComponent(e)}${r({forever:t?"true":void 0})}`,{method:"DELETE"})}publishPost(e){return this.request(`/admin/posts/${encodeURIComponent(e)}/publish`,{method:"POST"})}unpublishPost(e){return this.request(`/admin/posts/${encodeURIComponent(e)}/unpublish`,{method:"POST"})}async listCategories(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/categories${r({page:t,pageSize:n,brandId:e.brandId,q:e.q,status:e.status})}`);return i(s,t,n)}createCategory(e){return this.request("/admin/categories",{method:"POST",body:g(e)})}updateCategory(e,t){return this.request(`/admin/categories/${encodeURIComponent(e)}`,{method:"PATCH",body:g(t)})}deleteCategory(e,t=!1){return this.request(`/admin/categories/${encodeURIComponent(e)}${r({hard:t?"true":void 0})}`,{method:"DELETE"})}async listComments(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/comments${r({page:t,pageSize:n,brandId:e.brandId,postId:e.postId,status:e.status})}`);return i(s,t,n)}setCommentStatus(e,t){return this.request(`/admin/comments/${encodeURIComponent(e)}/status`,{method:"PATCH",body:{status:t}})}deleteComment(e){return this.request(`/admin/comments/${encodeURIComponent(e)}`,{method:"DELETE"})}async listTags(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/tags${r({page:t,pageSize:n,brandId:e.brandId,q:e.q,status:e.status})}`);return i(s,t,n)}createTag(e){return this.request("/admin/tags",{method:"POST",body:g(e)})}updateTag(e,t){return this.request(`/admin/tags/${encodeURIComponent(e)}`,{method:"PATCH",body:g(t)})}deleteTag(e,t=!1){return this.request(`/admin/tags/${encodeURIComponent(e)}${r({hard:t?"true":void 0})}`,{method:"DELETE"})}async listAuthors(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/authors${r({page:t,pageSize:n,brandId:e.brandId,q:e.q,status:e.status})}`);return i(s,t,n)}createAuthor(e){return this.request("/admin/authors",{method:"POST",body:g(e)})}updateAuthor(e,t){return this.request(`/admin/authors/${encodeURIComponent(e)}`,{method:"PATCH",body:g(t)})}deleteAuthor(e,t=!1){return this.request(`/admin/authors/${encodeURIComponent(e)}${r({hard:t?"true":void 0})}`,{method:"DELETE"})}async listMedia(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/media${r({page:t,pageSize:n,brandId:e.brandId,q:e.q})}`);return i(s,t,n)}uploadMedia(e,t){const n=new FormData;return n.append("file",e),n.append("brandId",t),this.request("/admin/media",{method:"POST",body:n})}updateMedia(e,t){return this.request(`/admin/media/${encodeURIComponent(e)}`,{method:"PATCH",body:g(t)})}deleteMedia(e){return this.request(`/admin/media/${encodeURIComponent(e)}`,{method:"DELETE"})}async resolveBrandByDomain(e){const t=e.trim().toLowerCase().replace(/^www\./,""),n=await this.listBrands({page:1,pageSize:100});return n.items.find(s=>(s.domain??"").toLowerCase().replace(/^www\./,"")===t)??n.items.find(s=>{try{return new URL(s.url||`https://${s.domain||""}`).hostname.toLowerCase().replace(/^www\./,"")===t}catch{return!1}})??n.items[0]??null}}a.AdminSdkClient=K,a.ApiError=c,a.BlogAdminClient=j,a.BlogClient=L,a.adminSdkRequest=T,a.getErrorMessage=B,a.httpRequest=f,a.isApiError=p,a.isAuthError=U,a.normalizePage=i,a.slugify=z,a.toQuery=r,a.unwrapData=C,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})})(this.BlogPlatform=this.BlogPlatform||{});
1
+ (function(d){"use strict";class c extends Error{constructor(e,t={status:0}){super(e),this.name="ApiError",this.status=t.status,this.code=t.code??"API_ERROR",this.details=t.details??null}}function b(o){return o instanceof c}function B(o){return b(o)&&(o.status===401||o.status===403)}function _(o){return b(o)||o instanceof Error?o.message:"Unexpected error"}function z(o,e){const t=o.replace(/\/+$/,""),n=e.startsWith("/")?e:`/${e}`;return`${t}${n}`}function i(o){if(!o)return"";const e=new URLSearchParams;for(const[n,s]of Object.entries(o))s==null||s===""||e.set(n,String(s));const t=e.toString();return t?`?${t}`:""}function O(o,e,t){if(e&&typeof e=="object"){const n=e;if(typeof n.message=="string"&&n.message)return new c(n.message,{status:o,code:typeof n.code=="string"?n.code:"API_ERROR",details:n.details??null});if(typeof n.detail=="string"&&n.detail)return new c(n.detail,{status:o,code:"API_ERROR"});if(Array.isArray(n.detail)){const s=n.detail.map(r=>r&&typeof r=="object"&&"msg"in r?String(r.msg):JSON.stringify(r)).join("; ");return new c(s||t,{status:o,code:"VALIDATION_ERROR",details:n.detail})}}return new c(t,{status:o,code:"API_ERROR"})}function T(o){return o&&typeof o=="object"&&"success"in o&&"data"in o?o.data:o}async function f(o,e,t,n={}){var A,$,S,P,U;if(typeof fetch!="function")throw new c("fetch is not available in this environment",{status:0,code:"FETCH_UNAVAILABLE"});const s=n.timeoutMs??3e4,r=new AbortController,p=setTimeout(()=>r.abort(),s),w=()=>r.abort();(A=n.signal)==null||A.addEventListener("abort",w);const l={Accept:"application/json",...n.headers};t&&(l.Authorization=t);const I=($=n.email)==null?void 0:$.trim();I&&(l.email=I.toLowerCase());const R=(S=n.apiKey)==null?void 0:S.trim();R&&(l.apiKey=R);const q=(P=n.domain)==null?void 0:P.trim();q&&(l.domain=q,l["X-Client-Domain"]=q);let C;typeof FormData<"u"&&n.body instanceof FormData?C=n.body:n.body!==void 0&&(l["Content-Type"]=l["Content-Type"]??"application/json",C=JSON.stringify(n.body));try{const u=await fetch(z(o,e),{method:n.method??"GET",headers:l,body:C,signal:r.signal,credentials:"omit"}),h=await u.text(),N=(u.headers.get("content-type")??"").includes("application/json")||h.startsWith("{")||h.startsWith("[");let m=h||null;if(h&&N)try{m=JSON.parse(h)}catch{m=h}if(!u.ok)throw O(u.status,m,`Blog API error: ${u.status}`);return m===null||m===""?void 0:T(m)}catch(u){throw u instanceof c?u:u instanceof Error&&u.name==="AbortError"?new c("Request timed out",{status:408,code:"REQUEST_TIMEOUT"}):new c(u instanceof Error?u.message:"Network error",{status:0,code:"NETWORK_ERROR"})}finally{clearTimeout(p),(U=n.signal)==null||U.removeEventListener("abort",w)}}async function E(o,e,t,n={}){return f(o,e,null,{...n,email:t.email,apiKey:t.apiKey,domain:t.domain??n.domain})}function a(o,e,t){if(Array.isArray(o))return{items:o,page:e,pageSize:t,total:o.length};if(o&&typeof o=="object"){const n=o,s=n.items??n.results??n.data??[],r=Array.isArray(s)?s:[];return{items:r,page:Number(n.page??e)||e,pageSize:Number(n.page_size??n.pageSize??t)||t,total:Number(n.total??n.count??r.length)||r.length}}return{items:[],page:e,pageSize:t,total:0}}function v(o){return o.toLowerCase().trim().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}function L(o,e,t){return i({page:e,page_size:t,search:o.search,status:o.status,category_id:o.category_id,tag_id:o.tag_id,published_after:o.published_after,published_before:o.published_before,sort:o.sort,order:o.order})}class M{constructor(e){this.config=e}request(e,t={}){return f(this.config.apiUrl,e,null,{timeoutMs:this.config.timeoutMs,email:this.config.email,apiKey:this.config.publicApiKey,domain:this.config.domain,...t})}async getBlogs(e={}){const t=e.page??1,n=e.page_size??12,s=await this.request(`/blogs${L(e,t,n)}`);return a(s,t,n)}getBlogById(e){return this.request(`/blogs/${encodeURIComponent(e)}`)}getBlogBySlug(e){return this.request(`/blogs/slug/${encodeURIComponent(e)}`)}getBlog(e){return this.getBlogBySlug(e)}async searchBlogs(e,t={}){return this.getBlogs({...t,search:e})}async getFeaturedBlogs(e=3){return(await this.getBlogs({page:1,page_size:e,sort:"published_at",order:"desc"})).items}async getRelatedBlogs(e,t=3){return e.category_id?(await this.getBlogs({category_id:e.category_id,page:1,page_size:t+1,sort:"published_at",order:"desc"})).items.filter(s=>s.id!==e.id).slice(0,t):[]}async getCategories(){const e=await this.request("/categories");return Array.isArray(e)?e:a(e,1,100).items}async getTags(){const e=await this.request("/tags");return Array.isArray(e)?e:a(e,1,100).items}trackView(e,t){return this.request("/analytics/views",{method:"POST",body:{blog_id:e,referrer:t??void 0}})}getPopularBlogs(e=10){return this.request(`/analytics/popular${i({limit:e})}`)}}function D(o){return`Bearer ${o.adminApiKey}`}function j(o,e,t){return i({page:e,page_size:t,search:o.search,status:o.status,category_id:o.category_id,tag_id:o.tag_id,published_after:o.published_after,published_before:o.published_before,sort:o.sort,order:o.order})}function y(o){const e={};for(const[t,n]of Object.entries(o))n===void 0||n===""||(e[t]=n);return e}class k{constructor(e){this.config=e}request(e,t={}){return f(this.config.apiUrl,e,D(this.config),{timeoutMs:this.config.timeoutMs,domain:this.config.domain,...t})}async getBlogs(e={}){const t=e.page??1,n=e.page_size??20,s=await this.request(`/blogs${j(e,t,n)}`);return a(s,t,n)}getBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}`)}createBlog(e){return this.request("/blogs",{method:"POST",body:y({...e})})}updateBlog(e,t){return this.request(`/blogs/${encodeURIComponent(e)}`,{method:"PUT",body:y({...t})})}patchBlog(e,t){return this.request(`/blogs/${encodeURIComponent(e)}`,{method:"PATCH",body:y({...t})})}deleteBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}`,{method:"DELETE"})}publishBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}/publish`,{method:"POST"})}unpublishBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}/unpublish`,{method:"POST"})}scheduleBlog(e,t){return this.request(`/blogs/${encodeURIComponent(e)}/schedule`,{method:"POST",body:{scheduled_at:t}})}archiveBlog(e){return this.request(`/blogs/${encodeURIComponent(e)}/archive`,{method:"POST"})}async getCategories(){const e=await this.request("/categories");return Array.isArray(e)?e:a(e,1,100).items}createCategory(e){return this.request("/categories",{method:"POST",body:y({...e})})}updateCategory(e,t){return this.request(`/categories/${encodeURIComponent(e)}`,{method:"PUT",body:t})}deleteCategory(e){return this.request(`/categories/${encodeURIComponent(e)}`,{method:"DELETE"})}async getTags(){const e=await this.request("/tags");return Array.isArray(e)?e:a(e,1,100).items}createTag(e){return this.request("/tags",{method:"POST",body:y({...e})})}updateTag(e,t){return this.request(`/tags/${encodeURIComponent(e)}`,{method:"PUT",body:t})}deleteTag(e){return this.request(`/tags/${encodeURIComponent(e)}`,{method:"DELETE"})}async getMedia(e={}){const t=e.page??1,n=e.page_size??20,s=await this.request(`/media${i({page:t,page_size:n})}`);return a(s,t,n)}async listMediaAssets(e={}){return(await this.getMedia(e)).items}uploadMedia(e){const t=new FormData;return t.append("file",e),this.request("/media",{method:"POST",body:t})}deleteMedia(e){return this.request(`/media/${encodeURIComponent(e)}`,{method:"DELETE"})}}function g(o){const e={};for(const[t,n]of Object.entries(o))n!==void 0&&(e[t]=n);return e}class K{constructor(e){this.config=e}credentials(){return{email:this.config.email,apiKey:this.config.adminApiKey,domain:this.config.domain}}request(e,t={}){return E(this.config.apiUrl,e,this.credentials(),{timeoutMs:this.config.timeoutMs,...t})}me(){return this.request("/admin/admins/me")}async listBrands(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/brands${i({page:t,pageSize:n,status:e.status})}`);return a(s,t,n)}createBrand(e){return this.request("/admin/brands",{method:"POST",body:g(e)})}async listPosts(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/posts${i({page:t,pageSize:n,brandId:e.brandId,status:e.status,q:e.q,categoryId:e.categoryId,includeTrash:e.includeTrash?"true":void 0})}`);return a(s,t,n)}async listPostsAll(e={}){const t=Math.min(e.pageSize??100,100);let n=1;const s=[];let r=0;for(;;){const p=await this.listPosts({...e,page:n,pageSize:t});if(r=p.total,s.push(...p.items),s.length>=r||p.items.length===0)break;n+=1}return{items:s,page:1,pageSize:s.length||t,total:r||s.length}}getPost(e){return this.request(`/admin/posts/${encodeURIComponent(e)}`)}createPost(e){return this.request("/admin/posts",{method:"POST",body:g({...e})})}updatePost(e,t){return this.request(`/admin/posts/${encodeURIComponent(e)}`,{method:"PATCH",body:g({...t})})}deletePost(e,t=!1){return this.request(`/admin/posts/${encodeURIComponent(e)}${i({forever:t?"true":void 0})}`,{method:"DELETE"})}publishPost(e){return this.request(`/admin/posts/${encodeURIComponent(e)}/publish`,{method:"POST"})}unpublishPost(e){return this.request(`/admin/posts/${encodeURIComponent(e)}/unpublish`,{method:"POST"})}async listCategories(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/categories${i({page:t,pageSize:n,brandId:e.brandId,q:e.q,status:e.status})}`);return a(s,t,n)}createCategory(e){return this.request("/admin/categories",{method:"POST",body:g(e)})}updateCategory(e,t){return this.request(`/admin/categories/${encodeURIComponent(e)}`,{method:"PATCH",body:g(t)})}deleteCategory(e,t=!1){return this.request(`/admin/categories/${encodeURIComponent(e)}${i({hard:t?"true":void 0})}`,{method:"DELETE"})}async listComments(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/comments${i({page:t,pageSize:n,brandId:e.brandId,postId:e.postId,status:e.status})}`);return a(s,t,n)}setCommentStatus(e,t){return this.request(`/admin/comments/${encodeURIComponent(e)}/status`,{method:"PATCH",body:{status:t}})}deleteComment(e){return this.request(`/admin/comments/${encodeURIComponent(e)}`,{method:"DELETE"})}async listTags(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/tags${i({page:t,pageSize:n,brandId:e.brandId,q:e.q,status:e.status})}`);return a(s,t,n)}createTag(e){return this.request("/admin/tags",{method:"POST",body:g(e)})}updateTag(e,t){return this.request(`/admin/tags/${encodeURIComponent(e)}`,{method:"PATCH",body:g(t)})}deleteTag(e,t=!1){return this.request(`/admin/tags/${encodeURIComponent(e)}${i({hard:t?"true":void 0})}`,{method:"DELETE"})}async listAuthors(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/authors${i({page:t,pageSize:n,brandId:e.brandId,q:e.q,status:e.status})}`);return a(s,t,n)}createAuthor(e){return this.request("/admin/authors",{method:"POST",body:g(e)})}updateAuthor(e,t){return this.request(`/admin/authors/${encodeURIComponent(e)}`,{method:"PATCH",body:g(t)})}deleteAuthor(e,t=!1){return this.request(`/admin/authors/${encodeURIComponent(e)}${i({hard:t?"true":void 0})}`,{method:"DELETE"})}async listMedia(e={}){const t=e.page??1,n=e.pageSize??100,s=await this.request(`/admin/media${i({page:t,pageSize:n,brandId:e.brandId,q:e.q})}`);return a(s,t,n)}uploadMedia(e,t){const n=new FormData;return n.append("file",e),n.append("brandId",t),this.request("/admin/media",{method:"POST",body:n})}updateMedia(e,t){return this.request(`/admin/media/${encodeURIComponent(e)}`,{method:"PATCH",body:g(t)})}deleteMedia(e){return this.request(`/admin/media/${encodeURIComponent(e)}`,{method:"DELETE"})}async resolveBrandByDomain(e){const t=e.trim().toLowerCase().replace(/^www\./,""),n=await this.listBrands({page:1,pageSize:100});return n.items.find(s=>(s.domain??"").toLowerCase().replace(/^www\./,"")===t)??n.items.find(s=>{try{return new URL(s.url||`https://${s.domain||""}`).hostname.toLowerCase().replace(/^www\./,"")===t}catch{return!1}})??n.items[0]??null}}d.AdminSdkClient=K,d.ApiError=c,d.BlogAdminClient=k,d.BlogClient=M,d.adminSdkRequest=E,d.getErrorMessage=_,d.httpRequest=f,d.isApiError=b,d.isAuthError=B,d.normalizePage=a,d.slugify=v,d.toQuery=i,d.unwrapData=T,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})})(this.BlogPlatform=this.BlogPlatform||{});
@@ -104,21 +104,21 @@ export function AdminPostDetail({ postId, onBack, onEdit: _onEdit, onViewRelated
104
104
  const contentHtml = useMemo(() => injectHeadingIds(sanitized, headings), [sanitized, headings]);
105
105
  const readingMinutes = post?.readingMinutes ?? (post ? estimateReadingMinutes(sanitized) : 0);
106
106
  if (!post) {
107
- return (_jsx("div", { className: "panel", children: _jsxs("div", { className: "panel-body", children: [_jsx("p", { className: "note", children: "Post not found." }), _jsx("button", { type: "button", className: "btn", onClick: onBack, children: "\u2190 Back to posts" })] }) }));
107
+ return (_jsx("div", { className: "blog_panel", children: _jsxs("div", { className: "blog_panel-body", children: [_jsx("p", { className: "blog_note", children: "Post not found." }), _jsx("button", { type: "button", className: "blog_btn", onClick: onBack, children: "\u2190 Back to posts" })] }) }));
108
108
  }
109
109
  const primaryCategory = categories[0] ?? null;
110
110
  const origin = typeof window !== "undefined" ? window.location.origin : "https://example.com";
111
- return (_jsxs("div", { className: "bp-site-article admin-post-detail", children: [_jsx(ReadingProgress, {}), _jsx(ArticleDetailHero, { homeHref: "#", homeLabel: "Blogs", category: primaryCategory ? { slug: primaryCategory.slug, name: primaryCategory.name } : null, title: post.title, excerpt: post.excerpt, publishedAt: post.publishedAt, readingMinutes: readingMinutes, authors: authors,
111
+ return (_jsxs("div", { className: "blog_bp-site-article blog_admin-post-detail", children: [_jsx(ReadingProgress, {}), _jsx(ArticleDetailHero, { homeHref: "#", homeLabel: "Blogs", category: primaryCategory ? { slug: primaryCategory.slug, name: primaryCategory.name } : null, title: post.title, excerpt: post.excerpt, publishedAt: post.publishedAt, readingMinutes: readingMinutes, authors: authors,
112
112
  /* Match /blog/[slug] post-details: blue animated hero, no media strip */
113
- mediaUrl: null, onBack: onBack, onHome: onBack }), _jsxs("main", { className: "wrap article-shell", children: [_jsx(ArticleRailDetails, { className: "rail-details-mobile", owners: authors, related: related, hrefFor: (slug) => `#/blog/${slug}`, onSelect: (slug) => {
113
+ mediaUrl: null, onBack: onBack, onHome: onBack }), _jsxs("main", { className: "blog_wrap blog_article-shell", children: [_jsx(ArticleRailDetails, { className: "blog_rail-details-mobile", owners: authors, related: related, hrefFor: (slug) => `#/blog/${slug}`, onSelect: (slug) => {
114
114
  const hit = store.filterPosts({ state: "all", brandId: post.brandId }).find((p) => p.slug === slug);
115
115
  if (hit && onViewRelated)
116
116
  onViewRelated(hit.id);
117
- } }), _jsxs("article", { className: "article", children: [media ? (_jsx("figure", { className: "article-hero-media", style: { marginBottom: 28, maxWidth: "100%" }, children: _jsx("img", { className: "article-hero-visual", src: media.url, alt: media.alt || post.title }) })) : null, _jsx("div", { className: "prose", dangerouslySetInnerHTML: { __html: contentHtml } }), categories.length > 0 || tags.length > 0 ? (_jsxs("div", { className: "tag-row", children: [categories.map((c) => (_jsx("span", { className: "tag-pill", children: c.name }, c.id))), tags.map((t) => (_jsxs("span", { className: "tag-pill", children: ["#", t.name] }, t.id)))] })) : null, _jsx("div", { className: "share-foot", children: _jsx(ShareRow, { title: post.title, slug: post.slug, origin: origin, path: `/blogs/${post.slug}` }) }), authors.map((a) => (_jsx("div", { className: "author-card", children: _jsxs("div", { children: [_jsxs("p", { className: "author-name", children: [a.name, a.jobTitle ? _jsxs("span", { children: [" \u2014 ", a.jobTitle] }) : null] }), a.bio ? _jsx("p", { className: "author-bio", children: a.bio }) : null] }) }, a.id))), _jsx(CommentSection, { postId: post.id, allowComments: post.allowComments && post.status === "published", initialComments: comments })] }), _jsxs("aside", { className: "rail", children: [_jsx(ArticleRailDetails, { className: "rail-details-desktop", owners: authors, related: related, hrefFor: (slug) => `#/blog/${slug}`, onSelect: (slug) => {
117
+ } }), _jsxs("article", { className: "blog_article", children: [media ? (_jsx("figure", { className: "blog_article-hero-media", style: { marginBottom: 28, maxWidth: "100%" }, children: _jsx("img", { className: "blog_article-hero-visual", src: media.url, alt: media.alt || post.title }) })) : null, _jsx("div", { className: "blog_prose", dangerouslySetInnerHTML: { __html: contentHtml } }), categories.length > 0 || tags.length > 0 ? (_jsxs("div", { className: "blog_tag-row", children: [categories.map((c) => (_jsx("span", { className: "blog_tag-pill", children: c.name }, c.id))), tags.map((t) => (_jsxs("span", { className: "blog_tag-pill", children: ["#", t.name] }, t.id)))] })) : null, _jsx("div", { className: "blog_share-foot", children: _jsx(ShareRow, { title: post.title, slug: post.slug, origin: origin, path: `/blogs/${post.slug}` }) }), authors.map((a) => (_jsx("div", { className: "blog_author-card", children: _jsxs("div", { children: [_jsxs("p", { className: "blog_author-name", children: [a.name, a.jobTitle ? _jsxs("span", { children: [" \u2014 ", a.jobTitle] }) : null] }), a.bio ? _jsx("p", { className: "blog_author-bio", children: a.bio }) : null] }) }, a.id))), _jsx(CommentSection, { postId: post.id, allowComments: post.allowComments && post.status === "published", initialComments: comments })] }), _jsxs("aside", { className: "blog_rail", children: [_jsx(ArticleRailDetails, { className: "blog_rail-details-desktop", owners: authors, related: related, hrefFor: (slug) => `#/blog/${slug}`, onSelect: (slug) => {
118
118
  const hit = store
119
119
  .filterPosts({ state: "all", brandId: post.brandId })
120
120
  .find((p) => p.slug === slug);
121
121
  if (hit && onViewRelated)
122
122
  onViewRelated(hit.id);
123
- } }), headings.length > 1 ? (_jsxs("nav", { className: "toc", "aria-label": "On this page", children: [_jsx("p", { className: "rail-title", children: "On this page" }), _jsx("ol", { children: headings.map((h) => (_jsx("li", { className: h.level === 3 ? "sub" : undefined, children: _jsx("a", { href: `#${h.id}`, children: h.text }) }, h.id))) })] })) : null] })] })] }));
123
+ } }), headings.length > 1 ? (_jsxs("nav", { className: "blog_toc", "aria-label": "On this page", children: [_jsx("p", { className: "blog_rail-title", children: "On this page" }), _jsx("ol", { children: headings.map((h) => (_jsx("li", { className: h.level === 3 ? "blog_sub" : undefined, children: _jsx("a", { href: `#${h.id}`, children: h.text }) }, h.id))) })] })) : null] })] })] }));
124
124
  }
@@ -31,7 +31,7 @@ export function BrandSettings({ brandId, canEdit = true }) {
31
31
  setFlash("Settings saved.");
32
32
  });
33
33
  };
34
- return (_jsxs("fieldset", { className: "settings", disabled: !canEdit, children: [!canEdit ? (_jsx("p", { className: "explain", children: "These are read-only for your account \u2014 changing a brand\u2019s domains changes where its content publishes, so it is a platform-admin job." })) : null, error ? (_jsx("p", { className: "form-error", role: "alert", children: error })) : null, flash ? (_jsx("p", { className: "form-ok", role: "status", children: flash })) : null, _jsxs("div", { className: "grid grid-2", children: [_jsxs("section", { className: "panel", children: [_jsx("div", { className: "panel-head", children: _jsx("h2", { children: "Identity" }) }), _jsxs("div", { className: "panel-body stack", children: [_jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Brand name" }), _jsx("input", { className: "fld", value: form.name, onChange: (e) => set("name", e.target.value) })] }), _jsxs("label", { className: "fld-row", children: [_jsxs("span", { className: "fld-lbl", children: ["Internal slug ", _jsx("em", { children: "used in cache tags and preview URLs" })] }), _jsx("input", { className: "fld mono", value: form.slug, disabled: true, readOnly: true })] }), _jsxs("label", { className: "fld-row", children: [_jsxs("span", { className: "fld-lbl", children: ["Primary domain ", _jsx("em", { children: "canonical URLs are built from this" })] }), _jsx("input", { className: "fld mono", value: form.primaryDomain, onChange: (e) => set("primaryDomain", e.target.value) })] }), _jsxs("div", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Hostnames that resolve to this brand" }), _jsxs("div", { className: "chips", children: [form.domains.map((host) => (_jsxs("span", { className: "token", children: [host, _jsx("button", { type: "button", "aria-label": `Remove ${host}`, onClick: () => set("domains", form.domains.filter((h) => h !== host)), children: "\u2715" })] }, host))), form.domains.length === 0 ? _jsx("span", { className: "note", children: "None yet." }) : null] }), _jsxs("div", { className: "btn-row", children: [_jsx("input", { className: "fld mono", placeholder: "staging.brand.com or localhost:3001", value: hostDraft, onChange: (e) => setHostDraft(e.target.value), onKeyDown: (e) => {
34
+ return (_jsxs("fieldset", { className: "blog_settings", disabled: !canEdit, children: [!canEdit ? (_jsx("p", { className: "blog_explain", children: "These are read-only for your account \u2014 changing a brand\u2019s domains changes where its content publishes, so it is a platform-admin job." })) : 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, _jsxs("div", { className: "blog_grid blog_grid-2", children: [_jsxs("section", { className: "blog_panel", children: [_jsx("div", { className: "blog_panel-head", children: _jsx("h2", { children: "Identity" }) }), _jsxs("div", { className: "blog_panel-body blog_stack", children: [_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Brand name" }), _jsx("input", { className: "blog_fld", value: form.name, onChange: (e) => set("name", e.target.value) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsxs("span", { className: "blog_fld-lbl", children: ["Internal slug ", _jsx("em", { children: "used in cache tags and preview URLs" })] }), _jsx("input", { className: "blog_fld blog_mono", value: form.slug, disabled: true, readOnly: true })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsxs("span", { className: "blog_fld-lbl", children: ["Primary domain ", _jsx("em", { children: "canonical URLs are built from this" })] }), _jsx("input", { className: "blog_fld blog_mono", value: form.primaryDomain, onChange: (e) => set("primaryDomain", e.target.value) })] }), _jsxs("div", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Hostnames that resolve to this brand" }), _jsxs("div", { className: "blog_chips", children: [form.domains.map((host) => (_jsxs("span", { className: "blog_token", children: [host, _jsx("button", { type: "button", "aria-label": `Remove ${host}`, onClick: () => set("domains", form.domains.filter((h) => h !== host)), children: "\u2715" })] }, host))), form.domains.length === 0 ? _jsx("span", { className: "blog_note", children: "None yet." }) : null] }), _jsxs("div", { className: "blog_btn-row", children: [_jsx("input", { className: "blog_fld blog_mono", placeholder: "staging.brand.com or localhost:3001", value: hostDraft, onChange: (e) => setHostDraft(e.target.value), onKeyDown: (e) => {
35
35
  if (e.key === "Enter") {
36
36
  e.preventDefault();
37
37
  if (hostDraft.trim()) {
@@ -39,12 +39,12 @@ export function BrandSettings({ brandId, canEdit = true }) {
39
39
  setHostDraft("");
40
40
  }
41
41
  }
42
- } }), _jsx("button", { type: "button", className: "btn", onClick: () => {
42
+ } }), _jsx("button", { type: "button", className: "blog_btn", onClick: () => {
43
43
  if (!hostDraft.trim())
44
44
  return;
45
45
  set("domains", [...form.domains, hostDraft.trim().toLowerCase()]);
46
46
  setHostDraft("");
47
- }, children: "Add" })] })] })] })] }), _jsxs("section", { className: "panel", children: [_jsx("div", { className: "panel-head", children: _jsx("h2", { children: "Presentation" }) }), _jsxs("div", { className: "panel-body stack", children: [_jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Accent colour" }), _jsxs("span", { className: "colour-row", children: [_jsx("input", { type: "color", className: "colour", value: /^#[0-9a-f]{6}$/i.test(form.accent) ? form.accent : "#2e3a8c", onChange: (e) => set("accent", e.target.value), "aria-label": "Accent colour" }), _jsx("input", { className: "fld mono", value: form.accent, onChange: (e) => set("accent", e.target.value) })] })] }), _jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Body typeface" }), _jsxs("select", { className: "fld", value: form.font, onChange: (e) => set("font", e.target.value), children: [_jsx("option", { value: "serif", children: "Serif" }), _jsx("option", { value: "sans", children: "Sans-serif" })] })] }), _jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Blog index heading" }), _jsx("input", { className: "fld", value: form.blogTitle, onChange: (e) => set("blogTitle", e.target.value) })] }), _jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Blog index description" }), _jsx("textarea", { className: "fld", rows: 2, value: form.blogDescription, onChange: (e) => set("blogDescription", e.target.value) })] })] })] }), _jsxs("section", { className: "panel", children: [_jsx("div", { className: "panel-head", children: _jsx("h2", { children: "Search defaults" }) }), _jsxs("div", { className: "panel-body stack", children: [_jsxs("label", { className: "fld-row", children: [_jsxs("span", { className: "fld-lbl", children: ["Site name ", _jsx("em", { children: "used in Open Graph and Organization schema" })] }), _jsx("input", { className: "fld", value: form.siteName, onChange: (e) => set("siteName", e.target.value) })] }), _jsxs("label", { className: "fld-row", children: [_jsxs("span", { className: "fld-lbl", children: ["Title template ", _jsx("em", { children: "%s is the page title, %site% the site name" })] }), _jsx("input", { className: "fld mono", value: form.titleTemplate, onChange: (e) => set("titleTemplate", e.target.value) })] })] })] }), _jsxs("section", { className: "panel", children: [_jsx("div", { className: "panel-head", children: _jsx("h2", { children: "Measurement" }) }), _jsxs("div", { className: "panel-body stack", children: [_jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "GA4 measurement id" }), _jsx("input", { className: "fld mono", placeholder: "G-XXXXXXXXXX", value: form.ga4MeasurementId, onChange: (e) => set("ga4MeasurementId", e.target.value) })] }), _jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Search Console verification" }), _jsx("input", { className: "fld mono", value: form.gscVerification, onChange: (e) => set("gscVerification", e.target.value) })] })] })] })] }), canEdit ? (_jsxs("div", { className: "save-bar", children: [_jsx("span", { className: "note", children: "Mock save \u2014 changes stay in this session only." }), _jsx("button", { type: "button", className: "btn btn-primary", onClick: submit, disabled: pending, children: pending ? "Saving…" : "Save settings" })] })) : null] }));
47
+ }, children: "Add" })] })] })] })] }), _jsxs("section", { className: "blog_panel", children: [_jsx("div", { className: "blog_panel-head", children: _jsx("h2", { children: "Presentation" }) }), _jsxs("div", { className: "blog_panel-body blog_stack", children: [_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Accent colour" }), _jsxs("span", { className: "blog_colour-row", children: [_jsx("input", { type: "color", className: "blog_colour", value: /^#[0-9a-f]{6}$/i.test(form.accent) ? form.accent : "#2e3a8c", onChange: (e) => set("accent", e.target.value), "aria-label": "Accent colour" }), _jsx("input", { className: "blog_fld blog_mono", value: form.accent, onChange: (e) => set("accent", e.target.value) })] })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Body typeface" }), _jsxs("select", { className: "blog_fld", value: form.font, onChange: (e) => set("font", e.target.value), children: [_jsx("option", { value: "serif", children: "Serif" }), _jsx("option", { value: "sans", children: "Sans-serif" })] })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Blog index heading" }), _jsx("input", { className: "blog_fld", value: form.blogTitle, onChange: (e) => set("blogTitle", e.target.value) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Blog index description" }), _jsx("textarea", { className: "blog_fld", rows: 2, value: form.blogDescription, onChange: (e) => set("blogDescription", e.target.value) })] })] })] }), _jsxs("section", { className: "blog_panel", children: [_jsx("div", { className: "blog_panel-head", children: _jsx("h2", { children: "Search defaults" }) }), _jsxs("div", { className: "blog_panel-body blog_stack", children: [_jsxs("label", { className: "blog_fld-row", children: [_jsxs("span", { className: "blog_fld-lbl", children: ["Site name ", _jsx("em", { children: "used in Open Graph and Organization schema" })] }), _jsx("input", { className: "blog_fld", value: form.siteName, onChange: (e) => set("siteName", e.target.value) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsxs("span", { className: "blog_fld-lbl", children: ["Title template ", _jsx("em", { children: "%s is the page title, %site% the site name" })] }), _jsx("input", { className: "blog_fld blog_mono", value: form.titleTemplate, onChange: (e) => set("titleTemplate", e.target.value) })] })] })] }), _jsxs("section", { className: "blog_panel", children: [_jsx("div", { className: "blog_panel-head", children: _jsx("h2", { children: "Measurement" }) }), _jsxs("div", { className: "blog_panel-body blog_stack", children: [_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "GA4 measurement id" }), _jsx("input", { className: "blog_fld blog_mono", placeholder: "G-XXXXXXXXXX", value: form.ga4MeasurementId, onChange: (e) => set("ga4MeasurementId", e.target.value) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Search Console verification" }), _jsx("input", { className: "blog_fld blog_mono", value: form.gscVerification, onChange: (e) => set("gscVerification", e.target.value) })] })] })] })] }), canEdit ? (_jsxs("div", { className: "blog_save-bar", children: [_jsx("span", { className: "blog_note", children: "Mock save \u2014 changes stay in this session only." }), _jsx("button", { type: "button", className: "blog_btn blog_btn-primary", onClick: submit, disabled: pending, children: pending ? "Saving…" : "Save settings" })] })) : null] }));
48
48
  }
49
49
  export function BrandsList({ onOpenSettings }) {
50
50
  const store = useAdminMock();
@@ -57,7 +57,7 @@ export function BrandsList({ onOpenSettings }) {
57
57
  .reduce((sum, p) => sum + (p.views ?? 0), 0);
58
58
  return { tenant: t, live, drafts, last: latest?.publishedAt ?? null, views };
59
59
  });
60
- return (_jsxs(_Fragment, { children: [_jsxs("p", { className: "explain", children: ["Onboarding a brand is ", _jsx("b", { children: "a row here plus a hostname" }), ", not a code fork. The renderer matches the incoming ", _jsx("span", { className: "mono", children: "Host" }), " header against the hostname list."] }), _jsxs("section", { className: "panel", children: [_jsxs("div", { className: "panel-head", children: [_jsx("h2", { children: "All brands" }), _jsx("span", { className: "hint", children: "views over 30d" })] }), _jsx("div", { className: "tw", children: _jsxs("table", { className: "tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Brand" }), _jsx("th", { className: "num", children: "Live" }), _jsx("th", { className: "num", children: "Drafts" }), _jsx("th", { children: "Last published" }), _jsx("th", { className: "num", children: "Views" })] }) }), _jsx("tbody", { children: rows.map(({ tenant, live, drafts, last, views }) => (_jsxs("tr", { children: [_jsxs("td", { children: [_jsx("button", { type: "button", className: "row-title", onClick: () => onOpenSettings?.(tenant.id), children: tenant.name }), _jsx("span", { className: "cell-sub mono", children: tenant.primaryDomain }), _jsx("div", { className: "row-actions", children: _jsx("button", { type: "button", onClick: () => onOpenSettings?.(tenant.id), children: "Settings" }) })] }), _jsx("td", { className: "num", children: live }), _jsx("td", { className: "num", children: drafts }), _jsx("td", { className: "muted", children: last ? relativeTime(last) : "—" }), _jsx("td", { className: "num", children: views })] }, tenant.id))) })] }) })] })] }));
60
+ return (_jsxs(_Fragment, { children: [_jsxs("p", { className: "blog_explain", children: ["Onboarding a brand is ", _jsx("b", { children: "a row here plus a hostname" }), ", not a code fork. The renderer matches the incoming ", _jsx("span", { className: "blog_mono", children: "Host" }), " header against the hostname list."] }), _jsxs("section", { className: "blog_panel", children: [_jsxs("div", { className: "blog_panel-head", children: [_jsx("h2", { children: "All brands" }), _jsx("span", { className: "blog_hint", children: "views over 30d" })] }), _jsx("div", { className: "blog_tw", children: _jsxs("table", { className: "blog_tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Brand" }), _jsx("th", { className: "blog_num", children: "Live" }), _jsx("th", { className: "blog_num", children: "Drafts" }), _jsx("th", { children: "Last published" }), _jsx("th", { className: "blog_num", children: "Views" })] }) }), _jsx("tbody", { children: rows.map(({ tenant, live, drafts, last, views }) => (_jsxs("tr", { children: [_jsxs("td", { children: [_jsx("button", { type: "button", className: "blog_row-title", onClick: () => onOpenSettings?.(tenant.id), children: tenant.name }), _jsx("span", { className: "blog_cell-sub blog_mono", children: tenant.primaryDomain }), _jsx("div", { className: "blog_row-actions", children: _jsx("button", { type: "button", onClick: () => onOpenSettings?.(tenant.id), children: "Settings" }) })] }), _jsx("td", { className: "blog_num", children: live }), _jsx("td", { className: "blog_num", children: drafts }), _jsx("td", { className: "blog_muted", children: last ? relativeTime(last) : "—" }), _jsx("td", { className: "blog_num", children: views })] }, tenant.id))) })] }) })] })] }));
61
61
  }
62
62
  export function ProfilePanel() {
63
63
  const store = useAdminMock();
@@ -67,5 +67,5 @@ export function ProfilePanel() {
67
67
  .split("-")
68
68
  .map((w) => w[0].toUpperCase() + w.slice(1))
69
69
  .join(" ");
70
- return (_jsxs("section", { className: "panel", children: [_jsx("div", { className: "panel-head", children: _jsx("h2", { children: "Account" }) }), _jsxs("div", { className: "panel-body profile-panel", children: [_jsxs("div", { className: "profile-hero", children: [_jsx("span", { className: "profile-avatar", "aria-hidden": "true", children: (me.name ?? me.email).slice(0, 1).toUpperCase() }), _jsxs("div", { children: [_jsx("p", { className: "profile-name", children: me.name }), _jsx("p", { className: "profile-email mono", children: me.email })] })] }), _jsxs("dl", { className: "facts profile-facts", children: [_jsxs("div", { children: [_jsx("dt", { children: "Platform role" }), _jsx("dd", { children: me.globalRole === "platform-admin" ? "Platform admin" : roleLabel(me.globalRole || "none") })] }), _jsxs("div", { children: [_jsx("dt", { children: "Brands" }), _jsx("dd", { children: store.brands.length })] })] }), self && self.roles.length > 0 ? (_jsxs("div", { className: "profile-roles", children: [_jsx("h3", { children: "Brand access" }), _jsx("ul", { className: "profile-role-list", children: self.roles.map((row) => (_jsxs("li", { children: [_jsx("span", { className: "profile-role-brand", children: row.tenantName }), _jsx("span", { className: "profile-role-level", children: roleLabel(String(row.role)) })] }, `${row.tenant}-${row.role}`))) })] })) : null] })] }));
70
+ return (_jsxs("section", { className: "blog_panel", children: [_jsx("div", { className: "blog_panel-head", children: _jsx("h2", { children: "Account" }) }), _jsxs("div", { className: "blog_panel-body blog_profile-panel", children: [_jsxs("div", { className: "blog_profile-hero", children: [_jsx("span", { className: "blog_profile-avatar", "aria-hidden": "true", children: (me.name ?? me.email).slice(0, 1).toUpperCase() }), _jsxs("div", { children: [_jsx("p", { className: "blog_profile-name", children: me.name }), _jsx("p", { className: "blog_profile-email blog_mono", children: me.email })] })] }), _jsxs("dl", { className: "blog_facts blog_profile-facts", children: [_jsxs("div", { children: [_jsx("dt", { children: "Platform role" }), _jsx("dd", { children: me.globalRole === "platform-admin" ? "Platform admin" : roleLabel(me.globalRole || "none") })] }), _jsxs("div", { children: [_jsx("dt", { children: "Brands" }), _jsx("dd", { children: store.brands.length })] })] }), self && self.roles.length > 0 ? (_jsxs("div", { className: "blog_profile-roles", children: [_jsx("h3", { children: "Brand access" }), _jsx("ul", { className: "blog_profile-role-list", children: self.roles.map((row) => (_jsxs("li", { children: [_jsx("span", { className: "blog_profile-role-brand", children: row.tenantName }), _jsx("span", { className: "blog_profile-role-level", children: roleLabel(String(row.role)) })] }, `${row.tenant}-${row.role}`))) })] })) : null] })] }));
71
71
  }
@@ -8,5 +8,5 @@ export function BrandSwitcher() {
8
8
  return null;
9
9
  const active = store.activeBrand;
10
10
  const label = active?.name ?? "All brands";
11
- return (_jsxs("details", { className: "menu", children: [_jsxs("summary", { className: "btn", children: [_jsx("span", { className: "dot", style: { background: active?.accent || "var(--viz-1)" }, "aria-hidden": "true" }), label, _jsx("span", { className: "caret", "aria-hidden": "true", children: "\u25BE" })] }), _jsxs("div", { className: "menu-panel", children: [_jsx("button", { type: "button", "aria-current": !active ? "true" : undefined, onClick: () => store.setActiveBrandId(null), children: "All brands" }), brands.map((t) => (_jsxs("button", { type: "button", "aria-current": active?.id === t.id ? "true" : undefined, onClick: () => store.setActiveBrandId(t.id), children: [_jsx("span", { className: "dot", style: { background: t.accent || "var(--viz-2)" }, "aria-hidden": "true" }), t.name] }, t.id)))] })] }));
11
+ return (_jsxs("details", { className: "blog_menu", children: [_jsxs("summary", { className: "blog_btn", children: [_jsx("span", { className: "blog_dot", style: { background: active?.accent || "var(--viz-1)" }, "aria-hidden": "true" }), label, _jsx("span", { className: "blog_caret", "aria-hidden": "true", children: "\u25BE" })] }), _jsxs("div", { className: "blog_menu-panel", children: [_jsx("button", { type: "button", "aria-current": !active ? "true" : undefined, onClick: () => store.setActiveBrandId(null), children: "All brands" }), brands.map((t) => (_jsxs("button", { type: "button", "aria-current": active?.id === t.id ? "true" : undefined, onClick: () => store.setActiveBrandId(t.id), children: [_jsx("span", { className: "blog_dot", style: { background: t.accent || "var(--viz-2)" }, "aria-hidden": "true" }), t.name] }, t.id)))] })] }));
12
12
  }
@@ -6,10 +6,10 @@ import { REVIEW_LABELS } from "./lib/publishing";
6
6
  */
7
7
  export const StateChip = ({ status, reviewState, }) => {
8
8
  if (status === "published")
9
- return _jsx("span", { className: "chip chip-live", children: "Live" });
9
+ return _jsx("span", { className: "blog_chip blog_chip-live", children: "Live" });
10
10
  const rs = (reviewState ?? "writing");
11
- const cls = rs === "in-review" ? "chip-review" : rs === "approved" ? "chip-approved" : "chip-writing";
12
- return _jsx("span", { className: `chip ${cls}`, children: REVIEW_LABELS[rs] ?? rs });
11
+ const cls = rs === "in-review" ? "blog_chip-review" : rs === "approved" ? "blog_chip-approved" : "blog_chip-writing";
12
+ return _jsx("span", { className: `blog_chip ${cls}`, children: REVIEW_LABELS[rs] ?? rs });
13
13
  };
14
- export const BrandChip = ({ name }) => (_jsx("span", { className: "chip chip-brand", children: name }));
15
- export const BlockedChip = ({ count }) => (_jsxs("span", { className: "chip chip-blocked", children: [count, " blocker", count === 1 ? "" : "s"] }));
14
+ export const BrandChip = ({ name }) => (_jsx("span", { className: "blog_chip blog_chip-brand", children: name }));
15
+ export const BlockedChip = ({ count }) => (_jsxs("span", { className: "blog_chip blog_chip-blocked", children: [count, " blocker", count === 1 ? "" : "s"] }));
@@ -15,16 +15,16 @@ export function CommentsManager({ onOpenPost }) {
15
15
  alert(res.error);
16
16
  });
17
17
  };
18
- return (_jsxs("section", { className: "panel", children: [_jsxs("div", { className: "panel-head", children: [_jsx("h2", { children: "All comments" }), _jsx("span", { className: "hint", children: rows.length })] }), rows.length === 0 ? (_jsx("p", { className: "d-empty", children: "No comments yet." })) : (_jsx("div", { className: "tw", children: _jsxs("table", { className: "tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Comment" }), _jsx("th", { children: "Post" }), _jsx("th", { children: "Status" }), showBrand ? _jsx("th", { children: "Brand" }) : null, _jsx("th", { children: "Date" })] }) }), _jsx("tbody", { children: rows.map((row) => {
18
+ return (_jsxs("section", { className: "blog_panel", children: [_jsxs("div", { className: "blog_panel-head", children: [_jsx("h2", { children: "All comments" }), _jsx("span", { className: "blog_hint", children: rows.length })] }), rows.length === 0 ? (_jsx("p", { className: "blog_d-empty", children: "No comments yet." })) : (_jsx("div", { className: "blog_tw", children: _jsxs("table", { className: "blog_tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Comment" }), _jsx("th", { children: "Post" }), _jsx("th", { children: "Status" }), showBrand ? _jsx("th", { children: "Brand" }) : null, _jsx("th", { children: "Date" })] }) }), _jsx("tbody", { children: rows.map((row) => {
19
19
  const canModerate = canModerateAll || (row.postOwnerId != null && row.postOwnerId === currentUserId);
20
- return (_jsxs("tr", { children: [_jsxs("td", { children: [_jsx("span", { className: "row-title", children: row.authorName }), row.authorEmail ? _jsx("span", { className: "cell-sub", children: row.authorEmail }) : null, _jsx("span", { className: "cell-sub", style: { whiteSpace: "pre-wrap" }, children: row.body.length > 180 ? `${row.body.slice(0, 180)}…` : row.body }), canModerate ? (_jsxs("div", { className: "row-actions", children: [row.status !== "approved" ? (_jsx("button", { type: "button", disabled: pending, onClick: () => run(() => store.setCommentStatus(row.id, "approved")), children: "Approve" })) : null, row.status !== "rejected" ? (_jsx("button", { type: "button", disabled: pending, onClick: () => run(() => store.setCommentStatus(row.id, "rejected")), children: "Reject" })) : null, canModerateAll ? (_jsx("button", { type: "button", className: "danger", disabled: pending, onClick: () => {
20
+ return (_jsxs("tr", { children: [_jsxs("td", { children: [_jsx("span", { className: "blog_row-title", children: row.authorName }), row.authorEmail ? _jsx("span", { className: "blog_cell-sub", children: row.authorEmail }) : null, _jsx("span", { className: "blog_cell-sub", style: { whiteSpace: "pre-wrap" }, children: row.body.length > 180 ? `${row.body.slice(0, 180)}…` : row.body }), canModerate ? (_jsxs("div", { className: "blog_row-actions", children: [row.status !== "approved" ? (_jsx("button", { type: "button", disabled: pending, onClick: () => run(() => store.setCommentStatus(row.id, "approved")), children: "Approve" })) : null, row.status !== "rejected" ? (_jsx("button", { type: "button", disabled: pending, onClick: () => run(() => store.setCommentStatus(row.id, "rejected")), children: "Reject" })) : null, canModerateAll ? (_jsx("button", { type: "button", className: "blog_danger", disabled: pending, onClick: () => {
21
21
  if (confirm("Delete this comment permanently?")) {
22
22
  run(() => store.deleteComment(row.id));
23
23
  }
24
- }, children: "Delete" })) : null] })) : null] }), _jsx("td", { children: row.postId ? (_jsx("button", { type: "button", className: "btn btn-ghost btn-sm", onClick: () => onOpenPost?.(row.postId), children: row.postTitle || "Post" })) : ("—") }), _jsx("td", { children: _jsx("span", { className: `chip ${row.status === "approved"
25
- ? "chip-approved"
24
+ }, children: "Delete" })) : null] })) : null] }), _jsx("td", { children: row.postId ? (_jsx("button", { type: "button", className: "blog_btn blog_btn-ghost blog_btn-sm", onClick: () => onOpenPost?.(row.postId), children: row.postTitle || "Post" })) : ("—") }), _jsx("td", { children: _jsx("span", { className: `blog_chip ${row.status === "approved"
25
+ ? "blog_chip-approved"
26
26
  : row.status === "pending"
27
- ? "chip-writing"
28
- : "chip-writing"}`, children: row.status }) }), showBrand ? _jsx("td", { children: row.brandName || "—" }) : null, _jsx("td", { className: "mono", children: new Date(row.createdAt).toLocaleString() })] }, row.id));
27
+ ? "blog_chip-writing"
28
+ : "blog_chip-writing"}`, children: row.status }) }), showBrand ? _jsx("td", { children: row.brandName || "—" }) : null, _jsx("td", { className: "blog_mono", children: new Date(row.createdAt).toLocaleString() })] }, row.id));
29
29
  }) })] }) }))] }));
30
30
  }
@@ -30,5 +30,5 @@ export function Dashboard() {
30
30
  { label: "Tech review", value: 0, tone: 3 },
31
31
  { label: "Approved", value: 1, tone: 4 }
32
32
  ];
33
- return (_jsxs(_Fragment, { children: [_jsxs("section", { className: "kpis", children: [_jsxs("div", { className: "kpi", children: [_jsxs("span", { className: "kpi-l", children: ["Views \u00B7 ", range, "d"] }), _jsxs("div", { className: "kpi-row", children: [_jsx("span", { className: "kpi-n", children: totalViews }), _jsx(Sparkline, { data: series.map((d) => d.views) })] }), _jsxs("div", { className: "kpi-delta", children: [_jsx("span", { className: "up", children: "\u2191 366.7%" }), " vs previous 30d"] })] }), _jsxs("div", { className: "kpi", children: [_jsx("span", { className: "kpi-l", children: "Unique readers" }), _jsx("div", { className: "kpi-row", children: _jsx("span", { className: "kpi-n", children: "14" }) }), _jsx("span", { className: "kpi-sub", children: "1.00 views per reader" })] }), _jsxs("div", { className: "kpi", children: [_jsx("span", { className: "kpi-l", children: "Needs attention" }), _jsx("div", { className: "kpi-row", children: _jsx("span", { className: "kpi-n", children: "0" }) }), _jsx("span", { className: "kpi-sub", children: "in review, or approved but blocked" })] }), _jsxs("div", { className: "kpi", children: [_jsx("span", { className: "kpi-l", children: "Published" }), _jsx("div", { className: "kpi-row", children: _jsx("span", { className: "kpi-n", children: "8" }) }), _jsx("span", { className: "kpi-sub", children: "0 scheduled next" })] })] }), _jsxs("section", { className: "grid grid-2", children: [_jsxs("div", { className: "panel", children: [_jsxs("div", { className: "panel-head", children: [_jsx("h2", { children: "Views per day" }), _jsx("span", { className: "hint", children: "hover for a day" })] }), _jsx("div", { className: "panel-body flush", children: _jsx(TrendArea, { data: series }) })] }), _jsxs("div", { className: "panel", children: [_jsxs("div", { className: "panel-head", children: [_jsx("h2", { children: "Editorial pipeline" }), _jsx("span", { className: "hint", children: "4 posts in progress" })] }), _jsx("div", { className: "panel-body", children: _jsx(Funnel, { stages: funnelStages }) })] })] }), _jsxs("section", { className: "grid grid-3", children: [_jsxs("div", { className: "panel", children: [_jsxs("div", { className: "panel-head", children: [_jsx("h2", { children: "Most read" }), _jsxs("span", { className: "hint", children: [range, "d"] })] }), _jsx("div", { className: "panel-body", children: _jsx(BarList, { items: top, colorVar: "var(--viz-1)" }) })] }), _jsxs("div", { className: "panel", children: [_jsx("div", { className: "panel-head", children: _jsx("h2", { children: "Published by category" }) }), _jsx("div", { className: "panel-body", children: _jsx(BarList, { items: categories, colorVar: "var(--viz-3)" }) })] }), _jsxs("div", { className: "panel", children: [_jsxs("div", { className: "panel-head", children: [_jsx("h2", { children: "Scheduled" }), _jsx("span", { className: "hint", children: "0" })] }), _jsx("div", { className: "panel-body", children: _jsx("p", { className: "d-empty", children: "Nothing scheduled." }) })] })] }), _jsxs("section", { className: "grid grid-2", children: [_jsxs("div", { className: "panel", children: [_jsxs("div", { className: "panel-head", children: [_jsx("h2", { children: "By brand" }), _jsxs("span", { className: "hint", children: ["views over ", range, "d"] })] }), _jsx("div", { className: "panel-body flush", children: _jsx("div", { className: "tw", children: _jsxs("table", { className: "tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Brand" }), _jsx("th", { children: "Views" }), _jsx("th", { className: "num", children: "Live" }), _jsx("th", { className: "num", children: "Drafts" }), _jsx("th", { className: "num", children: "Last published" })] }) }), _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { children: _jsx("a", { href: "/admin?brand=halcyon", children: "Halcyon Health" }) }), _jsx("td", { children: _jsxs("div", { className: "bar-cell", children: [_jsx("span", { className: "bar-cell-n", children: "9" }), _jsx("span", { className: "mini-track", children: _jsx("span", { className: "mini-fill", style: { width: '100%' } }) })] }) }), _jsx("td", { className: "num", children: "6" }), _jsx("td", { className: "num", children: "1" }), _jsx("td", { className: "num", children: "2d ago" })] }), _jsxs("tr", { children: [_jsx("td", { children: _jsx("a", { href: "/admin?brand=northbeam", children: "Northbeam" }) }), _jsx("td", { children: _jsxs("div", { className: "bar-cell", children: [_jsx("span", { className: "bar-cell-n", children: "5" }), _jsx("span", { className: "mini-track", children: _jsx("span", { className: "mini-fill", style: { width: '55%' } }) })] }) }), _jsx("td", { className: "num", children: "2" }), _jsx("td", { className: "num", children: "0" }), _jsx("td", { className: "num", children: "1mo ago" })] })] })] }) }) })] }), _jsxs("div", { className: "panel", children: [_jsx("div", { className: "panel-head", children: _jsx("h2", { children: "Recently published" }) }), _jsxs("div", { className: "panel-body flush", children: [_jsxs("div", { className: "row", children: [_jsxs("div", { className: "row-main", children: [_jsx("a", { href: "#", className: "t", children: "How to choose the best app development company in the middle east?" }), _jsxs("div", { className: "row-sub", children: [_jsx("span", { children: "2d ago" }), _jsx("a", { href: "#", children: "View \u2192" })] })] }), _jsx("div", { className: "row-right", children: _jsx("span", { className: "chip chip-brand", children: "Halcyon Health" }) })] }), _jsxs("div", { className: "row", children: [_jsxs("div", { className: "row-main", children: [_jsx("a", { href: "#", className: "t", children: "Hello this is jason speaking how may i help you" }), _jsxs("div", { className: "row-sub", children: [_jsx("span", { children: "16d ago" }), _jsx("a", { href: "#", children: "View \u2192" })] })] }), _jsx("div", { className: "row-right", children: _jsx("span", { className: "chip chip-brand", children: "Halcyon Health" }) })] })] })] })] }), _jsx("p", { className: "note", children: "Showing 2 brands \u2014 the ones your account has access to. Numbers come from the tracking beacon on published articles." })] }));
33
+ return (_jsxs(_Fragment, { children: [_jsxs("section", { className: "blog_kpis", children: [_jsxs("div", { className: "blog_kpi", children: [_jsxs("span", { className: "blog_kpi-l", children: ["Views \u00B7 ", range, "d"] }), _jsxs("div", { className: "blog_kpi-row", children: [_jsx("span", { className: "blog_kpi-n", children: totalViews }), _jsx(Sparkline, { data: series.map((d) => d.views) })] }), _jsxs("div", { className: "blog_kpi-delta", children: [_jsx("span", { className: "blog_up", children: "\u2191 366.7%" }), " vs previous 30d"] })] }), _jsxs("div", { className: "blog_kpi", children: [_jsx("span", { className: "blog_kpi-l", children: "Unique readers" }), _jsx("div", { className: "blog_kpi-row", children: _jsx("span", { className: "blog_kpi-n", children: "14" }) }), _jsx("span", { className: "blog_kpi-sub", children: "1.00 views per reader" })] }), _jsxs("div", { className: "blog_kpi", children: [_jsx("span", { className: "blog_kpi-l", children: "Needs attention" }), _jsx("div", { className: "blog_kpi-row", children: _jsx("span", { className: "blog_kpi-n", children: "0" }) }), _jsx("span", { className: "blog_kpi-sub", children: "in review, or approved but blocked" })] }), _jsxs("div", { className: "blog_kpi", children: [_jsx("span", { className: "blog_kpi-l", children: "Published" }), _jsx("div", { className: "blog_kpi-row", children: _jsx("span", { className: "blog_kpi-n", children: "8" }) }), _jsx("span", { className: "blog_kpi-sub", children: "0 scheduled next" })] })] }), _jsxs("section", { className: "blog_grid blog_grid-2", children: [_jsxs("div", { className: "blog_panel", children: [_jsxs("div", { className: "blog_panel-head", children: [_jsx("h2", { children: "Views per day" }), _jsx("span", { className: "blog_hint", children: "hover for a day" })] }), _jsx("div", { className: "blog_panel-body blog_flush", children: _jsx(TrendArea, { data: series }) })] }), _jsxs("div", { className: "blog_panel", children: [_jsxs("div", { className: "blog_panel-head", children: [_jsx("h2", { children: "Editorial pipeline" }), _jsx("span", { className: "blog_hint", children: "4 posts in progress" })] }), _jsx("div", { className: "blog_panel-body", children: _jsx(Funnel, { stages: funnelStages }) })] })] }), _jsxs("section", { className: "blog_grid blog_grid-3", children: [_jsxs("div", { className: "blog_panel", children: [_jsxs("div", { className: "blog_panel-head", children: [_jsx("h2", { children: "Most read" }), _jsxs("span", { className: "blog_hint", children: [range, "d"] })] }), _jsx("div", { className: "blog_panel-body", children: _jsx(BarList, { items: top, colorVar: "var(--viz-1)" }) })] }), _jsxs("div", { className: "blog_panel", children: [_jsx("div", { className: "blog_panel-head", children: _jsx("h2", { children: "Published by category" }) }), _jsx("div", { className: "blog_panel-body", children: _jsx(BarList, { items: categories, colorVar: "var(--viz-3)" }) })] }), _jsxs("div", { className: "blog_panel", children: [_jsxs("div", { className: "blog_panel-head", children: [_jsx("h2", { children: "Scheduled" }), _jsx("span", { className: "blog_hint", children: "0" })] }), _jsx("div", { className: "blog_panel-body", children: _jsx("p", { className: "blog_d-empty", children: "Nothing scheduled." }) })] })] }), _jsxs("section", { className: "blog_grid blog_grid-2", children: [_jsxs("div", { className: "blog_panel", children: [_jsxs("div", { className: "blog_panel-head", children: [_jsx("h2", { children: "By brand" }), _jsxs("span", { className: "blog_hint", children: ["views over ", range, "d"] })] }), _jsx("div", { className: "blog_panel-body blog_flush", children: _jsx("div", { className: "blog_tw", children: _jsxs("table", { className: "blog_tbl", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Brand" }), _jsx("th", { children: "Views" }), _jsx("th", { className: "blog_num", children: "Live" }), _jsx("th", { className: "blog_num", children: "Drafts" }), _jsx("th", { className: "blog_num", children: "Last published" })] }) }), _jsxs("tbody", { children: [_jsxs("tr", { children: [_jsx("td", { children: _jsx("a", { href: "/admin?brand=halcyon", children: "Halcyon Health" }) }), _jsx("td", { children: _jsxs("div", { className: "blog_bar-cell", children: [_jsx("span", { className: "blog_bar-cell-n", children: "9" }), _jsx("span", { className: "blog_mini-track", children: _jsx("span", { className: "blog_mini-fill", style: { width: '100%' } }) })] }) }), _jsx("td", { className: "blog_num", children: "6" }), _jsx("td", { className: "blog_num", children: "1" }), _jsx("td", { className: "blog_num", children: "2d ago" })] }), _jsxs("tr", { children: [_jsx("td", { children: _jsx("a", { href: "/admin?brand=northbeam", children: "Northbeam" }) }), _jsx("td", { children: _jsxs("div", { className: "blog_bar-cell", children: [_jsx("span", { className: "blog_bar-cell-n", children: "5" }), _jsx("span", { className: "blog_mini-track", children: _jsx("span", { className: "blog_mini-fill", style: { width: '55%' } }) })] }) }), _jsx("td", { className: "blog_num", children: "2" }), _jsx("td", { className: "blog_num", children: "0" }), _jsx("td", { className: "blog_num", children: "1mo ago" })] })] })] }) }) })] }), _jsxs("div", { className: "blog_panel", children: [_jsx("div", { className: "blog_panel-head", children: _jsx("h2", { children: "Recently published" }) }), _jsxs("div", { className: "blog_panel-body blog_flush", children: [_jsxs("div", { className: "blog_row", children: [_jsxs("div", { className: "blog_row-main", children: [_jsx("a", { href: "#", className: "blog_t", children: "How to choose the best app development company in the middle east?" }), _jsxs("div", { className: "blog_row-sub", children: [_jsx("span", { children: "2d ago" }), _jsx("a", { href: "#", children: "View \u2192" })] })] }), _jsx("div", { className: "blog_row-right", children: _jsx("span", { className: "blog_chip blog_chip-brand", children: "Halcyon Health" }) })] }), _jsxs("div", { className: "blog_row", children: [_jsxs("div", { className: "blog_row-main", children: [_jsx("a", { href: "#", className: "blog_t", children: "Hello this is jason speaking how may i help you" }), _jsxs("div", { className: "blog_row-sub", children: [_jsx("span", { children: "16d ago" }), _jsx("a", { href: "#", children: "View \u2192" })] })] }), _jsx("div", { className: "blog_row-right", children: _jsx("span", { className: "blog_chip blog_chip-brand", children: "Halcyon Health" }) })] })] })] })] }), _jsx("p", { className: "blog_note", children: "Showing 2 brands \u2014 the ones your account has access to. Numbers come from the tracking beacon on published articles." })] }));
34
34
  }
@@ -109,11 +109,11 @@ export function MediaLibrary() {
109
109
  });
110
110
  });
111
111
  };
112
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "filterbar", children: [_jsx("input", { className: "fld", type: "search", value: query, placeholder: "Search alt text or filename\u2026", "aria-label": "Search images", onChange: (e) => setQuery(e.target.value) }), _jsx("button", { type: "button", className: "btn btn-primary", onClick: () => fileRef.current?.click(), disabled: !!uploading, children: uploading ? `Uploading ${uploading.done}/${uploading.total}…` : "+ Upload images" }), _jsxs("span", { className: "note", children: [count, " image", count === 1 ? "" : "s", " \u00B7 ", tenant ? tenantName : "all brands"] })] }), _jsx("input", { ref: fileRef, type: "file", accept: "image/jpeg,image/png,image/webp,image/avif,image/gif", multiple: true, hidden: true, onChange: (e) => {
112
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "blog_filterbar", children: [_jsx("input", { className: "blog_fld", type: "search", value: query, placeholder: "Search alt text or filename\u2026", "aria-label": "Search images", onChange: (e) => setQuery(e.target.value) }), _jsx("button", { type: "button", className: "blog_btn blog_btn-primary", onClick: () => fileRef.current?.click(), disabled: !!uploading, children: uploading ? `Uploading ${uploading.done}/${uploading.total}…` : "+ Upload images" }), _jsxs("span", { className: "blog_note", children: [count, " image", count === 1 ? "" : "s", " \u00B7 ", tenant ? tenantName : "all brands"] })] }), _jsx("input", { ref: fileRef, type: "file", accept: "image/jpeg,image/png,image/webp,image/avif,image/gif", multiple: true, hidden: true, onChange: (e) => {
113
113
  if (e.target.files?.length)
114
114
  void upload(e.target.files);
115
115
  e.target.value = "";
116
- } }), error ? (_jsx("p", { className: "form-error", role: "alert", children: error })) : null, flash ? (_jsx("p", { className: "form-ok", role: "status", children: flash })) : null, _jsx("p", { className: "explain", children: "Uploaded images are stored on the Blog Platform API and shown here for every brand." }), _jsxs("section", { className: `panel dropzone${dragging ? " is-dragging" : ""}`, onDragOver: (e) => {
116
+ } }), 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("p", { className: "blog_explain", children: "Uploaded images are stored on the Blog Platform API and shown here for every brand." }), _jsxs("section", { className: `blog_panel blog_dropzone${dragging ? " blog_is-dragging" : ""}`, onDragOver: (e) => {
117
117
  e.preventDefault();
118
118
  setDragging(true);
119
119
  }, onDragLeave: () => setDragging(false), onDrop: (e) => {
@@ -121,14 +121,14 @@ export function MediaLibrary() {
121
121
  setDragging(false);
122
122
  if (e.dataTransfer.files?.length)
123
123
  void upload(e.dataTransfer.files);
124
- }, children: [items.length === 0 && !loading ? (_jsx("p", { className: "d-empty", children: "Nothing here yet \u2014 drop images anywhere on this panel to upload." })) : (_jsx("div", { className: "media-grid", children: items.map((item) => (_jsxs("button", { type: "button", className: "media-card", onClick: () => openDetail(item), children: [item.thumbUrl || item.url ? (_jsx("img", { src: item.thumbUrl || item.url, alt: "", loading: "lazy" })) : (_jsx("span", { className: "pick-blank" })), _jsxs("span", { className: "media-meta", children: [_jsx("span", { className: "media-alt", children: item.alt || item.filename || "Image" }), _jsxs("span", { className: "media-sub", children: [item.width && item.height ? `${item.width}×${item.height}` : "—", " \u00B7 ", kb(item.filesize)] })] })] }, item.id))) })), hasMore ? (_jsx("div", { className: "modal-more", children: _jsx("button", { type: "button", className: "btn", disabled: loading, onClick: () => {
124
+ }, children: [items.length === 0 && !loading ? (_jsx("p", { className: "blog_d-empty", children: "Nothing here yet \u2014 drop images anywhere on this panel to upload." })) : (_jsx("div", { className: "blog_media-grid", children: items.map((item) => (_jsxs("button", { type: "button", className: "blog_media-card", onClick: () => openDetail(item), children: [item.thumbUrl || item.url ? (_jsx("img", { src: item.thumbUrl || item.url, alt: "", loading: "lazy" })) : (_jsx("span", { className: "blog_pick-blank" })), _jsxs("span", { className: "blog_media-meta", children: [_jsx("span", { className: "blog_media-alt", children: item.alt || item.filename || "Image" }), _jsxs("span", { className: "blog_media-sub", children: [item.width && item.height ? `${item.width}×${item.height}` : "—", " \u00B7 ", kb(item.filesize)] })] })] }, item.id))) })), hasMore ? (_jsx("div", { className: "blog_modal-more", children: _jsx("button", { type: "button", className: "blog_btn", disabled: loading, onClick: () => {
125
125
  const next = page + 1;
126
126
  setPage(next);
127
127
  load(next, query, true);
128
- }, children: loading ? "Loading…" : "Load more" }) })) : null] }), open ? (_jsx("div", { className: "modal-backdrop", onMouseDown: () => setOpen(null), children: _jsxs("div", { className: "modal", role: "dialog", "aria-modal": "true", "aria-label": "Image details", onMouseDown: (e) => e.stopPropagation(), children: [_jsxs("header", { className: "modal-head", children: [_jsx("h2", { children: "Image details" }), _jsx("button", { type: "button", className: "icon-btn", onClick: () => setOpen(null), "aria-label": "Close", children: "\u2715" })] }), _jsxs("div", { className: "modal-body detail", children: [_jsx("img", { className: "detail-img", src: open.url || open.thumbUrl, alt: open.alt }), _jsxs("div", { className: "stack", children: [_jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Alt text \u2014 what a screen reader reads out" }), _jsx("textarea", { className: "fld", rows: 2, value: meta.alt, onChange: (e) => {
128
+ }, children: loading ? "Loading…" : "Load more" }) })) : null] }), open ? (_jsx("div", { className: "blog_modal-backdrop", onMouseDown: () => setOpen(null), children: _jsxs("div", { className: "blog_modal", role: "dialog", "aria-modal": "true", "aria-label": "Image details", onMouseDown: (e) => e.stopPropagation(), children: [_jsxs("header", { className: "blog_modal-head", children: [_jsx("h2", { children: "Image details" }), _jsx("button", { type: "button", className: "blog_icon-btn", onClick: () => setOpen(null), "aria-label": "Close", children: "\u2715" })] }), _jsxs("div", { className: "blog_modal-body blog_detail", children: [_jsx("img", { className: "blog_detail-img", src: open.url || open.thumbUrl, alt: open.alt }), _jsxs("div", { className: "blog_stack", children: [_jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Alt text \u2014 what a screen reader reads out" }), _jsx("textarea", { className: "blog_fld", rows: 2, value: meta.alt, onChange: (e) => {
129
129
  setMeta((m) => ({ ...m, alt: e.target.value }));
130
130
  setSaved(false);
131
- } })] }), _jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Caption" }), _jsx("input", { className: "fld", value: meta.caption, onChange: (e) => setMeta((m) => ({ ...m, caption: e.target.value })) })] }), _jsxs("label", { className: "fld-row", children: [_jsx("span", { className: "fld-lbl", children: "Credit" }), _jsx("input", { className: "fld", value: meta.credit, onChange: (e) => setMeta((m) => ({ ...m, credit: e.target.value })) })] }), _jsxs("dl", { className: "facts", children: [_jsxs("div", { children: [_jsx("dt", { children: "Dimensions" }), _jsx("dd", { children: open.width && open.height ? `${open.width}×${open.height}` : "—" })] }), _jsxs("div", { children: [_jsx("dt", { children: "Size" }), _jsx("dd", { children: kb(open.filesize) })] }), _jsxs("div", { children: [_jsx("dt", { children: "File" }), _jsx("dd", { className: "mono trunc", children: open.filename || open.url })] })] })] })] }), _jsxs("footer", { className: "modal-foot", children: [_jsx("button", { type: "button", className: "btn-danger-link", onClick: removeImage, disabled: saving, children: "Delete image" }), _jsxs("div", { className: "modal-foot-actions", children: [saved ? _jsx("span", { className: "form-ok inline", children: "Saved" }) : null, _jsx("button", { type: "button", className: "btn", onClick: () => setOpen(null), children: "Close" }), _jsx("button", { type: "button", className: "btn btn-primary", onClick: saveMeta, disabled: saving || !meta.alt.trim(), children: saving ? "Saving…" : "Save details" })] })] })] }) })) : null] }));
131
+ } })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Caption" }), _jsx("input", { className: "blog_fld", value: meta.caption, onChange: (e) => setMeta((m) => ({ ...m, caption: e.target.value })) })] }), _jsxs("label", { className: "blog_fld-row", children: [_jsx("span", { className: "blog_fld-lbl", children: "Credit" }), _jsx("input", { className: "blog_fld", value: meta.credit, onChange: (e) => setMeta((m) => ({ ...m, credit: e.target.value })) })] }), _jsxs("dl", { className: "blog_facts", children: [_jsxs("div", { children: [_jsx("dt", { children: "Dimensions" }), _jsx("dd", { children: open.width && open.height ? `${open.width}×${open.height}` : "—" })] }), _jsxs("div", { children: [_jsx("dt", { children: "Size" }), _jsx("dd", { children: kb(open.filesize) })] }), _jsxs("div", { children: [_jsx("dt", { children: "File" }), _jsx("dd", { className: "blog_mono blog_trunc", children: open.filename || open.url })] })] })] })] }), _jsxs("footer", { className: "blog_modal-foot", children: [_jsx("button", { type: "button", className: "blog_btn-danger-link", onClick: removeImage, disabled: saving, children: "Delete image" }), _jsxs("div", { className: "blog_modal-foot-actions", children: [saved ? _jsx("span", { className: "blog_form-ok blog_inline", children: "Saved" }) : null, _jsx("button", { type: "button", className: "blog_btn", onClick: () => setOpen(null), children: "Close" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-primary", onClick: saveMeta, disabled: saving || !meta.alt.trim(), children: saving ? "Saving…" : "Save details" })] })] })] }) })) : null] }));
132
132
  }
133
133
  /** @deprecated Prefer MediaLibrary — kept for export compatibility. */
134
134
  export function MediaManager() {
@@ -72,11 +72,11 @@ export function MediaPicker({ open, brandId, onClose, onSelect, title = "Choose
72
72
  };
73
73
  if (!open)
74
74
  return null;
75
- return (_jsx("div", { className: "modal-backdrop", onMouseDown: onClose, children: _jsxs("div", { className: "modal modal-wide", role: "dialog", "aria-modal": "true", "aria-label": title, onMouseDown: (e) => e.stopPropagation(), children: [_jsxs("header", { className: "modal-head", children: [_jsx("h2", { children: title }), _jsxs("div", { className: "modal-head-actions", children: [_jsx("input", { className: "fld", type: "search", placeholder: "Search alt text or filename\u2026", value: query, onChange: (e) => setQuery(e.target.value) }), _jsx("button", { type: "button", className: "btn", onClick: () => fileRef.current?.click(), disabled: uploading, children: uploading ? "Uploading…" : "Upload" }), _jsx("button", { type: "button", className: "icon-btn", onClick: onClose, "aria-label": "Close", children: "\u2715" })] })] }), _jsx("input", { ref: fileRef, type: "file", accept: "image/jpeg,image/png,image/webp,image/avif,image/gif", multiple: true, hidden: true, onChange: (e) => {
75
+ return (_jsx("div", { className: "blog_modal-backdrop", onMouseDown: onClose, children: _jsxs("div", { className: "blog_modal blog_modal-wide", role: "dialog", "aria-modal": "true", "aria-label": title, onMouseDown: (e) => e.stopPropagation(), children: [_jsxs("header", { className: "blog_modal-head", children: [_jsx("h2", { children: title }), _jsxs("div", { className: "blog_modal-head-actions", children: [_jsx("input", { className: "blog_fld", type: "search", placeholder: "Search alt text or filename\u2026", value: query, onChange: (e) => setQuery(e.target.value) }), _jsx("button", { type: "button", className: "blog_btn", onClick: () => fileRef.current?.click(), disabled: uploading, children: uploading ? "Uploading…" : "Upload" }), _jsx("button", { type: "button", className: "blog_icon-btn", onClick: onClose, "aria-label": "Close", children: "\u2715" })] })] }), _jsx("input", { ref: fileRef, type: "file", accept: "image/jpeg,image/png,image/webp,image/avif,image/gif", multiple: true, hidden: true, onChange: (e) => {
76
76
  if (e.target.files?.length)
77
77
  void upload(e.target.files);
78
78
  e.target.value = "";
79
- } }), error ? (_jsx("p", { className: "form-error", role: "alert", children: error })) : null, _jsxs("div", { className: `modal-body dropzone${dragging ? " is-dragging" : ""}`, onDragOver: (e) => {
79
+ } }), error ? (_jsx("p", { className: "blog_form-error", role: "alert", children: error })) : null, _jsxs("div", { className: `blog_modal-body blog_dropzone${dragging ? " blog_is-dragging" : ""}`, onDragOver: (e) => {
80
80
  e.preventDefault();
81
81
  setDragging(true);
82
82
  }, onDragLeave: () => setDragging(false), onDrop: (e) => {
@@ -84,9 +84,9 @@ export function MediaPicker({ open, brandId, onClose, onSelect, title = "Choose
84
84
  setDragging(false);
85
85
  if (e.dataTransfer.files?.length)
86
86
  void upload(e.dataTransfer.files);
87
- }, children: [items.length === 0 && !loading ? (_jsx("p", { className: "d-empty", children: "Nothing in the library yet. Drop an image here, or press Upload." })) : (_jsx("div", { className: "pick-grid", children: items.map((item) => (_jsxs("button", { type: "button", className: `pick${selected?.id === item.id ? " is-picked" : ""}`, onClick: () => setSelected(item), onDoubleClick: () => onSelect(item), children: [item.thumbUrl || item.url ? (_jsx("img", { src: item.thumbUrl || item.url, alt: "", loading: "lazy" })) : (_jsx("span", { className: "pick-blank" })), _jsxs("span", { className: "pick-meta", children: [_jsx("span", { className: `pick-alt${item.alt ? "" : " is-missing"}`, children: item.alt || "Image" }), _jsxs("span", { className: "pick-sub", children: [item.width && item.height ? `${item.width}×${item.height}` : "—", " \u00B7", " ", kb(item.filesize)] })] })] }, item.id))) })), hasMore ? (_jsx("div", { className: "modal-more", children: _jsx("button", { type: "button", className: "btn", disabled: loading, onClick: () => {
87
+ }, children: [items.length === 0 && !loading ? (_jsx("p", { className: "blog_d-empty", children: "Nothing in the library yet. Drop an image here, or press Upload." })) : (_jsx("div", { className: "blog_pick-grid", children: items.map((item) => (_jsxs("button", { type: "button", className: `blog_pick${selected?.id === item.id ? " blog_is-picked" : ""}`, onClick: () => setSelected(item), onDoubleClick: () => onSelect(item), children: [item.thumbUrl || item.url ? (_jsx("img", { src: item.thumbUrl || item.url, alt: "", loading: "lazy" })) : (_jsx("span", { className: "blog_pick-blank" })), _jsxs("span", { className: "blog_pick-meta", children: [_jsx("span", { className: `blog_pick-alt${item.alt ? "" : " blog_is-missing"}`, children: item.alt || "Image" }), _jsxs("span", { className: "blog_pick-sub", children: [item.width && item.height ? `${item.width}×${item.height}` : "—", " \u00B7", " ", kb(item.filesize)] })] })] }, item.id))) })), hasMore ? (_jsx("div", { className: "blog_modal-more", children: _jsx("button", { type: "button", className: "blog_btn", disabled: loading, onClick: () => {
88
88
  const next = page + 1;
89
89
  setPage(next);
90
90
  load(next, query, true);
91
- }, children: loading ? "Loading…" : "Load more" }) })) : null] }), _jsxs("footer", { className: "modal-foot", children: [_jsxs("span", { className: "note", children: [total, " image", total === 1 ? "" : "s", " \u00B7 drag files anywhere in this window to upload"] }), _jsxs("div", { className: "modal-foot-actions", children: [_jsx("button", { type: "button", className: "btn", onClick: onClose, children: "Cancel" }), _jsx("button", { type: "button", className: "btn btn-primary", disabled: !selected, onClick: () => selected && onSelect(selected), children: "Use this image" })] })] })] }) }));
91
+ }, children: loading ? "Loading…" : "Load more" }) })) : null] }), _jsxs("footer", { className: "blog_modal-foot", children: [_jsxs("span", { className: "blog_note", children: [total, " image", total === 1 ? "" : "s", " \u00B7 drag files anywhere in this window to upload"] }), _jsxs("div", { className: "blog_modal-foot-actions", children: [_jsx("button", { type: "button", className: "blog_btn", onClick: onClose, children: "Cancel" }), _jsx("button", { type: "button", className: "blog_btn blog_btn-primary", disabled: !selected, onClick: () => selected && onSelect(selected), children: "Use this image" })] })] })] }) }));
92
92
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PostList.d.ts","sourceRoot":"","sources":["../../../app/components/admin/PostList.tsx"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAsBlE,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,WAAmB,EACnB,MAAM,EACN,MAAM,EACN,YAAY,GACb,EAAE;IACD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;CAC9C,+BA0LA"}
1
+ {"version":3,"file":"PostList.d.ts","sourceRoot":"","sources":["../../../app/components/admin/PostList.tsx"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAwBlE,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,WAAmB,EACnB,MAAM,EACN,MAAM,EACN,YAAY,GACb,EAAE;IACD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;CAC9C,+BAqMA"}
@@ -17,7 +17,9 @@ const SORTS = [
17
17
  { key: "title", label: "Title A–Z" },
18
18
  { key: "oldest", label: "Oldest first" },
19
19
  ];
20
- const PER_PAGE = 20;
20
+ /** Rows per page in the posts table (BlogUploader-style lists use ~10–20). */
21
+ const PER_PAGE = 10;
22
+ const MIN_TABLE_ROWS = 10;
21
23
  export function PostList({ filterState = "all", onEdit, onView, onMetaChange, }) {
22
24
  const store = useAdminMock();
23
25
  const [tab, setTab] = useState(filterState);
@@ -35,6 +37,11 @@ export function PostList({ filterState = "all", onEdit, onView, onMetaChange, })
35
37
  setSort("published");
36
38
  setSortDraft("published");
37
39
  }
40
+ else {
41
+ // All Posts / drafts / etc. — recent edits, not published-only sort
42
+ setSort("recent");
43
+ setSortDraft("recent");
44
+ }
38
45
  }, [filterState]);
39
46
  const counts = store.postCounts();
40
47
  const categories = store.terms("categories");
@@ -63,6 +70,8 @@ export function PostList({ filterState = "all", onEdit, onView, onMetaChange, })
63
70
  onMetaChange?.({ total: rows.length, search });
64
71
  }, [rows.length, search, onMetaChange]);
65
72
  const totalPages = Math.max(1, Math.ceil(rows.length / PER_PAGE));
73
+ const pageStart = rows.length === 0 ? 0 : (page - 1) * PER_PAGE + 1;
74
+ const pageEnd = Math.min(page * PER_PAGE, rows.length);
66
75
  const pageRows = rows.slice((page - 1) * PER_PAGE, page * PER_PAGE);
67
76
  const showBrand = !store.activeBrandId && store.brands.length > 1;
68
77
  const isTrash = tab === "trash";
@@ -79,15 +88,15 @@ export function PostList({ filterState = "all", onEdit, onView, onMetaChange, })
79
88
  setCategoryId("");
80
89
  setPage(1);
81
90
  };
82
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: "tabs", role: "navigation", "aria-label": "Filter posts by state", children: TABS.map((t) => (_jsxs("button", { type: "button", "aria-current": t.key === tab ? "true" : undefined, onClick: () => {
91
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "blog_tabs", role: "navigation", "aria-label": "Filter posts by state", children: TABS.map((t) => (_jsxs("button", { type: "button", "aria-current": t.key === tab ? "true" : undefined, onClick: () => {
83
92
  setTab(t.key);
84
93
  setPage(1);
85
94
  if (t.key === "live") {
86
95
  setSort("published");
87
96
  setSortDraft("published");
88
97
  }
89
- }, children: [t.label, _jsx("span", { className: "tab-count", children: counts[t.key] ?? 0 })] }, t.key))) }), _jsxs("form", { className: "filterbar", onSubmit: (e) => {
98
+ }, children: [t.label, _jsx("span", { className: "blog_tab-count", children: counts[t.key] ?? 0 })] }, t.key))) }), _jsxs("form", { className: "blog_filterbar", onSubmit: (e) => {
90
99
  e.preventDefault();
91
100
  applyFilters();
92
- }, children: [_jsx("input", { className: "fld", type: "search", name: "q", placeholder: "Search titles\u2026", value: searchDraft, onChange: (e) => setSearchDraft(e.target.value), "aria-label": "Search post titles" }), _jsxs("select", { className: "fld", name: "cat", "aria-label": "Filter by category", value: categoryDraft, onChange: (e) => setCategoryDraft(e.target.value), children: [_jsx("option", { value: "", children: "All categories" }), categories.map((c) => (_jsx("option", { value: c.id, children: c.name }, c.id)))] }), _jsx("select", { className: "fld", name: "sort", "aria-label": "Sort", value: sortDraft, onChange: (e) => setSortDraft(e.target.value), children: SORTS.map((s) => (_jsx("option", { value: s.key, children: s.label }, s.key))) }), _jsx("button", { className: "btn", type: "submit", children: "Filter" }), search || categoryId ? (_jsx("button", { type: "button", className: "btn btn-ghost", onClick: clearFilters, children: "Clear" })) : null] }), isTrash ? (_jsxs("p", { className: "explain", children: ["Trashed posts are ", _jsx("b", { children: "soft-deleted" }), ": they keep their versions, their slug and their redirects, and no ordinary query returns them. Restoring one puts it back exactly as it was. Deleting permanently is a brand-admin action and cannot be undone."] })) : null, _jsx("section", { className: "panel", children: _jsx(PostsTable, { rows: pageRows, categories: categories, state: tab, showBrand: showBrand, onEdit: onEdit, onView: onView }) }), totalPages > 1 ? (_jsxs("nav", { className: "pager", "aria-label": "Pagination", children: [page > 1 ? (_jsx("button", { type: "button", className: "btn", onClick: () => setPage((p) => p - 1), children: "\u2190 Newer" })) : (_jsx("span", {})), _jsxs("span", { className: "note", children: ["Page ", page, " of ", totalPages] }), page < totalPages ? (_jsx("button", { type: "button", className: "btn", onClick: () => setPage((p) => p + 1), children: "Older \u2192" })) : (_jsx("span", {}))] })) : null] }));
101
+ }, children: [_jsx("input", { className: "blog_fld", type: "search", name: "q", placeholder: "Search titles\u2026", value: searchDraft, onChange: (e) => setSearchDraft(e.target.value), "aria-label": "Search post titles" }), _jsxs("select", { className: "blog_fld", name: "cat", "aria-label": "Filter by category", value: categoryDraft, onChange: (e) => setCategoryDraft(e.target.value), children: [_jsx("option", { value: "", children: "All categories" }), categories.map((c) => (_jsx("option", { value: c.id, children: c.name }, c.id)))] }), _jsx("select", { className: "blog_fld", name: "sort", "aria-label": "Sort", value: sortDraft, onChange: (e) => setSortDraft(e.target.value), children: SORTS.map((s) => (_jsx("option", { value: s.key, children: s.label }, s.key))) }), _jsx("button", { className: "blog_btn", type: "submit", children: "Filter" }), search || categoryId ? (_jsx("button", { type: "button", className: "blog_btn blog_btn-ghost", onClick: clearFilters, children: "Clear" })) : null] }), isTrash ? (_jsxs("p", { className: "blog_explain", children: ["Trashed posts are ", _jsx("b", { children: "soft-deleted" }), ": they keep their versions, their slug and their redirects, and no ordinary query returns them. Restoring one puts it back exactly as it was. Deleting permanently is a brand-admin action and cannot be undone."] })) : null, _jsx("section", { className: "blog_panel", children: _jsx(PostsTable, { rows: pageRows, minRows: MIN_TABLE_ROWS, totalCount: rows.length, page: page, pageSize: PER_PAGE, categories: categories, state: tab, showBrand: showBrand, onEdit: onEdit, onView: onView }) }), rows.length > 0 ? (_jsxs("nav", { className: "blog_pager", "aria-label": "Pagination", children: [page > 1 ? (_jsx("button", { type: "button", className: "blog_btn", onClick: () => setPage((p) => p - 1), children: "\u2190 Newer" })) : (_jsx("span", {})), _jsxs("span", { className: "blog_note", children: ["Showing ", pageStart, "\u2013", pageEnd, " of ", rows.length, totalPages > 1 ? ` · Page ${page} of ${totalPages}` : ""] }), page < totalPages ? (_jsx("button", { type: "button", className: "blog_btn", onClick: () => setPage((p) => p + 1), children: "Older \u2192" })) : (_jsx("span", {}))] })) : null] }));
93
102
  }
@@ -1,6 +1,11 @@
1
1
  import type { PostRow } from "./mock/fixtures";
2
2
  type Props = {
3
3
  rows: PostRow[];
4
+ /** Pad tbody so the list keeps a consistent height (e.g. 10 rows). */
5
+ minRows?: number;
6
+ totalCount?: number;
7
+ page?: number;
8
+ pageSize?: number;
4
9
  categories: {
5
10
  id: string;
6
11
  name: string;
@@ -10,6 +15,6 @@ type Props = {
10
15
  onEdit: (id: string) => void;
11
16
  onView?: (id: string) => void;
12
17
  };
13
- export declare const PostsTable: ({ rows, categories, state, showBrand, onEdit, onView }: Props) => import("react").JSX.Element;
18
+ export declare const PostsTable: ({ rows, minRows, totalCount, page, pageSize, categories, state, showBrand, onEdit, onView, }: Props) => import("react").JSX.Element;
14
19
  export {};
15
20
  //# sourceMappingURL=PostsTable.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PostsTable.d.ts","sourceRoot":"","sources":["../../../app/components/admin/PostsTable.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAuBF,eAAO,MAAM,UAAU,GAAI,wDAAwD,KAAK,gCA4avF,CAAC"}
1
+ {"version":3,"file":"PostsTable.d.ts","sourceRoot":"","sources":["../../../app/components/admin/PostsTable.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAwBF,eAAO,MAAM,UAAU,GAAI,8FAWxB,KAAK,gCA0bP,CAAC"}