Add latest article section to homepage and back to articles link

This commit is contained in:
2026-04-06 15:34:29 +03:00
parent 999967eb74
commit 09becbd76e
5 changed files with 92 additions and 41 deletions

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
const { data: article } = await useAsyncData("latest-article", async () => {
return queryCollection("articles").order("date", "DESC").first();
});
const date = useDateFormat(new Date(article.value?.date ?? ""), "Do of MMMM YYYY");
const excerpt = computed(() => {
if (!article.value) return "";
const text = article.value.description ?? article.value.body ?? "";
return text.slice(0, 150) + (text.length > 150 ? "..." : "");
});
</script>
<template>
<UContainer
v-if="article"
class="p-4 sm:p-6 lg:p-8 max-w-3xl"
as="section"
>
<span class="text-sm text-gray-500 flex flex-row gap-1 items-center">
Latest article
</span>
<ULink :to="article.path" class="block mt-4">
<div class="flex gap-6 items-start flex-row-reverse">
<NuxtImg
v-if="article.coverImage?.url"
class="w-24 h-24 object-cover rounded-lg shrink-0"
:src="article.coverImage.url.replace('w=1287&h=600', 'w=300&h=300')"
:alt="article.title"
/>
<div class="flex flex-col gap-2">
<h3 class="font-semibold text-lg">{{ article.title }}</h3>
<p class="text-sm text-slate-600 dark:text-slate-400 line-clamp-3">{{ excerpt }}</p>
<time class="text-xs text-slate-500">{{ date }}</time>
</div>
</div>
</ULink>
</UContainer>
</template>

View File

@@ -1,45 +1,47 @@
<template> <template>
<UContainer class="flex flex-col gap-5 p-4 sm:p-6 lg:p-8 max-w-3xl" as="section"> <UContainer
<div class="text-sm text-gray-500">Personal projects</div> class="flex flex-col gap-5 p-4 sm:p-6 lg:p-8 max-w-3xl"
as="section"
>
<div class="text-sm text-gray-500">Personal projects</div>
<div class="grid grid-cols-1 gap-5 md:grid-cols-3"> <div class="grid grid-cols-1 gap-5 md:grid-cols-3">
<Project <Project
title="Ghostwriter" title="Ghostwriter"
image="https://www.ghostwriter.rocks/ghostwriter-logo.png" image="https://app.ghostwriter.rocks/_ipx/_/ghostwriter-logo.png"
url="https://ghostwriter.rocks" url="https://ghostwriter.rocks"
> >
<template v-slot:description> <template v-slot:description>
Creative post-generation for Strava activities with a lightweight, playful product feel. Creative post-generation for Strava activities with a lightweight,
</template> playful product feel.
<template v-slot:subtitle> Compatible with Strava </template> </template>
</Project> <template v-slot:subtitle> Compatible with Strava </template>
</Project>
<Project <Project
title="Places for Zendesk" title="Places for Zendesk"
image="https://990141.apps.zdusercontent.com/990141/assets/1701544308-8c83d0f02afd0b3d7342e5f19304c4b4/logo.png" image="https://990141.apps.zdusercontent.com/990141/assets/1701544308-8c83d0f02afd0b3d7342e5f19304c4b4/logo.png"
url="https://getplaces.co" url="https://getplaces.co"
> >
<template v-slot:description> <template v-slot:description>
Location-aware lead capture designed to connect digital conversations to real-world businesses. Location-aware lead capture designed to connect digital conversations
</template> to real-world businesses.
<template v-slot:subtitle> </template>
Built for Zendesk marketplace <template v-slot:subtitle> Built for Zendesk marketplace </template>
</template> </Project>
</Project>
<Project <Project
title="SneakPeek" title="SneakPeek"
image="https://cdn.livechat-files.com/api/file/developers/img/applications/449B6QFGg/icons/AecAyk1Gg-960x960.png" image="https://cdn.livechat-files.com/api/file/developers/img/applications/449B6QFGg/icons/AecAyk1Gg-960x960.png"
url="https://www.livechat.com/marketplace/apps/sneakpeek" url="https://www.livechat.com/marketplace/apps/sneakpeek"
marketplace="LiveChat" marketplace="LiveChat"
> >
<template v-slot:description <template v-slot:description
>Turn plain links into richer, more informative chat experiences</template >Turn plain links into richer, more informative chat
> experiences</template
<template v-slot:subtitle> >
Built for LiveChat marketplace <template v-slot:subtitle> Built for LiveChat marketplace </template>
</template> </Project>
</Project> </div>
</div> </UContainer>
</UContainer>
</template> </template>

View File

@@ -20,7 +20,14 @@ const { data: article } = useAsyncData(path, async () => {
class="flex flex-col gap-3 prose max-w-3xl dark:prose-invert p-4 sm:p-6 lg:p-8" class="flex flex-col gap-3 prose max-w-3xl dark:prose-invert p-4 sm:p-6 lg:p-8"
as="article" as="article"
> >
<div class="text-sm text-slate-500"> <ULink
class="text-sm text-gray-500 flex flex-row gap-1 items-center group w-fit"
to="/articles"
>
<UIcon class="opacity-100 group-hover:-translate-x-1 transition-transform" name="i-heroicons-arrow-left" />
Back to articles
</ULink>
<div class="text-sm text-slate-500 mt-6">
{{ formatDate(new Date(article.date), "Do of MMMM YYYY") }} {{ formatDate(new Date(article.date), "Do of MMMM YYYY") }}
</div> </div>
<h1 class="text-3xl md:text-4xl font-bold">{{ article.title }}</h1> <h1 class="text-3xl md:text-4xl font-bold">{{ article.title }}</h1>

View File

@@ -12,6 +12,7 @@ useSeoMeta({
<template> <template>
<HomeIntroduction v-motion-fade /> <HomeIntroduction v-motion-fade />
<HomeLatestArticle v-motion-fade :delay="500" />
<HomeBeenWorkingWith v-motion-fade :delay="500" /> <HomeBeenWorkingWith v-motion-fade :delay="500" />
<HomePersonalProjects v-motion-fade :delay="500" /> <HomePersonalProjects v-motion-fade :delay="500" />
<HomeConnect v-motion-fade :delay="500" /> <HomeConnect v-motion-fade :delay="500" />

BIN
public/ghostwriter-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB