Skip to content

Commit

Permalink
feat: Update to Nuxt Content v3 (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
HugoRCD authored Jan 13, 2025
1 parent 6ab1b6c commit d29a5f4
Show file tree
Hide file tree
Showing 32 changed files with 2,105 additions and 1,633 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ logs
# Local env files
.env
.vercel
.data

rt_base
rt_base
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ WORKDIR /app

COPY --from=build /app/.output .output

RUN apk update && apk add --no-cache curl

EXPOSE 3000

CMD ["node", ".output/server/index.mjs"]
18 changes: 8 additions & 10 deletions app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,14 @@ const { locale } = useI18n()
class="font-geist text-[var(--ui-text)] transition-colors duration-300 selection:bg-white/60 selection:text-zinc-800"
>
<Body>
<FolioMeta>
<LayoutScrollToTop />
<NuxtLayout>
<UApp :locale="locales[locale]">
<NuxtPage />
</UApp>
</NuxtLayout>
<Toaster close-button />
<DotPattern class="absolute inset-0 -z-10 size-full fill-white/5 [mask-image:radial-gradient(white,transparent_85%)]" />
</FolioMeta>
<LayoutScrollToTop />
<NuxtLayout>
<UApp :locale="locales[locale]">
<NuxtPage />
</UApp>
</NuxtLayout>
<Toaster close-button />
<DotPattern class="absolute inset-0 -z-10 size-full fill-white/5 [mask-image:radial-gradient(white,transparent_85%)]" />
</Body>
</Html>
</template>
2 changes: 1 addition & 1 deletion app/assets/style/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ html, body, #__nuxt, #__layout {
}

