add: category, date sorting

Add category layouts and date sorting
This commit is contained in:
webfussel 2025-06-12 09:08:21 +02:00
parent 91b59e4ebe
commit 9b66a79a8c
17 changed files with 2495 additions and 17729 deletions

View file

@ -12,6 +12,17 @@
scale: 1.05; scale: 1.05;
} }
& > .image {
flex: 0 0 200px;
width: 100%;
& img {
height: 100%;
width: 100%;
object-fit: cover;
}
}
& .card-content { & .card-content {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -24,6 +35,11 @@
margin-bottom: 1rem; margin-bottom: 1rem;
} }
& header {
display: flex;
flex-direction: column;
}
& footer { & footer {
margin-top: auto; margin-top: auto;
display: flex; display: flex;
@ -42,3 +58,11 @@
} }
} }
} }
@media (width <= 780px) {
.BlogCard header {
align-items: center;
}
}

View file

@ -290,7 +290,7 @@ span.chip {
.grid { .grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem; gap: 2rem;
} }
@ -325,7 +325,7 @@ span.chip {
} }
@media (width <= 780px) { @media (width <= 780px) {
h1, h2, h3, h4, h5, h6, p { h1, h2, h3, h4, h5, h6, p, small {
text-align: center; text-align: center;
} }
} }

View file

@ -1,7 +1,7 @@
<template> <template>
<aside class="BlogAuthor"> <aside class="BlogAuthor">
<div class="image"> <div class="image">
<img :src="image" :alt="`Bild von ${name}`" /> <img :src="image" :alt="`Bild von ${name}`"/>
</div> </div>
<div class="meta"> <div class="meta">
<span class="name">{{ name }}</span> <span class="name">{{ name }}</span>
@ -12,9 +12,9 @@
<script setup lang="ts"> <script setup lang="ts">
type Props = { type Props = {
name : string name: string
image : string image: string
date : string date: string
} }
const { date } = defineProps<Props>() const { date } = defineProps<Props>()

View file

