feat: Update home page notices and privacy information in English, Spanish, and French; add public guides and routing for guides
All checks were successful
Deploy to production / deploy (push) Successful in 2m11s

- Changed beta notice to service notice on home page translations for English, Spanish, and French.
- Updated privacy information to reflect transparency and continuous maintenance.
- Added new public guides content with detailed sections for various topics.
- Implemented routing for guide list and individual guide articles.
- Created new components for displaying guides and articles.
This commit is contained in:
Torsten Schulz (local)
2026-05-18 14:37:04 +02:00
parent e87ed85867
commit 072d578c88
14 changed files with 833 additions and 81 deletions

View File

@@ -1,8 +1,5 @@
<template>
<div class="no-login-view">
<div class="beta-banner" role="status" aria-live="polite">
<strong>{{ $t('home.betaNoticeLabel') }}</strong> {{ $t('home.betaNoticeText') }}
</div>
<div class="home-structure">
<div class="mascot">
<Character3D gender="male" :lightweight="true" />
@@ -40,8 +37,8 @@
<p>{{ $t('home.nologin.falukantShort.text') }}</p>
</article>
<article>
<h3>{{ $t('home.nologin.privacyBeta.title') }}</h3>
<p>{{ $t('home.nologin.privacyBeta.text') }}</p>
<h3>{{ $t('home.nologin.privacyInfo.title') }}</h3>
<p>{{ $t('home.nologin.privacyInfo.text') }}</p>
</article>
</div>
@@ -259,18 +256,6 @@ export default {
</script>
<style scoped>
.beta-banner {
width: min(100%, var(--content-max-width));
background: linear-gradient(180deg, #fff2cf 0%, #fde7b2 100%);
border: 1px solid rgba(201, 130, 31, 0.24);
color: #8a5a12;
padding: 10px 14px;
margin: 0 0 14px 0;
text-align: center;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-soft);
}
.home-structure {
display: flex;
align-items: stretch;

View File

@@ -0,0 +1,219 @@
<template>
<main v-if="guide" class="guide-page">
<nav class="breadcrumb" aria-label="Breadcrumb">
<router-link to="/">YourPart</router-link>
<span>/</span>
<router-link to="/ratgeber">Ratgeber</router-link>
<span>/</span>
<span>{{ guide.category }}</span>
</nav>
<article class="guide-article">
<header class="guide-header">
<p class="guide-category">{{ guide.category }}</p>
<h1>{{ guide.title }}</h1>
<p class="guide-description">{{ guide.description }}</p>
<p class="guide-updated">Aktualisiert: {{ formattedDate }}</p>
</header>
<section v-for="section in guide.sections" :key="section.heading" class="guide-section">
<h2>{{ section.heading }}</h2>
<p v-for="paragraph in section.paragraphs" :key="paragraph">{{ paragraph }}</p>
</section>
</article>
<aside class="related-guides" aria-label="Weitere Ratgeber">
<h2>Weitere Ratgeber</h2>
<ul>
<li v-for="item in relatedGuides" :key="item.slug">
<router-link :to="`/ratgeber/${item.slug}`">{{ item.title }}</router-link>
</li>
</ul>
</aside>
</main>
<main v-else class="guide-page">
<article class="guide-article">
<h1>Ratgeber nicht gefunden</h1>
<p>Der gesuchte Beitrag ist nicht vorhanden.</p>
<router-link to="/ratgeber">Zur Ratgeber-Übersicht</router-link>
</article>
</main>
</template>
<script>
import { getPublicGuide, publicGuides } from '@/content/publicGuides.js';
import { applySeo, buildAbsoluteUrl } from '@/utils/seo.js';
export default {
name: 'GuideArticleView',
computed: {
guide() {
return getPublicGuide(this.$route.params.slug);
},
formattedDate() {
if (!this.guide?.updatedAt) return '';
return new Date(this.guide.updatedAt).toLocaleDateString('de-DE');
},
relatedGuides() {
if (!this.guide) return publicGuides.slice(0, 4);
return publicGuides
.filter((item) => item.slug !== this.guide.slug)
.slice(0, 4);
},
},
watch: {
'$route.params.slug': {
immediate: true,
handler() {
this.applyGuideSeo();
},
},
},
methods: {
applyGuideSeo() {
const guide = getPublicGuide(this.$route.params.slug);
if (!guide) {
applySeo({
title: 'Ratgeber nicht gefunden | YourPart',
description: 'Der gesuchte YourPart-Ratgeber ist nicht vorhanden.',
canonicalPath: '/ratgeber',
robots: 'noindex, follow',
});
return;
}
const canonicalPath = `/ratgeber/${guide.slug}`;
applySeo({
title: `${guide.title} | YourPart Ratgeber`,
description: guide.description,
keywords: `${guide.category}, YourPart, Ratgeber, Community, Vokabeltrainer, Falukant, Browsergames`,
canonicalPath,
robots: 'index, follow',
type: 'article',
lang: 'de',
locale: 'de_DE',
includeHreflangAlternates: false,
jsonLd: [
{
'@context': 'https://schema.org',
'@type': 'Article',
headline: guide.title,
description: guide.description,
dateModified: guide.updatedAt,
datePublished: guide.updatedAt,
inLanguage: 'de',
url: buildAbsoluteUrl(canonicalPath),
publisher: {
'@type': 'Organization',
name: 'YourPart',
url: buildAbsoluteUrl('/'),
},
},
],
});
},
},
};
</script>
<style scoped>
.guide-page {
max-width: 980px;
margin: 0 auto;
padding: 32px 20px 72px;
color: #253043;
}
.breadcrumb {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 22px;
font-size: 0.9rem;
color: #5f6b7a;
}
.breadcrumb a {
color: #245da8;
font-weight: 700;
text-decoration: none;
}
.guide-article {
max-width: 820px;
}
.guide-header {
margin-bottom: 34px;
}
.guide-category {
margin: 0 0 10px;
color: #245da8;
font-size: 0.85rem;
font-weight: 800;
text-transform: uppercase;
}
.guide-header h1 {
margin: 0;
font-size: clamp(2rem, 4vw, 3rem);
line-height: 1.12;
}
.guide-description {
margin: 18px 0 0;
font-size: 1.12rem;
line-height: 1.7;
color: #46556a;
}
.guide-updated {
margin: 14px 0 0;
color: #667085;
font-size: 0.9rem;
}
.guide-section {
margin-top: 34px;
}
.guide-section h2 {
margin: 0 0 12px;
font-size: 1.45rem;
line-height: 1.25;
}
.guide-section p {
margin: 0 0 16px;
font-size: 1.02rem;
line-height: 1.78;
}
.related-guides {
margin-top: 48px;
padding-top: 26px;
border-top: 1px solid rgba(36, 93, 168, 0.16);
}
.related-guides h2 {
margin: 0 0 14px;
font-size: 1.2rem;
}
.related-guides ul {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 10px 18px;
margin: 0;
padding: 0;
list-style: none;
}
.related-guides a {
color: #245da8;
font-weight: 700;
text-decoration: none;
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<main class="guide-list-page">
<header class="guide-list-header">
<p class="guide-kicker">YourPart Ratgeber</p>
<h1>Ratgeber zu Falukant, Vokabeltrainer, Community und Browsergames</h1>
<p>
Oeffentliche Artikel erklaeren die wichtigsten Bereiche von YourPart und geben neuen Besuchern genug Kontext,
bevor sie ein Konto erstellen oder einen Kurs beziehungsweise ein Spiel starten.
</p>
</header>
<section class="guide-grid" aria-label="Ratgeber">
<article v-for="guide in guides" :key="guide.slug" class="guide-card">
<p>{{ guide.category }}</p>
<h2>
<router-link :to="`/ratgeber/${guide.slug}`">{{ guide.title }}</router-link>
</h2>
<span>{{ guide.description }}</span>
</article>
</section>
</main>
</template>
<script>
import { publicGuides } from '@/content/publicGuides.js';
export default {
name: 'GuideListView',
computed: {
guides() {
return publicGuides;
},
},
};
</script>
<style scoped>
.guide-list-page {
max-width: 1120px;
margin: 0 auto;
padding: 44px 20px 72px;
color: #253043;
}
.guide-list-header {
max-width: 820px;
}
.guide-kicker {
margin: 0 0 10px;
color: #245da8;
font-size: 0.85rem;
font-weight: 800;
text-transform: uppercase;
}
.guide-list-header h1 {
margin: 0;
font-size: clamp(2rem, 4vw, 3.2rem);
line-height: 1.1;
}
.guide-list-header p:last-child {
margin: 18px 0 0;
color: #46556a;
font-size: 1.08rem;
line-height: 1.7;
}
.guide-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 18px;
margin-top: 34px;
}
.guide-card {
padding: 22px;
border: 1px solid rgba(36, 93, 168, 0.14);
border-radius: 8px;
background: #fff;
}
.guide-card p {
margin: 0 0 8px;
color: #245da8;
font-size: 0.78rem;
font-weight: 800;
text-transform: uppercase;
}
.guide-card h2 {
margin: 0 0 10px;
font-size: 1.16rem;
line-height: 1.3;
}
.guide-card a {
color: #1d2b3f;
text-decoration: none;
}
.guide-card span {
color: #536276;
line-height: 1.62;
}
</style>