.writing {
@apply antialiased font-geist text-neutral-700/90 dark:text-neutral-400/80 leading-relaxed;
@apply font-geist text-neutral-700/90 dark:text-neutral-300 leading-relaxed;

h1 {
@apply text-2xl sm:text-3xl text-zinc-900 dark:text-zinc-100 italic font-[600] mb-6 mt-8;
Expand Down
37 changes: 20 additions & 17 deletions app/components/FolioMeta.vue
Original file line number Diff line number Diff line change
@@ -1,45 +1,48 @@
<script setup lang="ts">
import type { ContentEnCollectionItem, ContentFrCollectionItem } from '@nuxt/content'
useScriptPlausibleAnalytics({
domain: 'canvas.hrcd.fr',
scriptInput: {
src: 'https://analytics.hrcd.fr/js/script.js',
},
})
const { page, isWriting } = defineProps<{
page: ContentEnCollectionItem | ContentFrCollectionItem
isWriting: boolean
}>()
const route = useRoute()
const { link, seo, profile } = useAppConfig()
const getPageSEO = () => ({
title: seo.title,
description: seo.description,
})
const isWriting = computed(() => route.path.includes('/articles/'))
const pageSEO = computed(() => ({
title: isWriting ? page?.title : page?.title || seo.title,
description: isWriting ? page?.description : page?.description || seo.description,
}))
const getTitleTemplate = (title: string | undefined) => {
if (route.path === '/') return title || `${seo.title}`
if (isWriting.value) return title
if (isWriting) return title
return `${title} | ${seo.title}`
}
const pageSEO = getPageSEO()
useSeoMeta({
ogSiteName: seo.title,
ogTitle: pageSEO.title,
ogDescription: pageSEO.description,
ogType: 'website',
ogTitle: pageSEO.value.title,
ogDescription: pageSEO.value.description,
ogType: isWriting ? 'article' : 'website',
ogUrl: seo.url,
author: profile.name,
title: pageSEO.title,
description: pageSEO.description,
twitterTitle: pageSEO.title,
twitterDescription: pageSEO.description,
title: pageSEO.value.title,
description: pageSEO.value.description,
twitterTitle: pageSEO.value.title,
twitterDescription: pageSEO.value.description,
twitterCard: 'summary_large_image',
})
useHead({
title: seo.title,
title: pageSEO.value.title,
titleTemplate: getTitleTemplate,
meta: [
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
Expand Down
32 changes: 25 additions & 7 deletions app/components/content/About.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
<script setup lang="ts">
const { data: stack } = await useAsyncData('stack', () => queryContent('/stack').findOne())
const stack = await queryCollection('stack').first()
</script>

<template>
<section class="mx-auto mt-4 flex max-w-4xl flex-col p-7 sm:mt-20 text-[var(--ui-text-toned)]">
<h1 class="font-newsreader italic text-white-shadow text-center text-4xl">
<ContentSlot :use="$slots.title" />
<slot
name="title"
mdc-unwrap="p"
/>
</h1>
<h2 class="text-center text-lg font-extralight italic text-muted">
<ContentSlot :use="$slots.subtitle" />
<slot
name="subtitle"
mdc-unwrap="p"
/>
</h2>
<Divider class="mb-8 mt-2" />
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
Expand All @@ -18,22 +24,34 @@ const { data: stack } = await useAsyncData('stack', () => queryContent('/stack')
Intro
</h3>
<div class="flex flex-col gap-4 text-primary">
<ContentSlot :use="$slots.intro" />
<slot
name="intro"
mdc-unwrap="p"
/>
</div>
<AboutSignature class="absolute -bottom-24 right-0 hidden w-40 sm:block" />
<AboutSignature class="black absolute -bottom-24 -right-2 w-32 sm:hidden" />
</div>
</div>
<Divider class="my-8" />
<ContentSlot :use="$slots.experiences" />
<slot
name="experiences"
mdc-unwrap="p"
/>
<Divider class="my-8" />
<div class="flex flex-col gap-3">
<div class="mb-6 flex flex-col gap-1">
<h3 class="text-white-shadow font-newsreader italic text-3xl">
<ContentSlot :use="$slots.stack_title" />
<slot
name="stack_title"
mdc-unwrap="p"
/>
</h3>
<p>
<ContentSlot :use="$slots.stack_description" />
<slot
name="stack_description"
mdc-unwrap="p"
/>
</p>
</div>
<div class="flex flex-wrap gap-4">
Expand Down
98 changes: 49 additions & 49 deletions app/components/content/Contact.vue
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
<script setup lang="ts">
import type { ContactEmail } from '~~/types/ContactEmail'
import * as z from 'zod'
import type { FormSubmitEvent } from '#ui/types'
const { profile } = useAppConfig()
const { t } = useI18n()
const email = ref('')
const message = ref('')
const phone = ref('')
const fullname = ref('')
const subject = ref('')
const loading = ref(false)
const state = ref({
email: '',
message: '',
phone: '',
fullname: '',
subject: '',
})
const contactData = computed(() => {
return {
email: email.value,
message: message.value,
phone: phone.value,
fullname: fullname.value,
subject: subject.value,
} as ContactEmail
const schema = z.object({
email: z.string().email('Invalid email'),
message: z.string().min(10, 'Message is too short'),
subject: z.string().min(5, 'Subject is too short'),
fullname: z.string().min(3, 'Name is too short'),
})
type Schema = z.output<typeof schema>
async function submitForm() {
const loading = ref(false)
async function onSubmit(event: FormSubmitEvent<Schema>) {
loading.value = true
try {
await $fetch('/api/sendEmail', {
method: 'POST',
body: contactData.value,
body: event.data,
})
email.value = ''
message.value = ''
phone.value = ''
fullname.value = ''
subject.value = ''
state.value = {
email: '',
message: '',
phone: '',
fullname: '',
subject: '',
}
toast.success(t('contact.success'))
}
catch (error) {
Expand All @@ -46,27 +49,33 @@ async function submitForm() {
<template>
<section class="mx-auto mt-4 flex max-w-4xl flex-col p-7 sm:mt-20">
<h1 class="font-newsreader italic text-white-shadow text-center text-4xl">
<ContentSlot :use="$slots.title" />
<slot
name="title"
mdc-unwrap="p"
/>
</h1>
<h2 class="text-center text-lg font-extralight italic text-muted">
<ContentSlot :use="$slots.subtitle" />
<slot
name="subtitle"
mdc-unwrap="p"
/>
</h2>
<Divider class="mb-8 mt-2" />
<div class="flex flex-col sm:items-center sm:justify-between">
<form
<UForm
:state
:schema
class="flex w-full max-w-[40rem] flex-col gap-3"
@submit.prevent="submitForm"
@submit="onSubmit"
>
<UFormField
label="Fullname"
name="fullname"
required
>
<UInput
id="full-name"
v-model="fullname"
v-model="state.fullname"
type="text"
required
name="fullname"
autocomplete="name"
class="w-full"
placeholder="John Doe"
Expand All @@ -75,14 +84,11 @@ async function submitForm() {

<UFormField
label="Email"
name="email"
required
>
<UInput
id="email"
v-model="email"
type="email"
required
name="email"
v-model="state.email"
autocomplete="email"
class="w-full"
placeholder="[email protected]"
Expand All @@ -91,12 +97,10 @@ async function submitForm() {

<UFormField
label="Phone"
name="phone"
>
<UInput
id="phone"
v-model="phone"
type="text"
name="phone"
v-model="state.phone"
autocomplete="tel"
class="w-full"
placeholder="123-456-7890"
Expand All @@ -105,28 +109,24 @@ async function submitForm() {

<UFormField
label="Subject"
name="subject"
required
>
<UInput
id="subject"
v-model="subject"
type="text"
name="subject"
v-model="state.subject"
class="w-full"
:placeholder="$t('contact.subject')"
/>
</UFormField>

<UFormField
label="Message"
name="message"
required
>
<UTextarea
id="message"
v-model="message"
v-model="state.message"
autoresize
required
name="message"
class="w-full"
:rows="4"
placeholder="Lets work together!"
Expand All @@ -141,7 +141,7 @@ async function submitForm() {
{{ $t("contact.submit") }}
</UButton>
</div>
</form>
</UForm>
<Divider class="my-10" />
<div class="flex w-full flex-col items-center justify-between gap-4 sm:flex-row">
<div class="flex flex-col gap-3">
Expand Down
10 changes: 8 additions & 2 deletions app/components/content/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@
data-animate
>
<h1 class="mx-auto font-medium text-center text-pretty bg-gradient-to-b from-white/90 to-white/30 bg-clip-text text-3xl text-transparent lg:max-w-3xl lg:text-4xl">
<ContentSlot :use="$slots.hero_title" />
<slot
name="hero_title"
mdc-unwrap="p"
/>
</h1>

<!-- subtitle -->
<h2 class="mx-auto mt-4 max-w-xl text-center text-lg antialiased text-white/60">
<ContentSlot :use="$slots.hero_subtitle" />
<slot
name="hero_subtitle"
mdc-unwrap="p"
/>
</h2>
</div>

Expand Down
Loading

0 comments on commit d29a5f4

Please sign in to comment.