Add featured-post flag, align tool-page connector arrows, misc blog polish

- Posts collection: new "featured" checkbox (Payload) — /blog now prefers
  a manually-featured post over just the most recent one. Query sorts by
  "-featured,-publishedAt" so multiple accidentally-featured posts
  resolve deterministically (most recent among them wins) instead of
  erroring or being ambiguous.
- challenge/page.tsx: step-connector arrow now uses the same
  /icon-arrow-connector.svg asset as todo-cards/newsletter's HowItWorks,
  replacing its own hand-drawn inline SVG.
- todo-cards & newsletter HowItWorks: connector arrows now vertically
  centered on their icon (md:mt-[1.625rem]), matching Challenge's
  margin-based centering technique instead of sitting flush at the row's
  top edge.
- Blog overview: featured card overlaps the header slightly (-mt-8) per
  Figma-inspired refinement, and now also shows the publish date in its
  meta row (previously only the list items did).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-07-19 21:24:04 +00:00
parent ed58a65b8f
commit 8bfbf74693
5 changed files with 38 additions and 14 deletions
+10 -1
View File
@@ -10,6 +10,7 @@ export type BlogPost = {
excerpt: string;
thumbnail: string | null;
publishedAt: string;
featured: boolean;
};
type PayloadPost = {
@@ -21,12 +22,18 @@ type PayloadPost = {
excerpt: string;
thumbnail: { url: string } | number | null;
publishedAt: string;
featured: boolean;
};
// sort: "-featured,-publishedAt" — Payload's multi-field sort puts any
// featured post(s) first, most-recently-published first among those, then
// the rest by date. Callers that just want posts[0] as "the" featured post
// (e.g. /blog) get a deterministic pick even if more than one post was
// accidentally marked featured — no special-casing needed here.
export async function getBlogPosts(limit = 3): Promise<BlogPost[]> {
const params = new URLSearchParams({
"where[tenant.slug][equals]": TENANT_SLUG,
sort: "-publishedAt",
sort: "-featured,-publishedAt",
depth: "2",
limit: String(limit),
});
@@ -56,6 +63,7 @@ export async function getBlogPosts(limit = 3): Promise<BlogPost[]> {
? post.thumbnail.url
: null,
publishedAt: post.publishedAt,
featured: post.featured,
}));
}
@@ -96,6 +104,7 @@ export async function getPostBySlug(slug: string): Promise<PostDetail | null> {
typeof doc.thumbnail === "object" && doc.thumbnail ? doc.thumbnail.url : null,
content: doc.content,
publishedAt: doc.publishedAt,
featured: doc.featured,
};
}