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:
+6
-1
@@ -43,7 +43,10 @@ export default async function BlogOverviewPage() {
|
||||
</Reveal>
|
||||
|
||||
{featured && (
|
||||
<Reveal delay={0.1} className="w-full px-[var(--layout-padding-x)] pt-10 pb-4">
|
||||
// -mt-8, not pt-10 — pulls the card up to slightly overlap the
|
||||
// hero section's bottom edge instead of sitting flush below it.
|
||||
// relative z-10 keeps it stacked above the hero's own photo.
|
||||
<Reveal delay={0.1} className="relative z-10 w-full px-[var(--layout-padding-x)] -mt-8 pb-4">
|
||||
<Link
|
||||
href={`/blog/${featured.slug}`}
|
||||
className="group grid grid-cols-1 md:grid-cols-2 w-full max-w-[80rem] mx-auto rounded-md overflow-hidden bg-bg-muted transition-transform duration-300 hover:-translate-y-1"
|
||||
@@ -58,6 +61,8 @@ export default async function BlogOverviewPage() {
|
||||
<span>{featured.category}</span>
|
||||
<span>•</span>
|
||||
<span>{featured.readTime} Min</span>
|
||||
<span>•</span>
|
||||
<span className="uppercase">{formatDate(featured.publishedAt)}</span>
|
||||
</div>
|
||||
<p
|
||||
className="font-semibold text-h-section text-text-primary"
|
||||
|
||||
+12
-10
@@ -86,14 +86,6 @@ function LockIcon() {
|
||||
);
|
||||
}
|
||||
|
||||
function ArrowRight() {
|
||||
return (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" className="text-[#ccc] shrink-0">
|
||||
<path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
const steps = [
|
||||
{
|
||||
icon: <IconEnvelope />,
|
||||
@@ -294,8 +286,18 @@ export default function ChallengePage() {
|
||||
</div>
|
||||
</RevealItem>,
|
||||
i < steps.length - 1 ? (
|
||||
<div key={`arrow-${i}`} className="hidden lg:flex items-center shrink-0 mt-5">
|
||||
<ArrowRight />
|
||||
// Same /icon-arrow-connector.svg asset and rotate-on-stack
|
||||
// pattern as todo-cards/newsletter's HowItWorks — this
|
||||
// used to be its own hand-drawn SVG arrow, inconsistent
|
||||
// with those two. Always visible (rotated 90° while
|
||||
// stacked below lg, this page's own structural
|
||||
// breakpoint) rather than hidden below lg like before.
|
||||
<div key={`arrow-${i}`} className="flex items-center justify-center shrink-0 lg:mt-5">
|
||||
<img
|
||||
alt=""
|
||||
src="/icon-arrow-connector.svg"
|
||||
className="w-6 h-6 rotate-90 lg:w-10 lg:h-3 lg:rotate-0"
|
||||
/>
|
||||
</div>
|
||||
) : null,
|
||||
])}
|
||||
|
||||
+10
-1
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,11 @@ export function HowItWorks() {
|
||||
<p className="text-body-sm text-text-primary text-center">{step.desc}</p>
|
||||
</RevealItem>
|
||||
{i < steps.length - 1 && (
|
||||
<div className="flex items-center justify-center shrink-0">
|
||||
// md:mt-[1.625rem] (26px) centers the arrow on the h-16
|
||||
// (64px) icon above it — same margin-based centering
|
||||
// technique as Challenge's step connector and todo-cards'
|
||||
// identical HowItWorks, not just the same icon asset.
|
||||
<div className="flex items-center justify-center shrink-0 md:mt-[1.625rem]">
|
||||
<img
|
||||
alt=""
|
||||
src="/icon-arrow-connector.svg"
|
||||
|
||||
@@ -68,7 +68,11 @@ export function HowItWorks() {
|
||||
<p className="text-body-sm text-text-primary text-center">{step.desc}</p>
|
||||
</RevealItem>
|
||||
{i < steps.length - 1 && (
|
||||
<div className="flex items-center justify-center shrink-0">
|
||||
// md:mt-[1.625rem] (26px) centers the arrow on the h-16
|
||||
// (64px) icon above it — (64 - arrow's own 12px height) / 2
|
||||
// — same margin-based centering technique as Challenge's
|
||||
// step connector, not just the same icon asset.
|
||||
<div className="flex items-center justify-center shrink-0 md:mt-[1.625rem]">
|
||||
<img
|
||||
alt=""
|
||||
src="/icon-arrow-connector.svg"
|
||||
|
||||
Reference in New Issue
Block a user