add: first post, external posts, new comps
Add first post for monday, add external posts from other authors, add components for internal and external links
This commit is contained in:
parent
427d9ae276
commit
0a481fee5e
19 changed files with 264 additions and 329 deletions
|
@ -24,12 +24,20 @@
|
|||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& small {
|
||||
font-size: 1.2rem;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
font-style: italic;
|
||||
font-weight: lighter;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
& .image {
|
||||
|
@ -47,4 +55,8 @@
|
|||
object-position: center;
|
||||
}
|
||||
}
|
||||
|
||||
& p {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
loading="lazy"
|
||||
width="50"
|
||||
height="50"
|
||||
:srcset="imageSet.join(', ')"
|
||||
:srcset="imageSet"
|
||||
:src="initialImage"
|
||||
aria-hidden="true"
|
||||
:alt="`Profilbild von ${name}`"
|
||||
|
@ -24,9 +24,10 @@ import { getImageSet, getInitialImage } from '../../utils/image'
|
|||
type Props = {
|
||||
name: string
|
||||
date: string
|
||||
img?: string
|
||||
}
|
||||
|
||||
const { name, date } = defineProps<Props>()
|
||||
const { name, date, img } = defineProps<Props>()
|
||||
|
||||
const formatter = new Intl.DateTimeFormat('de-DE', {
|
||||
year: 'numeric',
|
||||
|
@ -36,6 +37,13 @@ const formatter = new Intl.DateTimeFormat('de-DE', {
|
|||
|
||||
const dateFormatted = computed(() => formatter.format(new Date(date)))
|
||||
|
||||
const imageSet = getImageSet('/img/blog/authors/', name)
|
||||
const initialImage = getInitialImage('/img/blog/authors/', name)
|
||||
const imageSet = computed(() => {
|
||||
if (img) return img
|
||||
return getImageSet('/img/blog/authors/', name).join(', ')
|
||||
})
|
||||
|
||||
const initialImage = computed(() => {
|
||||
if (img) return img
|
||||
return getInitialImage('/img/blog/authors/', name)
|
||||
})
|
||||
</script>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<NuxtLink :to="link" class="BlogCard z-2">
|
||||
<NuxtLink :to="link" :external="isExternal" :target="isExternal ? '_blank' : '_self'" class="BlogCard z-2">
|
||||
<div class="image">
|
||||
<img :src="image" alt=" " aria-hidden="true"/>
|
||||
</div>
|
||||
|
@ -14,27 +14,16 @@
|
|||
</p>
|
||||
</main>
|
||||
<footer>
|
||||
<BlogAuthor :name="author.name" :date="date"/>
|
||||
<BlogAuthor :name="author.name" :img="author.img" :date="date"/>
|
||||
</footer>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Category } from './types'
|
||||
import type { BlogCard } from './types'
|
||||
|
||||
type Props = {
|
||||
title: string
|
||||
description: string
|
||||
image: string
|
||||
date: string
|
||||
link: string
|
||||
category: Category
|
||||
author: {
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
const { link } = defineProps<BlogCard>()
|
||||
|
||||
const isExternal = computed(() => link.startsWith('http'))
|
||||
</script>
|
|
@ -18,6 +18,7 @@ const icons: Record<Category, string> = {
|
|||
'tutorial': 'ph:lightbulb-duotone',
|
||||
'news': 'ph:newspaper-duotone',
|
||||
'freelancing': 'ph:laptop-duotone',
|
||||
'extern': 'ph:repeat-duotone',
|
||||
}
|
||||
|
||||
const icon = computed(() => icons[name] ?? 'ph:question-mark-duotone')
|
||||
|
|
|
@ -1 +1,14 @@
|
|||
export type Category = 'story' | 'snippet' | 'tutorial' | 'news' | 'freelancing'
|
||||
export type Category = 'story' | 'snippet' | 'tutorial' | 'news' | 'freelancing' | 'extern'
|
||||
|
||||
export type BlogCard = {
|
||||
title: string
|
||||
description: string
|
||||
image: string
|
||||
date: string
|
||||
link: string
|
||||
category: Category
|
||||
author: {
|
||||
name: string
|
||||
img?: string
|
||||
}
|
||||
}
|
14
app/components/Link/External.vue
Normal file
14
app/components/Link/External.vue
Normal file
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<NuxtLink class="inline-flex-row text" external :to="url" target="_blank">
|
||||
<slot/>
|
||||
<IconLinkExternal/>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type Props = {
|
||||
url: string
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
14
app/components/Link/Internal.vue
Normal file
14
app/components/Link/Internal.vue
Normal file
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<NuxtLink class="inline-flex-row text" :to="url">
|
||||
<slot/>
|
||||
<IconLinkInternal/>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
type Props = {
|
||||
url: string
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
|
@ -15,10 +15,12 @@
|
|||
<span class="chip interactive"><BlogCategory :name="article.category as Category"/></span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<h1 class="margin-top">{{ article.title }}</h1>
|
||||
<h2>{{ article.description }}</h2>
|
||||
<h1 class="margin-top">
|
||||
<span>{{ article.title }}</span>
|
||||
<small>{{ article.description }}</small>
|
||||
</h1>
|
||||
</header>
|
||||
<div class="flex-col gap-default article-text">
|
||||
<div class="flex-col article-text">
|
||||
<ContentRenderer v-if="article" :value="article" :style="{ display: 'contents' }"/>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
</li>
|
||||
</ul>
|
||||
<div class="grid margin-top-middle article-overview">
|
||||
<BlogCard v-for="article in firstTen" v-bind="makeBlogCard(article)"/>
|
||||
<div v-if="firstTen.length < 2"/>
|
||||
<div v-if="firstTen.length < 3"/>
|
||||
<BlogCard v-for="article in allPosts" v-bind="article"/>
|
||||
<div v-if="allPosts.length < 2"/>
|
||||
<div v-if="allPosts.length < 3"/>
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
|
@ -31,7 +31,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import type { BlogCollectionItem } from '@nuxt/content'
|
||||
import type { Category } from '../../components/Blog/types'
|
||||
import type { BlogCard, Category } from '../../components/Blog/types'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
|
@ -41,14 +41,14 @@ const { data: articles } = await useAsyncData('articles', () => queryCollection(
|
|||
.all(),
|
||||
)
|
||||
|
||||
const firstTen = computed(() => {
|
||||
const firstTen = computed<BlogCard[]>(() => {
|
||||
if (route.query.category && Object.keys(allCategoriesAndCount.value).includes(route.query.category as Category)) {
|
||||
return articles.value?.filter(article => article.category === route.query.category).slice(0, 10) ?? []
|
||||
return (articles.value?.filter(article => article.category === route.query.category).slice(0, 10) ?? []).map(makeBlogCard)
|
||||
}
|
||||
return articles.value?.slice(0, 10) ?? []
|
||||
return (articles.value?.slice(0, 10) ?? []).map(makeBlogCard)
|
||||
})
|
||||
|
||||
const allCategoriesAndCount = computed(() => {
|
||||
const allCategoriesAndCount = computed<Record<Category, number>>(() => {
|
||||
const categories = {} as Record<Category, number>
|
||||
articles.value?.forEach(article => {
|
||||
const category = article.category as Category
|
||||
|
@ -59,7 +59,23 @@ const allCategoriesAndCount = computed(() => {
|
|||
return categories
|
||||
})
|
||||
|
||||
const makeBlogCard = (article: BlogCollectionItem) => ({
|
||||
const { data: externalPostsRaw } = useFetch('/api/external-posts', { method: 'POST' })
|
||||
|
||||
const externalPosts = computed<BlogCard[]>(() => externalPostsRaw.value?.flatMap(externalBlog => {
|
||||
return externalBlog.posts.map(post => ({
|
||||
title: post.title,
|
||||
description: post.excerpt,
|
||||
image: 'asdf',
|
||||
date: post.date,
|
||||
link: post.url,
|
||||
category: 'extern',
|
||||
author: externalBlog.author,
|
||||
}))
|
||||
}))
|
||||
|
||||
const allPosts = computed<BlogCard[]>(() => [...(firstTen.value ?? []), ...(externalPosts.value ?? [])].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()))
|
||||
|
||||
const makeBlogCard = (article: BlogCollectionItem): BlogCard => ({
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
image: article.thumbnail as string,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue