managarten/picture/apps/landing/src/pages/features/[...slug].astro
Till-JS 36b85fc8a0 fix(picture): migrate to Astro 5.x content collections and fix TypeScript errors
- Rename dynamic routes from [slug].astro to [...slug].astro for multi-segment paths
- Replace deprecated entry.slug with entry.id across all components and utils
- Fix TypeScript implicit any types in TemplateFilters and prompt-templates
- Add proper type narrowing for feature.note in pricing page
- Remove unused marked import from FAQCard
- Delete invalid placeholder content files
- Add shared-landing-ui dependency and integrate StepsSection/PricingSection
- Update tailwind config with shared-landing-ui content paths
- Add global.css with Indigo/Violet dark theme variables

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 18:13:57 +01:00

225 lines
6.5 KiB
Text

---
import { getCollection } from 'astro:content';
import type { CollectionEntry } from 'astro:content';
import Layout from '@layouts/Layout.astro';
import { getRelatedFeatures } from '@/utils/features';
import { t } from '../../i18n';
import { localizePath } from '../../i18n';
import FeatureCard from '@components/features/FeatureCard.astro';
export async function getStaticPaths() {
const allFeatures = await getCollection('features');
return allFeatures.map(feature => ({
params: { slug: feature.id },
props: { feature },
}));
}
interface Props {
feature: CollectionEntry<'features'>;
}
const { feature } = Astro.props;
const { Content } = await feature.render();
const { title, description, icon, category, benefits, useCases, available, comingSoon } = feature.data;
const relatedFeatures = await getRelatedFeatures(feature, 3);
---
<Layout title={title} description={description}>
<article class="min-h-screen bg-dark-bg">
<!-- Hero Section -->
<div class="relative py-24 overflow-hidden">
<div class="absolute inset-0 bg-gradient-to-br from-primary-900/20 via-dark-surface to-secondary-900/20"></div>
<div class="relative z-10 container mx-auto px-4">
<div class="max-w-4xl mx-auto">
<!-- Back Button -->
<a
href={localizePath('/features')}
class="inline-flex items-center gap-2 text-primary hover:text-primary-400 transition-colors mb-8"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
</svg>
<span>{t('features.back_to_features')}</span>
</a>
<!-- Icon & Category -->
<div class="flex items-center gap-4 mb-6">
<span class="text-6xl">{icon}</span>
<span class="px-3 py-1 bg-primary/20 text-primary text-sm font-medium rounded-full capitalize">
{t(`features.categories.${category}`)}
</span>
</div>
<!-- Title -->
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6">
{title}
</h1>
<!-- Description -->
<p class="text-xl text-gray-300 mb-8">
{description}
</p>
<!-- Status Badges -->
<div class="flex flex-wrap gap-3">
{available && (
<span class="px-4 py-2 bg-success/20 text-success border border-success/30 rounded-lg font-medium">
✓ {t('features.available_now')}
</span>
)}
{comingSoon && (
<span class="px-4 py-2 bg-secondary/20 text-secondary border border-secondary/30 rounded-lg font-medium">
🚀 {t('features.coming_soon')}
</span>
)}
</div>
</div>
</div>
</div>
<!-- Benefits Section -->
{benefits && benefits.length > 0 && (
<section class="py-16 bg-dark-surface">
<div class="container mx-auto px-4">
<div class="max-w-4xl mx-auto">
<h2 class="text-3xl font-bold text-white mb-8">{t('features.key_benefits')}</h2>
<div class="grid md:grid-cols-2 gap-4">
{benefits.map(benefit => (
<div class="flex items-start gap-3 p-4 bg-dark-elevated border border-dark-border rounded-lg">
<svg class="w-6 h-6 text-primary mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
<span class="text-gray-300">{benefit}</span>
</div>
))}
</div>
</div>
</div>
</section>
)}
<!-- Content -->
<section class="py-16">
<div class="container mx-auto px-4">
<div class="max-w-4xl mx-auto">
<div class="prose prose-invert prose-primary prose-lg max-w-none">
<Content />
</div>
</div>
</div>
</section>
<!-- Use Cases -->
{useCases && useCases.length > 0 && (
<section class="py-16 bg-dark-surface">
<div class="container mx-auto px-4">
<div class="max-w-4xl mx-auto">
<h2 class="text-3xl font-bold text-white mb-8">{t('features.use_cases')}</h2>
<div class="grid md:grid-cols-2 gap-6">
{useCases.map(useCase => (
<div class="p-6 bg-dark-elevated border border-dark-border rounded-xl">
<div class="flex items-start gap-3">
<span class="text-2xl">💡</span>
<p class="text-gray-300 flex-1">{useCase}</p>
</div>
</div>
))}
</div>
</div>
</div>
</section>
)}
<!-- CTA -->
<section class="py-16">
<div class="container mx-auto px-4">
<div class="max-w-4xl mx-auto text-center p-12 bg-gradient-to-br from-primary/20 to-secondary/20 border border-primary/30 rounded-2xl">
<h2 class="text-3xl font-bold text-white mb-4">
{t('features.try_feature')}
</h2>
<p class="text-gray-300 mb-8 max-w-2xl mx-auto">
{t('features.try_feature_subtitle')}
</p>
<a
href="#"
class="inline-block px-8 py-4 bg-gradient-to-r from-primary-600 to-secondary-600 hover:from-primary-700 hover:to-secondary-700 text-white rounded-lg font-semibold text-lg transition-all duration-300 shadow-lg shadow-primary/50 hover:shadow-primary/70 hover:scale-105"
>
{t('features.get_started')}
</a>
</div>
</div>
</section>
<!-- Related Features -->
{relatedFeatures.length > 0 && (
<section class="py-16 bg-dark-surface">
<div class="container mx-auto px-4">
<h2 class="text-3xl font-bold text-white mb-8">{t('features.related_features')}</h2>
<div class="grid md:grid-cols-3 gap-8">
{relatedFeatures.map(relatedFeature => (
<FeatureCard feature={relatedFeature} />
))}
</div>
</div>
</section>
)}
</article>
</Layout>
<style is:global>
/* Feature Page Styles - same as blog */
.prose {
@apply text-gray-300;
}
.prose h2 {
@apply text-3xl font-bold text-white mt-12 mb-6;
}
.prose h3 {
@apply text-2xl font-semibold text-white mt-8 mb-4;
}
.prose p {
@apply mb-6 leading-relaxed;
}
.prose ul, .prose ol {
@apply mb-6 space-y-2;
}
.prose li {
@apply text-gray-300;
}
.prose a {
@apply text-primary hover:text-primary-400 transition-colors underline;
}
.prose strong {
@apply text-white font-semibold;
}
.prose code {
@apply bg-dark-elevated px-2 py-1 rounded text-primary-300 text-sm;
}
.prose pre {
@apply bg-dark-elevated border border-dark-border rounded-xl p-4 overflow-x-auto mb-6;
}
.prose table {
@apply w-full mb-6;
}
.prose th {
@apply bg-dark-elevated text-white font-semibold p-3 text-left;
}
.prose td {
@apply border-t border-dark-border p-3;
}
</style>