fix: FAQ in NuxtContent

FAQ converted to NuxtContent
This commit is contained in:
webfussel 2025-06-10 20:03:42 +02:00
parent 8668b96eff
commit 48efe0f75b
14 changed files with 151 additions and 96 deletions

View file

@ -1,7 +1,7 @@
<template>
<div class="Burger" :class="{ open }">
<nav class="z-4" ref="navElement">
<ul>
<ul class="row">
<li v-for="({label, to, aria, icon}) in navigation" :key="label">
<NuxtLink :to="to" :aria-label="aria" active-class="active" class="inline-flex-row big-gap" @click="close">
<Icon :name="icon" mode="svg" />

View file

@ -7,7 +7,7 @@
<main>
<small class="customer">{{ company }}</small>
<h3 class="title">{{ title }}</h3>
<ul>
<ul class="row">
<li v-for="skill in tech">
<Technology v-bind="skill" link="" />
</li>

View file

@ -43,8 +43,8 @@
</article>
</div>
<div class="flex-col gap-sm margin-top">
<Spoiler v-for="entry in faq" v-bind="entry" class="z-2" />
<div v-if="faq" class="flex-col gap-sm margin-top">
<ContentRenderer :value="faq" :style="{ display: 'contents' }" />
</div>
</section>
</template>
@ -71,7 +71,8 @@ const intl = new Intl.NumberFormat(
currency: 'EUR',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
})
}
)
const oneOff : Service[] = [
{
@ -128,36 +129,5 @@ const oneOff : Service[] = [
},
]
const faq = [
{
header: 'Warum machst du keine Stundensätze?',
content: [
'Ich finde Stundensätze haben für beide Seiten nur Nachteile:',
'Wenn ich schnell und gut arbeite, dann bekomme ich weniger Geld. Hab ich mal einen Knoten im Gehirn und brauche sehr lange, muss der Kunde mehr zahlen.',
'Klar kann man sagen, dass sich das irgendwann ausgleichen könnte - aber so weit will ich es garnicht erst kommen lassen.'
]
},
{
header: 'Welche Themen bietest du für deine Schulungen an?',
content: [
'Sprachen: JavaScript, TypeScript, HTML, CSS',
'Frameworks: Vue, Nuxt',
]
},
{
header: 'Wo finden die Schulungen statt?',
content: [
'Die Schulungen finden online statt. Normalerweise nutze ich dafür Google Meet, aber wenn du oder deine Firma eine andere Plattform wünschen und bereitstellen bin ich natürlich flexibel.',
'Wenn sich deine Firma in der Nähe meines Wohnortes befindet - und damit meine ich "In einer Stunde mit der Straßenbahn zu erreichen", dann kann alles natürlich auch vor Ort stattfinden.',
]
},
{
header: 'Ich hab ein cooles Projekt! Aber kein Geld...',
content: [
'Tja.',
'Ne, awas. Meld dich einfach trotzdem über meine E-Mail-Adresse und vielleicht finden wir eine Lösung.'
]
}
]
const faq = await queryCollection('faq').path('/snippets/faq/booking').first()
</script>

View file

@ -15,7 +15,7 @@
<p>Falls du irgendwo einen anderen Social Media Account von mir findest, der nicht hier aufgelistet ist, aber aktiv postet, dann ist dieser höchstwahrscheinlich <Highlight>Fake</Highlight>.
<br />Meld' dich gerne bei mir, wenn du so einen findest.
</p>
<ul class="social-media">
<ul class="row social-media">
<li v-for="({icon, name, ...rest}) in socials" :key="rest.href">
<a v-bind="rest" target="_blank">
<Icon :name="icon" :alt="rest['aria-label']" size="1.5em" mode="svg" />

View file

@ -43,8 +43,8 @@
</article>
</div>
<div class="flex-col gap-sm margin-top">
<Spoiler v-for="entry in faq" v-bind="entry" class="z-2" />
<div v-if="faq" class="flex-col gap-sm margin-top">
<ContentRenderer :value="faq" :style="{ display: 'contents' }" />
</div>
</section>
</template>
@ -138,48 +138,5 @@ const flatrate : Service[] =
}
]
const faq = [
{
header: 'Was ist eine Entwickler-Flatrate?',
content: [
'Die Entwickler-Flatrate ist ein Angebot, bei dem du für eine bestimmte Zeit eine bestimmte Menge an Leistungen erhältst.',
'Sie wird auch oft als sogenannter "Retainer" bezeichnet und ist eine günstige Art immer wieder anfallende Aufgaben auszulagern.'
]
},
{
header: 'Wie läuft die Zusammenarbeit ab?',
content: [
'Nach einer ersten Analyse deines Projekts legen wir gemeinsam den Umfang und die monatlichen Aufgaben fest. Du kannst mich je nach Paket auf den vereinbarten Wegen jederzeit kontaktieren.',
'Die Bearbeitung erfolgt innerhalb der vereinbarten Fristen.',
]
},
{
header: 'Wie wird abgerechnet?',
content: [
'Du zahlst monatlich einen festen Betrag im Prepaid-Format unabhängig davon wie viele Aufgaben tatsächlich anfallen.',
'Dadurch kannst du dein Budget besser einplanen und Aufgaben verteilen.'
]
},
{
header: 'Kann ich den Retainer jederzeit kündigen?',
content: [
'Ja klar. Du musst dabei nur die Mindestvertragslaufzeit beachten - die bezahlte Leistung steht dir weiterhin zu.',
'Falls wir merken, dass wir für eine Zusammenarbeit mehr als ungeeignet sind, bekommst du dein Geld anteilig der verbleibenden Zeit zurück und verlierst den Anspruch auf Leistungen ab diesem Zeitpunkt.'
]
},
{
header: 'Was passiert, wenn ich dich mal weniger brauche?',
content: [
'Der monatliche feste Betrag bleibt bestehen. Ähnlich wie bei einer Versicherung bezahlst du hier für den Fall, dass du mich brauchst und erhältst dann entsprechend die vereinbarten Leistungen.',
'Wenn du merkst, dass du mich nicht mehr brauchst, dann kannst du den Vertrag jederzeit kündigen und die Leistungen auslaufen lassen.'
]
},
{
header: 'Für wen lohnt sich sowas überhaupt?',
content: [
'Vor allem Unternehmen, bei denen immer wieder Aufgaben anfallen für die sich aber kein komplettes Projekt oder gar eine feste Stelle in der Firma lohnt, profitieren von dieser Art von Leistung.',
'Sie ist ideal für eine langfristige und zuverlässige Zusammenarbeit.'
]
}
]
const faq = await queryCollection('faq').path('/snippets/faq/flatrate').first()
</script>

