[PIE-16] Basic tag page implementation #13

Merged
breadone merged 5 commits from PIE-16 into main 2025-08-16 23:17:31 +12:00
8 changed files with 88 additions and 6 deletions

View File

@ -6,7 +6,7 @@ const { tags } = Astro.props
{ {
(tags ?? []).map(tag => ( (tags ?? []).map(tag => (
<a <a
href={`/tag/${tag.id}`} href={`/tags/${tag.name}`}
class="text-white bg-white/20 px-2 mr-2 mt-2 rounded-md inline-block hover:bg-white/30" class="text-white bg-white/20 px-2 mr-2 mt-2 rounded-md inline-block hover:bg-white/30"
> >
{tag.name} {tag.name}

View File

@ -8,7 +8,8 @@
<div class="ml-auto space-x-5"> <div class="ml-auto space-x-5">
<a class="hover:underline underline-offset-4" href="/recipe/new">new</a> <a class="hover:underline underline-offset-4" href="/recipe/new">new</a>
<a class="hover:underline underline-offset-4 " >tags</a> <a class="hover:underline underline-offset-4" href="/recipe/import">add</a>
<!-- <a class="hover:underline underline-offset-4" href="/tags">tags</a> -->
<a class="hover:underline underline-offset-4" >search</a> <a class="hover:underline underline-offset-4" >search</a>
</div> </div>
</div> </div>

View File

@ -29,7 +29,7 @@ class APIClient {
// IMAGE // IMAGE
async getImageURL(imgID: string, relative: boolean = true) { async getImageURL(imgID: string, relative: boolean = true) {
const record = await this.client.collection("images").getOne(imgID) const record = await this.client.collection(Collection.IMAGES).getOne(imgID)
const res = this.client.files.getURL(record, record.image) const res = this.client.files.getURL(record, record.image)
return relative ? res.substring(21) : res return relative ? res.substring(21) : res
} }
@ -44,6 +44,28 @@ class APIClient {
return urls return urls
} }
async getAllTags() {
return await this.client.collection<Tag>(Collection.TAGS).getFullList()
}
async getTag(name: string) {
return await this.client.collection<Tag>(Collection.TAGS).getList(1, 50, { filter: `name = '${name}'` })
}
async getRecipesOfTag(tagName: string) {
// get the tag id first
const tagResult = await this.getTag(tagName)
if (tagResult.items.length === 0) {
return []
}
const tag = tagResult.items[0]
return await this.client.collection<Recipe>(Collection.RECIPES).getFullList({
filter: `tags ~ '${tag.id}'`,
expand: 'ingredients,tags,steps,images,steps.ingredients'
})
}
} }
const client = new APIClient() const client = new APIClient()
export default client; export default client;

9
src/pages/404.astro Normal file
View File

@ -0,0 +1,9 @@
---
import Base from "@/layouts/base";
---
<Base>
<div class="flex items-center justify-center text-3xl font-bold">
🥧 404 🥧
</div>
</Base>

View File

@ -9,7 +9,7 @@ const recipes = await client.getAllRecipes()
<PageLayout> <PageLayout>
<!-- <p class="pb-2">What would you like today?</p> --> <!-- <p class="pb-2">What would you like today?</p> -->
<div class="grid md:gap-2 gap-3 grid-cols-2 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-8"> <div class="grid gap-3 grid-cols-1 py-3 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6">
{ {
recipes.map(r => ( recipes.map(r => (
<OverviewCard recipe={r} /> <OverviewCard recipe={r} />

View File

@ -0,0 +1,22 @@
---
import SiteLayout from "@/layouts/base";
import client from "@/data/pocketbase";
import OverviewCard from "@/components/Card/OverviewCard";
const { name } = Astro.params
const recipes = await client.getRecipesOfTag(name) // todo redir to 404 if not found
---
<SiteLayout>
<p class="text-xl pb-2">
{recipes.length} { recipes.length == 1 ? "Recipe" : "Recipes" } with: <code class="bg-white/10 p-1 text-sm rounded-lg" >{name}</code>
</p>
<div class="grid md:gap-2 gap-3 grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-8">
{
recipes.map(r => (
<OverviewCard recipe={r} />
))
}
</div>
</SiteLayout>

View File

@ -0,0 +1,24 @@
---
import client from "@/data/pocketbase";
import SiteLayout from "@/layouts/base";
const tags = await client.getAllTags()
const countsPerTag = await Promise.all(
tags.map(async t => (await client.getRecipesOfTag(t.name)).length)
)
---
<SiteLayout>
<p class="title pb-2">
{tags.length} Tags
</p>
{
(tags ?? []).map((t, i) => (
// <p>{t.name} -&gt; {countsPerTag[i]} {countsPerTag[i] == 1 ? "Recipe" : "Recipes" } </p>
<a class="hover:underline" href={`/tags/${t.name}`}>
{t.name} ({countsPerTag[i]})
</a><br/>
))
}
</SiteLayout>

View File

@ -8,3 +8,7 @@ html {
@apply font-sans; @apply font-sans;
/* font-family: 'SF Pro Display', 'Segoe UI', 'Helvetica Neue', Arial, 'Noto Sans', sans-serif; */ /* font-family: 'SF Pro Display', 'Segoe UI', 'Helvetica Neue', Arial, 'Noto Sans', sans-serif; */
} }
.title {
@apply text-2xl;
}