Skip to content

Commit 6861a1a

Browse files
luciscursoragent
andcommitted
Add back-home link and Ler mais recommendations on posts
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e115e96 commit 6861a1a

4 files changed

Lines changed: 125 additions & 2 deletions

File tree

public/.assetsignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
_worker.js
21
_routes.json
2+
_worker.js
3+
.DS_Store
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
import type { BlogEntry } from "../lib/posts";
3+
import { getPostSlug } from "../lib/posts";
4+
5+
interface Props {
6+
posts: BlogEntry[];
7+
}
8+
9+
const { posts } = Astro.props;
10+
---
11+
12+
{
13+
posts.length > 0 && (
14+
<section class="mt-16 max-w-3xl mx-auto w-full px-4 md:px-0">
15+
<div class="text-center mb-8">
16+
<span class="text-black text-2xl">•</span>
17+
<h2 class="mt-4 text-2xl font-bold font-serif text-black">Ler mais</h2>
18+
</div>
19+
20+
<div class="hidden md:block space-y-3">
21+
{posts.map((post) => {
22+
const slug = getPostSlug(post);
23+
const hasImage = Boolean(post.data.image?.trim());
24+
25+
return (
26+
<article class="border border-gray-100 rounded-lg overflow-hidden hover:bg-gray-50 transition-colors duration-200 delay-75">
27+
<a href={`/blog/${slug}`} class="block p-6">
28+
<div class="flex gap-4 items-center">
29+
{hasImage && (
30+
<div class="w-20 h-20 flex-shrink-0">
31+
<img
32+
src={post.data.image}
33+
alt={post.data.title}
34+
width="80"
35+
height="80"
36+
class="w-full h-full object-cover rounded-lg"
37+
loading="lazy"
38+
decoding="async"
39+
/>
40+
</div>
41+
)}
42+
<div class="flex-1 min-w-0">
43+
<h3 class="text-lg font-bold text-gray-900 font-serif leading-tight line-clamp-2">
44+
{post.data.title}
45+
</h3>
46+
</div>
47+
</div>
48+
</a>
49+
</article>
50+
);
51+
})}
52+
</div>
53+
54+
<div class="md:hidden grid gap-6 grid-cols-1 sm:grid-cols-2">
55+
{posts.map((post) => {
56+
const slug = getPostSlug(post);
57+
const hasImage = Boolean(post.data.image?.trim());
58+
59+
return (
60+
<a href={`/blog/${slug}`} class="block group">
61+
<div class="space-y-2">
62+
{hasImage && (
63+
<img
64+
src={post.data.image}
65+
alt={post.data.title}
66+
width="300"
67+
height="200"
68+
class="w-full h-40 object-cover rounded-lg group-hover:opacity-90 transition-opacity"
69+
loading="lazy"
70+
decoding="async"
71+
/>
72+
)}
73+
<h3 class="text-lg font-bold text-gray-900 font-serif leading-tight line-clamp-2">
74+
{post.data.title}
75+
</h3>
76+
</div>
77+
</a>
78+
);
79+
})}
80+
</div>
81+
</section>
82+
)
83+
}

src/lib/posts.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,14 @@ export function getAuthorNames(post: BlogEntry): string {
6868
export function getPostSlug(post: BlogEntry): string {
6969
return post.id.replace(/\.mdx?$/i, "");
7070
}
71+
72+
export async function getRecommendedPosts(
73+
currentSlug: string,
74+
limit = 3,
75+
): Promise<BlogEntry[]> {
76+
const posts = await getPublishedPosts();
77+
78+
return posts
79+
.filter((post) => getPostSlug(post) !== currentSlug)
80+
.slice(0, limit);
81+
}

src/pages/blog/[slug].astro

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
import { getCollection, render } from "astro:content";
33
import BaseLayout from "../../layouts/BaseLayout.astro";
44
import BlogPostLayout from "../../layouts/BlogPostLayout.astro";
5+
import RecommendedPosts from "../../components/RecommendedPosts.astro";
56
import {
67
DEFAULT_AUTHOR_NAME,
78
DEFAULT_AVATAR,
89
formatLongDate,
910
getAuthorAvatar,
1011
getAuthorNames,
1112
getPostSlug,
13+
getRecommendedPosts,
1214
SITE_DEFAULT_IMAGE,
1315
} from "../../lib/posts";
1416
@@ -25,12 +27,14 @@ export async function getStaticPaths() {
2527
2628
const { post } = Astro.props;
2729
const { title, date, image } = post.data;
30+
const slug = getPostSlug(post);
2831
const formattedDate = formatLongDate(date);
2932
const description = `Escrito em ${formattedDate}`;
30-
const canonical = `https://blog.lucis.dev/blog/${getPostSlug(post)}`;
33+
const canonical = `https://blog.lucis.dev/blog/${slug}`;
3134
const hasImage = Boolean(image?.trim());
3235
3336
const { Content } = await render(post);
37+
const recommended = await getRecommendedPosts(slug, 3);
3438
3539
const authorName = getAuthorNames(post) || DEFAULT_AUTHOR_NAME;
3640
const authorAvatar = getAuthorAvatar(post) || DEFAULT_AVATAR;
@@ -58,6 +62,28 @@ const jsonLd = {
5862
>
5963
<article class="w-full flex flex-col gap-20 container mx-auto px-4 md:px-0 py-12 lg:py-12 bg-white">
6064
<div class="w-full flex flex-col gap-12 max-w-3xl lg:mx-auto">
65+
<a
66+
href="/"
67+
class="inline-flex items-center text-sm text-gray-600 hover:text-black transition-colors w-fit"
68+
aria-label="Voltar para a home"
69+
>
70+
<svg
71+
xmlns="http://www.w3.org/2000/svg"
72+
class="h-6 w-6 mr-2"
73+
fill="none"
74+
viewBox="0 0 24 24"
75+
stroke="currentColor"
76+
aria-hidden="true"
77+
>
78+
<path
79+
stroke-linecap="round"
80+
stroke-linejoin="round"
81+
stroke-width="2"
82+
d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
83+
</svg>
84+
<span>Home</span>
85+
</a>
86+
6187
<h1 class="text-5xl font-bold font-serif">{title}</h1>
6288
<div class="flex items-center gap-4">
6389
<img
@@ -90,5 +116,7 @@ const jsonLd = {
90116
<BlogPostLayout>
91117
<Content />
92118
</BlogPostLayout>
119+
120+
<RecommendedPosts posts={recommended} />
93121
</article>
94122
</BaseLayout>

0 commit comments

Comments
 (0)