add: category, date sorting

Add category layouts and date sorting
This commit is contained in:
webfussel 2025-06-12 13:13:30 +02:00
parent 9b66a79a8c
commit 579491f216
7 changed files with 85 additions and 25 deletions

View file

@ -1,7 +1,7 @@
.BlogCard { .BlogCard {
overflow: hidden; overflow: hidden;
border-radius: 1rem; border-radius: 1rem;
background: var(--color-orange-black); background: var(--color-black);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
@ -60,7 +60,6 @@
} }
@media (width <= 780px) { @media (width <= 780px) {
.BlogCard header { .BlogCard header {
align-items: center; align-items: center;

View file

@ -0,0 +1,15 @@
.BlogOverview {
& .category-list {
display: flex;
flex-wrap: wrap;
gap: 1rem;
list-style: none;
}
& .article-overview {
width: 100%;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
}

View file

@ -97,6 +97,15 @@ a {
color: var(--color-white); color: var(--color-white);
} }
a.side {
color: var(--color-orange);
text-decoration: none;
&:hover {
color: var(--color-orange-light);
}
}
a.text { a.text {
color: var(--color-orange); color: var(--color-orange);
text-decoration: underline; text-decoration: underline;
@ -148,6 +157,7 @@ span.chip {
align-items: center; align-items: center;
gap: .5em; gap: .5em;
width: max-content; width: max-content;
transition: var(--transition-time);
&:not(.dark) { &:not(.dark) {
--background: var(--color-orange); --background: var(--color-orange);
@ -197,6 +207,10 @@ span.chip {
margin-top: 1rem; margin-top: 1rem;
} }
.margin-top-middle {
margin-top: 2rem;
}
.margin-top-big { .margin-top-big {
margin-top: 6rem; margin-top: 6rem;
} }
@ -241,6 +255,10 @@ span.chip {
flex-direction: column; flex-direction: column;
} }
.flex-wrap {
flex-wrap: wrap;
}
.gap-default { .gap-default {
gap: 3rem; gap: 3rem;
} }
@ -288,12 +306,6 @@ span.chip {
align-items: center; align-items: center;
} }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
.tip-container .tip { .tip-container .tip {
scale: 0; scale: 0;
position: absolute; position: absolute;

View file

@ -20,5 +20,5 @@ const icons: Record<Category, string> = {
'freelancing': 'ph:laptop-duotone', 'freelancing': 'ph:laptop-duotone',
} }
const icon = computed(() => name ? icons[name] : 'ph:question-mark-duotone') const icon = computed(() => icons[name] ?? 'ph:question-mark-duotone')
</script> </script>

View file

@ -1,19 +1,52 @@
<template> <template>
<section id="blog" class="Blog content"> <section id="blog" class="BlogOverview content">
<div class="grid"> <main>
<BlogCard v-for="article in articles" v-bind="makeBlogCard(article)"/> <ul class="category-list">
<li>
<NuxtLink class="inline-flex-row gap-sm side" to="/blog">
<span class="chip" :class="{ 'dark' : route.query.category}">Alle {{ articles?.length }}</span>
</NuxtLink>
</li>
<li v-for="(count, category) in allCategoriesAndCount">
<NuxtLink class="inline-flex-row gap-sm side" :to="`?category=${category}`">
<span class="chip" :class="{ 'dark' : category !== route.query.category}"><BlogCategory :name="category"/> {{ count }}</span>
</NuxtLink>
</li>
</ul>
<div class="grid margin-top-middle article-overview">
<BlogCard v-for="article in firstTen" v-bind="makeBlogCard(article)"/>
</div> </div>
</main>
</section> </section>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { BlogCollectionItem } from '@nuxt/content'
import type { Category } from '../../components/Blog/types' import type { Category } from '../../components/Blog/types'
const { data } = await useAsyncData('articles', () => queryCollection('blog').order('date', 'DESC').limit(10).all()) const route = useRoute()
const articles = data.value! const { data: articles } = await useAsyncData('articles', () => queryCollection('blog').order('date', 'DESC').all())
const makeBlogCard = (article: typeof articles[0]) => ({ const firstTen = computed(() => {
if (route.query.category) {
return articles.value?.filter(article => article.meta.category === route.query.category).slice(0, 10) ?? []
}
return articles.value?.slice(0, 10) ?? []
})
const allCategoriesAndCount = computed(() => {
const categories = {} as Record<Category, number>
articles.value?.forEach(article => {
const category = article.meta.category as Category
if (category) {
categories[category] = (categories[category] ?? 0) + 1
}
})
return categories
})
const makeBlogCard = (article: BlogCollectionItem) => ({
title: article.title, title: article.title,
description: article.description, description: article.description,
image: article.meta.image as string, image: article.meta.image as string,

View file

@ -22,8 +22,8 @@ export default defineNuxtConfig({
'/flatrate', '/flatrate',
'/references', '/references',
'/contact', '/contact',
] ],
} },
}, },
css: [ css: [
@ -43,8 +43,9 @@ export default defineNuxtConfig({
'~/assets/css/burger.css', '~/assets/css/burger.css',
'~/assets/css/teaser.css', '~/assets/css/teaser.css',
'~/assets/css/project.css', '~/assets/css/project.css',
'~/assets/css/blogCard.css', '~/assets/css/blog/card.css',
'~/assets/css/blogAuthor.css', '~/assets/css/blog/author.css',
'~/assets/css/blog/overview.css',
], ],
postcss: { postcss: {
@ -69,7 +70,7 @@ export default defineNuxtConfig({
}, },
head: { head: {
htmlAttrs: { lang: 'de' }, htmlAttrs: { lang: 'de' },
} },
}, },
modules: ['@nuxt/icon', '@nuxt/fonts', '@vueuse/nuxt', '@nuxtjs/seo', '@nuxt/content'], modules: ['@nuxt/icon', '@nuxt/fonts', '@vueuse/nuxt', '@nuxtjs/seo', '@nuxt/content'],
@ -78,8 +79,8 @@ export default defineNuxtConfig({
customCollections: [ customCollections: [
{ {
prefix: 'wf', prefix: 'wf',
dir: './app/assets/icons' dir: './app/assets/icons',
} },
], ],
provider: 'iconify', provider: 'iconify',
serverBundle: 'local', serverBundle: 'local',
@ -94,5 +95,5 @@ export default defineNuxtConfig({
trailingSlash: true, trailingSlash: true,
}, },
compatibilityDate: '2024-12-04' compatibilityDate: '2024-12-04',
}) })