@ -1,9 +1,11 @@
<template> <template>
<NuxtLink :to="link" class="BlogCard z-2"> <NuxtLink :to="link" class="BlogCard z-2">
<div class="image">
<img :src="image" alt=" " aria-hidden="true" /> <img :src="image" alt=" " aria-hidden="true" />
</div>
<div class="card-content"> <div class="card-content">
<header> <header>
<span class="chip">{{ category }}</span> <span class="chip"><BlogCategory :name="category" /></span>
<h2>{{ title }}</h2> <h2>{{ title }}</h2>
<small>{{ description }}</small> <small>{{ description }}</small>
</header> </header>
@ -22,6 +24,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { Category } from './types'
type Props = { type Props = {
title : string title : string
description : string description : string
@ -30,7 +34,7 @@ type Props = {
excerpt : { type: string, children?: any } excerpt : { type: string, children?: any }
link : string link : string
tags : string[] tags : string[]
category : string category : Category
author : { author : {
name : string name : string
image : string image : string

View file

@ -0,0 +1,24 @@
<template>
<Icon :name="icon" mode="svg"/>
{{ name }}
</template>
<script setup lang="ts">
import type { Category } from './types'
type Props = {
name: Category
}
const { name } = defineProps<Props>()
const icons: Record<Category, string> = {
'story': 'ph:chat-circle-dots-duotone',
'snippet': 'ph:code-duotone',
'tutorial': 'ph:lightbulb-duotone',
'news': 'ph:newspaper-duotone',
'freelancing': 'ph:laptop-duotone',
}
const icon = computed(() => name ? icons[name] : 'ph:question-mark-duotone')
</script>

View file

@ -0,0 +1 @@
export type Category = 'story' | 'snippet' | 'tutorial' | 'news' | 'freelancing'

View file

@ -1,25 +1,33 @@
<template> <template>
<section id="blog" class="Blog content"> <section id="blog" class="Blog content">
<div class="grid"> <div class="grid">
<BlogCard v-for="article in articles" v-bind="makeBlogCard(article)" /> <BlogCard v-for="article in articles" v-bind="makeBlogCard(article)"/>
</div> </div>
</section> </section>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const { data } = await useAsyncData('articles', () => queryCollection('blog').limit(10).all()) import type { Category } from '../../components/Blog/types'
const { data } = await useAsyncData('articles', () => queryCollection('blog').order('date', 'DESC').limit(10).all())
const articles = data.value! const articles = data.value!
const makeBlogCard = (article : typeof articles[0]) => ({ const makeBlogCard = (article: typeof articles[0]) => ({
title: article.title, title: article.title,
description: article.description, description: article.description,
image: article.meta.image as string, image: article.meta.image as string,
date: article.meta.date as string, date: article.date as string,
excerpt: article.excerpt, excerpt: article.excerpt,
link: article.path, link: article.path,
tags: article.meta.tags as string[], tags: article.meta.tags as string[],
category: article.meta.category as string, category: article.meta.category as Category,
author: article.meta.author as { name: string, image: string }, author: article.meta.author as { name: string, image: string },
}) })
useHead({
link: [
{ rel: 'alternate', type: 'application/rss+xml', href: '/blog/rss.xml', title: 'blogfussel' },
],
})
</script> </script>

2341
bun.lock Normal file

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@ export default defineContentConfig({
type: 'page', type: 'page',
source: 'blog/*.md', source: 'blog/*.md',
schema: z.object({ schema: z.object({
date: z.string(),
excerpt: z.object({ excerpt: z.object({
type: z.string(), type: z.string(),
children: z.any(), children: z.any(),
@ -23,7 +24,7 @@ export default defineContentConfig({
source: 'snippets/faq/*.md', source: 'snippets/faq/*.md',
schema: z.object({ schema: z.object({
rawbody: z.string(), rawbody: z.string(),
}) }),
}) }),
} },
}) })

View file

@ -4,14 +4,15 @@ title: 'Blogfussel - für mehr Fussel im Blog'
description: 'Und nochmal...' description: 'Und nochmal...'
navigation: true navigation: true
date: 2025-07-01 date: 2025-07-01
category: 'start' category: 'story'
tags: ['start', 'story'] tags: [ 'start', 'story' ]
author: author:
name: 'webfussel' name: 'webfussel'
image: '/img/og.webp' image: '/img/og.webp'
--- ---
Warum ich mich dazu entschlossen habe jetzt doch wieder mit dem Bloggen anzufangen? Nun ja, das hat alles angefangen, als man mich einfach so auf Instagram gesperrt hat. Warum ich mich dazu entschlossen habe jetzt doch wieder mit dem Bloggen anzufangen? Nun ja, das hat alles angefangen, als man mich einfach so auf
Instagram gesperrt hat.
<!-- more --> <!-- more -->

View file

@ -3,9 +3,9 @@ image: 'https://picsum.photos/600/250?random=3'
title: 'Post 1' title: 'Post 1'
description: 'Blablabla' description: 'Blablabla'
navigation: true navigation: true
date: 2025-07-01 date: 2025-06-20
category: 'test' category: 'tutorial'
tags: ['test', 'wasd'] tags: [ 'test', 'wasd' ]
author: author:
name: 'webfussel' name: 'webfussel'
image: '/img/og.webp' image: '/img/og.webp'

View file

@ -3,9 +3,9 @@ image: 'https://picsum.photos/600/250?random=2'
title: 'Post 2' title: 'Post 2'
description: 'Blablabla' description: 'Blablabla'
navigation: true navigation: true
date: 2025-07-01 date: 2025-06-16
category: 'test' category: 'snippet'
tags: ['test', 'asdf'] tags: [ 'test', 'asdf' ]
author: author:
name: 'webfussel' name: 'webfussel'
image: '/img/og.webp' image: '/img/og.webp'

View file

@ -3,9 +3,9 @@ image: 'https://picsum.photos/600/250?random=1'
title: 'Post 3' title: 'Post 3'
description: 'Blablabla' description: 'Blablabla'
navigation: true navigation: true
date: 2025-07-01 date: 2025-06-25
category: 'test' category: 'freelancing'
tags: ['test', '123'] tags: [ 'test', '123' ]
author: author:
name: 'webfussel' name: 'webfussel'
image: '/img/og.webp' image: '/img/og.webp'

View file

@ -0,0 +1,18 @@
---
image: 'https://picsum.photos/600/250?random=5'
title: 'Post 4'
description: 'Blablabla'
navigation: true
date: 2025-06-25
category: 'news'
tags: [ 'test', '123' ]
author:
name: 'webfussel'
image: '/img/og.webp'
---
Blablabla test 1 2 3
<!-- more -->
Noch mehr Text

17696
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -9,9 +9,7 @@
"generate": "nuxt generate", "generate": "nuxt generate",
"preview": "nuxt preview", "preview": "nuxt preview",
"gen:prev": "nuxt generate && nuxt preview", "gen:prev": "nuxt generate && nuxt preview",
"postinstall": "nuxt prepare", "postinstall": "nuxt prepare"
"generate:deploy": "nuxt generate && firebase deploy --only hosting",
"build:deploy": "nuxt build && firebase deploy --only hosting"
}, },
"devDependencies": { "devDependencies": {
"@iconify-json/material-symbols": "^1.2.14", "@iconify-json/material-symbols": "^1.2.14",
@ -27,5 +25,8 @@
}, },
"dependencies": { "dependencies": {
"@nuxt/content": "^3.5.1" "@nuxt/content": "^3.5.1"
} },
"trustedDependencies": [
"@parcel/watcher"
]
} }

View file

@ -0,0 +1,39 @@
import { BlogCollectionItem } from '@nuxt/content'
const clean = (value: string) => value
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/ä/g, '&auml;')
.replace(/ö/g, '&ouml;')
.replace(/ü/g, '&uuml;')
.replace(/ß/g, '&szlig;')
const makeItem = (article: BlogCollectionItem, base: string) => `
<item>
<title>${clean(article.title)}</title>
<description>${clean(article.description)}</description>
<link>${base}${article.path}</link>
</item>
`
export default defineEventHandler(async event => {
const baseUrl = getRequestURL(event)
const blogUrl = `${baseUrl.protocol}//${baseUrl.host}`
const articles = (await queryCollection(event, 'blog').order('date', 'DESC').all())
const xmlItems = articles.map(article => makeItem(article, blogUrl)).join('')
return `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Blogfussel</title>
<link>${baseUrl}/blog</link>
<description>f&uuml;r mehr Fussel im Blog</description>
<language>de</language>
${xmlItems}
</channel>
</rss>
`
})