View file

@ -1,18 +1,18 @@
<template>
<footer class="Footer flex-col gap-default">
<ul class="sitemap gap-default">
<ul class="row sitemap gap-default">
<li v-for="{ label, ...rest} in nav" :key="label">
<NuxtLink v-bind="rest">{{label}}</NuxtLink>
</li>
</ul>
<ul class="sitemap gap-default">
<ul class="row sitemap gap-default">
<li v-for="({icon, ...rest}) in socials" :key="rest.href">
<a v-bind="rest" target="_blank">
<Icon :name="icon" :alt="rest['aria-label']" size="1.5em" mode="svg" />
</a>
</li>
</ul>
<ul class="sitemap gap-default">
<ul class="row sitemap gap-default">
<li class="tip-container">
<Icon name="wf:cookie-slash" size="1.5rem" mode="svg" />
<span class="tip">Ohne Cookies</span>

View file

@ -12,7 +12,7 @@
<Icon name="ph:waves" mode="svg" size="2em" />
</button>
<nav>
<ul class="main-nav">
<ul class="row main-nav">
<li v-for="({label, to, aria, icon}) in navigation" :key="label">
<NuxtLink :to="to" :aria-label="aria" active-class="active" class="inline-flex-row big-gap">
<Icon :name="icon" mode="svg" />

View file

@ -9,7 +9,7 @@
</div>
<Card title="Technologien" titleTag="h3" class="margin-top tech-list">
<p>Neben den klassischen Webentwicklungsstandards JavaScript, HTML und CSS biete ich außerdem folgende Technologien.</p>
<ul class="gap-default margin-top-small">
<ul class="row gap-default margin-top-small">
<li v-for="tech in technologies">
<Technology v-bind="tech" size="l"/>
</li>

View file

@ -1,16 +1,15 @@
<template>
<details class="Spoiler" :open="open" @click="toggle">
<summary><Icon class="icon" :name="icon" mode="svg" />{{ header }}</summary>
<details class="Spoiler" :open="open">
<summary @click="toggle"><Icon class="icon" :name="icon" mode="svg" />{{ title }}</summary>
<div>
<p v-for="text in content">{{ text }}</p>
<slot />
</div>
</details>
</template>
<script setup lang="ts">
type Props = {
header: string
content: string[]
title: string
}
defineProps<Props>()