d668aa0fdf
Replaces Astro Rocket demo content with Armarium branding and adds complete DE/FR/IT/EN translations across all pages. Branding & content (v0.7.0): - Add horizontal SVG logo to navbar with currentColor dark mode support - Rewrite homepage with Armarium hero, 6 feature cards, trust bar, Zürich coat of arms SVG, and CTA; shared HomePage.astro component - Add privacy page (/datenschutz) with 6 Infomaniak certification cards and 8-section policy (ISO 27001:2022, Swiss Hosting, nDSG/GDPR, etc.) - Add legal notice page (/impressum) - Rewrite about, contact, 404 pages with Armarium content - Add features page (/projects) from projects content collection - Add language switcher dropdown (LanguageSwitcherDropdown.astro) - Add single launch blog post; remove all demo blog/project content - Set up i18n foundation: astro.config.mjs, ui.ts, utils.ts Full i18n (v0.8.0): - Add all pages in FR/IT/EN: about, contact, blog, features, privacy, legal notice — 28 locale variants total - Language switcher visible in every layout (PageLayout, BlogLayout, ProjectLayout, LandingLayout) with translated nav items - Locale-aware nav and footer hrefs via nav.*.href keys in ui.ts - Shared page components (AboutPage, ContactPage, FeaturesIndexPage, BlogIndexPage) accept locale prop; locale pages are 4-line wrappers - Extend content.config.ts locale enum with de and it Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
98 lines
3.9 KiB
Plaintext
98 lines
3.9 KiB
Plaintext
---
|
|
import PageLayout from '@/layouts/PageLayout.astro';
|
|
import Icon from '@/components/ui/primitives/Icon/Icon.astro';
|
|
import Badge from '@/components/ui/data-display/Badge/Badge.astro';
|
|
import Card from '@/components/ui/data-display/Card/Card.astro';
|
|
import Button from '@/components/ui/form/Button/Button.astro';
|
|
import { Hero } from '@/components/hero';
|
|
import { getCollection } from 'astro:content';
|
|
import { useTranslations } from '@/i18n/utils';
|
|
import type { Locale } from '@/i18n/ui';
|
|
|
|
interface Props {
|
|
locale: Locale;
|
|
}
|
|
|
|
const { locale } = Astro.props;
|
|
const t = useTranslations(locale);
|
|
|
|
const items = await getCollection('projects', ({ data }) => !data.draft);
|
|
const features = items.sort((a, b) => a.data.order - b.data.order);
|
|
|
|
const iconMap: Record<string, string> = {
|
|
'budget-uebersicht': 'layout-dashboard',
|
|
'transaktionen': 'list',
|
|
'kategorien-berichte': 'pie-chart',
|
|
'mehrere-konten': 'wallet',
|
|
'sparziele': 'target',
|
|
'datenschutz-sicherheit': 'shield-check',
|
|
};
|
|
|
|
const featuresHref = t('nav.features.href');
|
|
---
|
|
|
|
<PageLayout
|
|
title={`${t('nav.features')} — Armarium`}
|
|
description={t('features.description')}
|
|
locale={locale}
|
|
>
|
|
<Hero layout="centered" size="sm">
|
|
<Badge slot="badge" variant="brand" pill>
|
|
<Icon name="zap" size="sm" />
|
|
{t('features.badge')}
|
|
</Badge>
|
|
<h1 slot="title">
|
|
{t('features.title').split(' ').slice(0, -1).join(' ')} <span class="text-brand-500 [-webkit-text-fill-color:var(--color-brand-500)]">{t('features.title').split(' ').slice(-1)[0]}</span>
|
|
</h1>
|
|
<p slot="description">{t('features.description')}</p>
|
|
</Hero>
|
|
|
|
<!-- Feature cards -->
|
|
<section class="py-[var(--space-section-md)] bg-background-secondary border-t border-border">
|
|
<div class="mx-auto max-w-6xl px-6">
|
|
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3" data-reveal>
|
|
{features.map((feature) => {
|
|
const slug = feature.id.replace(/\.mdx?$/, '');
|
|
const icon = iconMap[slug] ?? 'check-circle';
|
|
return (
|
|
<Card variant="elevated" hover padding="lg" href={`/projects/${slug}`} class="group flex flex-col">
|
|
<div class="flex flex-1 flex-col">
|
|
<div class="mb-4 flex items-start justify-between gap-4">
|
|
<div class="w-11 h-11 rounded-xl bg-gradient-to-br from-brand-500/20 to-brand-500/5 flex items-center justify-center text-brand-500 shrink-0">
|
|
<Icon name={icon} size="md" />
|
|
</div>
|
|
<Icon name="arrow-up-right" size="sm" class="text-foreground-muted group-hover:text-brand-500 transition-colors shrink-0" />
|
|
</div>
|
|
<h3 class="font-display text-lg font-bold text-foreground mb-2">{feature.data.title}</h3>
|
|
<p class="text-sm text-foreground-muted leading-relaxed flex-1">{feature.data.description}</p>
|
|
<div class="flex flex-wrap gap-1.5 mt-4">
|
|
{feature.data.tags.map((tag) => (
|
|
<Badge variant="brand">{tag}</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- CTA -->
|
|
<section class="py-[var(--space-section-md)] bg-background border-t border-border">
|
|
<div class="mx-auto max-w-2xl px-6 text-center" data-reveal>
|
|
<h2 class="font-display text-4xl font-bold text-foreground mb-4 text-balance">{t('features.cta.title')}</h2>
|
|
<p class="text-lg text-foreground-muted mb-8 text-balance">{t('features.cta.desc')}</p>
|
|
<div class="flex flex-col sm:flex-row gap-4 justify-center">
|
|
<Button size="lg" href="/register">
|
|
{t('cta.register')}
|
|
<Icon name="arrow-right" size="sm" />
|
|
</Button>
|
|
<Button size="lg" variant="outline" href="/login">
|
|
{t('features.cta.login')}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</PageLayout>
|