Skip to content

Commit 235bb9e

Browse files
committed
Implementación de búsqueda de blogs por tag.
1 parent dfa0669 commit 235bb9e

7 files changed

Lines changed: 144 additions & 45 deletions

File tree

src/components/Badge.svelte

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
let { text = 'Badge' } = $props();
99
</script>
1010

11-
<span class="rounded-full px-3 py-1 text-xs bg-cmxblack text-white whitespace-nowrap">
12-
{text}
11+
<span
12+
class="rounded-full px-3 py-1 text-xs bg-cmxblack text-white whitespace-nowrap hover:bg-cmxgreen"
13+
>
14+
<a href="/blog/tag/{text}">{text}</a>
1315
</span>

src/components/Cards/ArticleCard.svelte

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,32 @@
44
let { slug, title, date, image, tags, authors } = $props();
55
</script>
66

7-
<a href="/blog/{slug}">
8-
<div class="w-full">
9-
<div class="md:aspect-video aspect-square">
7+
<div class="w-full">
8+
<div class="md:aspect-video aspect-square mb-4 border border-cmxblack border-1">
9+
<a href="/blog/{slug}">
1010
<img class="object-cover w-full h-full" src={image} alt="Card" />
11-
</div>
12-
<div class="space-y-2 mt-5">
13-
<div class="flex flex-row flex-wrap gap-2">
14-
{#each tags as tag}
15-
<Badge text={tag} />
16-
{/each}
17-
</div>
18-
<h2 class="font-extrabold text-xl">{title}</h2>
11+
</a>
12+
</div>
13+
<div class="space-y-2 flex flex-col gap-2">
14+
<a href="/blog/{slug}">
15+
<h2 class="font-bold text-xl">{title}</h2>
16+
</a>
17+
<div class="flex flex-col gap-1">
1918
{#if authors}
2019
<p class="text-sm">
2120
{#each authors as author, i}
22-
<span
23-
>{author.authors_id.name || ''}{#if i < authors.length - 1},
24-
{/if}</span
25-
>
21+
<span>
22+
{author.authors_id.name || ''}{#if i < authors.length - 1},&nbsp;{/if}
23+
</span>
2624
{/each}
2725
</p>
2826
{/if}
2927
<p class="text-sm text-gray-400"><HumanDate {date} /></p>
3028
</div>
29+
<div class="flex flex-row flex-wrap gap-2">
30+
{#each tags as tag}
31+
<Badge text={tag} />
32+
{/each}
33+
</div>
3134
</div>
32-
</a>
35+
</div>

src/components/MenuTagsBlog.svelte

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,25 @@
11
<script>
2-
let { activeTag, handleTags, tags } = $props();
2+
let { activeTag, handleTags, tags } = $props();
33
</script>
44

55
{#each tags as tag}
6-
<a
7-
href={null}
8-
class={activeTag === tag ? 'text-cmxgreen' : null}
9-
onclick={() => handleTags(tag)}
10-
>
11-
{tag.toUpperCase()}
12-
</a>
6+
<a href={null} class={activeTag === tag ? 'text-cmxgreen' : null} onclick={() => handleTags(tag)}>
7+
{tag.toUpperCase()}
8+
</a>
139
{/each}
1410

1511
<style>
16-
a {
17-
display: inline-block;
18-
text-transform: uppercase;
19-
font-weight: 700;
20-
line-height: 2.5;
21-
}
22-
a:not(:last-child) {
23-
padding-right: 15px;
24-
}
25-
a:hover {
26-
cursor: pointer;
27-
text-decoration: underline;
28-
}
12+
a {
13+
display: inline-block;
14+
text-transform: uppercase;
15+
font-weight: 700;
16+
line-height: 2.5;
17+
}
18+
a:not(:last-child) {
19+
padding-right: 15px;
20+
}
21+
a:hover {
22+
cursor: pointer;
23+
text-decoration: underline;
24+
}
2925
</style>

src/lib/utilities.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ export function getFlatArrayUnrepeated(elements = /** @type {any[]} */ (interfac
1111
unrepeated = [...new Set([...unrepeated, ...new Set(item?.tags?.map(setToLowerCase))])]
1212
})
1313

14-
return unrepeated;
14+
return unrepeated.sort();
1515
}

src/routes/blog/+page.svelte

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
</script>
2626

2727
<div class="container my-20 pt-hero mx-auto">
28-
<div class="container m-auto px-3">
28+
<div class="container m-auto px-3 mb-8">
2929
<div class="my-7">
3030
<h1 class="text-5xl text-cmxgreen font-bold">Blog</h1>
3131
</div>
@@ -39,11 +39,8 @@
3939
content={highlightedPost.content}
4040
/>
4141
</div>
42-
<div class="container mx-auto px-3 pt-16 pb-12 text-center">
43-
<MenuTagsBlog {activeTag} {handleTags} {tags} />
44-
</div>
4542
<div class="container m-auto p-3">
46-
<div class="md:grid grid-cols-3 gap-5">
43+
<div class="md:grid grid-cols-3 gap-x-4 gap-y-12">
4744
{#each filteredPosts as post}
4845
<ArticleCard
4946
slug={post.slug}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import client from '$lib/apiClient'
2+
import { readItems } from '@directus/sdk'
3+
import { getFlatArrayUnrepeated } from '$lib/utilities';
4+
import { error } from '@sveltejs/kit';
5+
6+
export async function entries() {
7+
const posts = await client.request(readItems('Blog_Posts', {
8+
fields: ['tags'],
9+
filter: { status: { _eq: 'published' } },
10+
}));
11+
const allTags = getFlatArrayUnrepeated(posts);
12+
return allTags.map((tag) => ({ tag }));
13+
}
14+
15+
export async function load({ params }) {
16+
const tag = params.tag.toLowerCase();
17+
18+
const allPosts = await client.request(readItems('Blog_Posts', {
19+
fields: ['*', { authors: [{ authors_id: ['*'] }] }],
20+
filter: { status: { _eq: 'published' } },
21+
sort: ['-date_published'],
22+
}));
23+
24+
const allTags = getFlatArrayUnrepeated(allPosts);
25+
26+
if (!allTags.includes(tag)) {
27+
throw error(404, `No hay entradas con el tag "${tag}"`);
28+
}
29+
30+
const filteredPosts = allPosts.filter((post) =>
31+
post.tags?.map((t) => t.toLowerCase()).includes(tag)
32+
);
33+
34+
return { posts: filteredPosts, tag, allTags };
35+
}
36+
37+
export const prerender = true;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<script>
2+
import { updateMenuSelector } from '@/lib/menuSelectorUpdater.js';
3+
import ArticleCard from '@/components/Cards/ArticleCard.svelte';
4+
5+
let { data } = $props();
6+
const posts = $derived(data.posts);
7+
const tag = $derived(data.tag);
8+
const allTags = $derived(data.allTags);
9+
10+
$effect(() => updateMenuSelector({ url: '/blog', color: 'text-cmxgreen' }));
11+
</script>
12+
13+
<svelte:head>
14+
<title>Tag: {tag} — Blog · Codeando México</title>
15+
<meta name="description" content="Entradas del blog con el tag {tag}" />
16+
</svelte:head>
17+
18+
<div class="container my-20 pt-hero mx-auto">
19+
<div class="container m-auto px-3">
20+
<div class="my-7">
21+
<a href="/blog" class="text-sm text-gray-500 hover:underline">← Blog</a>
22+
<h1 class="text-5xl text-cmxgreen font-bold mt-2">
23+
Tag: <span class="capitalize">{tag}</span>
24+
</h1>
25+
</div>
26+
</div>
27+
28+
<div class="container mx-auto px-3 pt-16 pb-12 text-center">
29+
<a href="/blog" class="tag-link">TODOS</a>
30+
{#each allTags as t}
31+
<a href="/blog/tag/{t}" class="tag-link {t === tag ? 'text-cmxgreen' : ''}">
32+
{t.toUpperCase()}
33+
</a>
34+
{/each}
35+
</div>
36+
37+
<div class="container m-auto p-3">
38+
<div class="md:grid grid-cols-3 gap-x-4 gap-y-12">
39+
{#each posts as post}
40+
<ArticleCard
41+
slug={post.slug}
42+
title={post.title}
43+
tags={post.tags}
44+
authors={post.authors}
45+
date={post.date_published || post.date_created}
46+
image={`https://content.codeandomexico.org/assets/${post.post_image}`}
47+
/>
48+
{/each}
49+
</div>
50+
</div>
51+
</div>
52+
53+
<style>
54+
.tag-link {
55+
display: inline-block;
56+
font-weight: 700;
57+
line-height: 2.5;
58+
padding-right: 15px;
59+
}
60+
.tag-link:hover {
61+
cursor: pointer;
62+
text-decoration: underline;
63+
}
64+
</style>

0 commit comments

Comments
 (0)