From 36b85fc8a02954811de87d0e19c5f3c580f36881 Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:13:57 +0100 Subject: [PATCH] fix(picture): migrate to Astro 5.x content collections and fix TypeScript errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- picture/apps/landing/package.json | 1 + .../caseStudies/CaseStudyCard.astro | 4 +- .../components/changelog/ChangelogEntry.astro | 4 +- .../comparisons/ComparisonCard.astro | 2 +- .../comparisons/ComparisonSchema.astro | 4 +- .../landing/src/components/faq/FAQCard.astro | 3 +- .../promptTemplates/TemplateFilters.astro | 2 +- .../components/tutorials/TutorialCard.astro | 2 +- .../src/components/useCases/UseCaseCard.astro | 2 +- .../src/content/comparisons/en/placeholder.md | 6 - .../landing/src/content/faq/en/placeholder.md | 6 - .../src/content/useCases/en/placeholder.md | 7 -- picture/apps/landing/src/layouts/Layout.astro | 1 + .../blog/{[slug].astro => [...slug].astro} | 2 +- .../{[slug].astro => [...slug].astro} | 2 +- .../landing/src/pages/changelog/index.astro | 2 +- .../{[slug].astro => [...slug].astro} | 6 +- .../{[slug].astro => [...slug].astro} | 12 +- picture/apps/landing/src/pages/index.astro | 105 ++++++++++++++++++ picture/apps/landing/src/pages/pricing.astro | 2 +- .../{[slug].astro => [...slug].astro} | 2 +- .../src/pages/prompt-templates/index.astro | 4 +- .../{[slug].astro => [...slug].astro} | 2 +- picture/apps/landing/src/styles/global.css | 84 ++++++++++++++ picture/apps/landing/src/utils/blog.ts | 2 +- picture/apps/landing/src/utils/caseStudies.ts | 6 +- picture/apps/landing/src/utils/features.ts | 4 +- picture/apps/landing/src/utils/tutorials.ts | 4 +- picture/apps/landing/tailwind.config.mjs | 25 ++++- 29 files changed, 248 insertions(+), 60 deletions(-) delete mode 100644 picture/apps/landing/src/content/comparisons/en/placeholder.md delete mode 100644 picture/apps/landing/src/content/faq/en/placeholder.md delete mode 100644 picture/apps/landing/src/content/useCases/en/placeholder.md rename picture/apps/landing/src/pages/blog/{[slug].astro => [...slug].astro} (99%) rename picture/apps/landing/src/pages/changelog/{[slug].astro => [...slug].astro} (99%) rename picture/apps/landing/src/pages/comparisons/{[slug].astro => [...slug].astro} (99%) rename picture/apps/landing/src/pages/features/{[slug].astro => [...slug].astro} (96%) rename picture/apps/landing/src/pages/prompt-templates/{[slug].astro => [...slug].astro} (99%) rename picture/apps/landing/src/pages/tutorials/{[slug].astro => [...slug].astro} (99%) create mode 100644 picture/apps/landing/src/styles/global.css diff --git a/picture/apps/landing/package.json b/picture/apps/landing/package.json index a700c4fdf..bfea7b00c 100644 --- a/picture/apps/landing/package.json +++ b/picture/apps/landing/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@astrojs/check": "^0.9.0", + "@manacore/shared-landing-ui": "workspace:*", "@picture/design-tokens": "workspace:*", "astro": "^5.16.0", "astro-i18next": "1.0.0-beta.21", diff --git a/picture/apps/landing/src/components/caseStudies/CaseStudyCard.astro b/picture/apps/landing/src/components/caseStudies/CaseStudyCard.astro index df70be42e..faf1141e9 100644 --- a/picture/apps/landing/src/components/caseStudies/CaseStudyCard.astro +++ b/picture/apps/landing/src/components/caseStudies/CaseStudyCard.astro @@ -103,7 +103,7 @@ const primaryMetric = data.metrics[0] || null; { !featured && data.metrics.length > 0 && (
- {data.metrics.slice(0, 3).map((metric) => ( + {data.metrics.slice(0, 3).map((metric: { value: string; label: string }) => (
{metric.value}
{metric.label}
@@ -117,7 +117,7 @@ const primaryMetric = data.metrics[0] || null; { data.tags.length > 0 && (
- {data.tags.slice(0, 3).map((tag) => ( + {data.tags.slice(0, 3).map((tag: string) => ( {tag} diff --git a/picture/apps/landing/src/components/changelog/ChangelogEntry.astro b/picture/apps/landing/src/components/changelog/ChangelogEntry.astro index 0b431a5f0..3781c6963 100644 --- a/picture/apps/landing/src/components/changelog/ChangelogEntry.astro +++ b/picture/apps/landing/src/components/changelog/ChangelogEntry.astro @@ -45,7 +45,7 @@ const isRecent = isRecentRelease(data.releaseDate); )}
- +

{data.title}

@@ -74,7 +74,7 @@ const isRecent = isRecentRelease(data.releaseDate);
- + Read More diff --git a/picture/apps/landing/src/components/comparisons/ComparisonCard.astro b/picture/apps/landing/src/components/comparisons/ComparisonCard.astro index d9aa502ca..da41326e6 100644 --- a/picture/apps/landing/src/components/comparisons/ComparisonCard.astro +++ b/picture/apps/landing/src/components/comparisons/ComparisonCard.astro @@ -11,7 +11,7 @@ const { data } = comparison; ---
diff --git a/picture/apps/landing/src/components/comparisons/ComparisonSchema.astro b/picture/apps/landing/src/components/comparisons/ComparisonSchema.astro index 1aa9e280a..d36c82f6e 100644 --- a/picture/apps/landing/src/components/comparisons/ComparisonSchema.astro +++ b/picture/apps/landing/src/components/comparisons/ComparisonSchema.astro @@ -29,7 +29,7 @@ const comparisonSchema = { }, mainEntityOfPage: { '@type': 'WebPage', - '@id': `https://picture.com/comparisons/${data.slug}`, + '@id': `https://picture.com/comparisons/${comparison.id}`, }, ...(data.coverImage && { image: { @@ -90,7 +90,7 @@ const breadcrumbSchema = { '@type': 'ListItem', position: 3, name: data.title, - item: `https://picture.com/comparisons/${data.slug}`, + item: `https://picture.com/comparisons/${comparison.id}`, }, ], }; diff --git a/picture/apps/landing/src/components/faq/FAQCard.astro b/picture/apps/landing/src/components/faq/FAQCard.astro index 2739ccd71..d76cfe060 100644 --- a/picture/apps/landing/src/components/faq/FAQCard.astro +++ b/picture/apps/landing/src/components/faq/FAQCard.astro @@ -1,6 +1,5 @@ --- import type { CollectionEntry } from 'astro:content'; -import { marked } from 'marked'; interface Props { faq: CollectionEntry<'faq'>; @@ -42,7 +41,7 @@ const { Content } = await faq.render(); } .faq-item:hover { - @apply border-dark-hover; + @apply border-border-hover; } .faq-question { diff --git a/picture/apps/landing/src/components/promptTemplates/TemplateFilters.astro b/picture/apps/landing/src/components/promptTemplates/TemplateFilters.astro index 245ff0656..3d1955779 100644 --- a/picture/apps/landing/src/components/promptTemplates/TemplateFilters.astro +++ b/picture/apps/landing/src/components/promptTemplates/TemplateFilters.astro @@ -53,7 +53,7 @@ import { formatCategoryName } from '../../utils/promptTemplates'; { difficulties.map((diff) => ( )) } diff --git a/picture/apps/landing/src/components/tutorials/TutorialCard.astro b/picture/apps/landing/src/components/tutorials/TutorialCard.astro index a37e74735..067dda47a 100644 --- a/picture/apps/landing/src/components/tutorials/TutorialCard.astro +++ b/picture/apps/landing/src/components/tutorials/TutorialCard.astro @@ -18,7 +18,7 @@ const difficultyColor = getDifficultyColor(data.difficulty); --- diff --git a/picture/apps/landing/src/components/useCases/UseCaseCard.astro b/picture/apps/landing/src/components/useCases/UseCaseCard.astro index dc38f99fb..98b72ee06 100644 --- a/picture/apps/landing/src/components/useCases/UseCaseCard.astro +++ b/picture/apps/landing/src/components/useCases/UseCaseCard.astro @@ -11,7 +11,7 @@ const { data } = useCase; ---
diff --git a/picture/apps/landing/src/content/comparisons/en/placeholder.md b/picture/apps/landing/src/content/comparisons/en/placeholder.md deleted file mode 100644 index bf1d121e7..000000000 --- a/picture/apps/landing/src/content/comparisons/en/placeholder.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: "Picture vs Traditional Design" -order: 1 ---- - -Compare Picture's AI-powered approach with traditional design workflows and see how you can save time and resources. diff --git a/picture/apps/landing/src/content/faq/en/placeholder.md b/picture/apps/landing/src/content/faq/en/placeholder.md deleted file mode 100644 index 0155194e8..000000000 --- a/picture/apps/landing/src/content/faq/en/placeholder.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: "What is Picture?" -order: 1 ---- - -Picture is an AI-powered image generation platform that helps you create stunning visuals from text descriptions. diff --git a/picture/apps/landing/src/content/useCases/en/placeholder.md b/picture/apps/landing/src/content/useCases/en/placeholder.md deleted file mode 100644 index 396f38293..000000000 --- a/picture/apps/landing/src/content/useCases/en/placeholder.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Marketing & Social Media" -icon: "📱" -order: 1 ---- - -Create eye-catching visuals for your social media campaigns and marketing materials with AI-generated images. diff --git a/picture/apps/landing/src/layouts/Layout.astro b/picture/apps/landing/src/layouts/Layout.astro index ad3cce116..9d091d4e7 100644 --- a/picture/apps/landing/src/layouts/Layout.astro +++ b/picture/apps/landing/src/layouts/Layout.astro @@ -1,4 +1,5 @@ --- +import '../styles/global.css'; // import { HeadHrefLangs } from 'astro-i18next/components'; import { t } from '../i18n'; import LanguageSwitcher from '@components/LanguageSwitcher.astro'; diff --git a/picture/apps/landing/src/pages/blog/[slug].astro b/picture/apps/landing/src/pages/blog/[...slug].astro similarity index 99% rename from picture/apps/landing/src/pages/blog/[slug].astro rename to picture/apps/landing/src/pages/blog/[...slug].astro index 58e89d1ff..5f3e1f857 100644 --- a/picture/apps/landing/src/pages/blog/[slug].astro +++ b/picture/apps/landing/src/pages/blog/[...slug].astro @@ -11,7 +11,7 @@ import BlogCard from '@components/blog/BlogCard.astro'; export async function getStaticPaths() { const allPosts = await getCollection('blog'); return allPosts.map(post => ({ - params: { slug: post.slug }, + params: { slug: post.id }, props: { post }, })); } diff --git a/picture/apps/landing/src/pages/changelog/[slug].astro b/picture/apps/landing/src/pages/changelog/[...slug].astro similarity index 99% rename from picture/apps/landing/src/pages/changelog/[slug].astro rename to picture/apps/landing/src/pages/changelog/[...slug].astro index 0b20d0114..de7703631 100644 --- a/picture/apps/landing/src/pages/changelog/[slug].astro +++ b/picture/apps/landing/src/pages/changelog/[...slug].astro @@ -16,7 +16,7 @@ import { export async function getStaticPaths() { const changelog = await getCollection('changelog'); return changelog.map((entry) => ({ - params: { slug: entry.slug }, + params: { slug: entry.id }, props: { entry }, })); } diff --git a/picture/apps/landing/src/pages/changelog/index.astro b/picture/apps/landing/src/pages/changelog/index.astro index aab90e03e..da0d3f71d 100644 --- a/picture/apps/landing/src/pages/changelog/index.astro +++ b/picture/apps/landing/src/pages/changelog/index.astro @@ -64,7 +64,7 @@ const groupedByYearMonth = await getChangelogGroupedByYearMonth(language);

{latestRelease.data.summary}

Read Full Release diff --git a/picture/apps/landing/src/pages/comparisons/[slug].astro b/picture/apps/landing/src/pages/comparisons/[...slug].astro similarity index 99% rename from picture/apps/landing/src/pages/comparisons/[slug].astro rename to picture/apps/landing/src/pages/comparisons/[...slug].astro index 9e00778e8..6b0b5298a 100644 --- a/picture/apps/landing/src/pages/comparisons/[slug].astro +++ b/picture/apps/landing/src/pages/comparisons/[...slug].astro @@ -8,7 +8,7 @@ import { getWinnerBadgeColor, getWinnerBadgeText, calculateOverallWinner } from export async function getStaticPaths() { const comparisons = await getCollection('comparisons'); return comparisons.map((comparison) => ({ - params: { slug: comparison.data.slug }, + params: { slug: comparison.id }, props: { comparison }, })); } @@ -28,8 +28,8 @@ const overallWinner = data.winnerBadge || calculateOverallWinner(data.comparison const allComparisons = await getCollection('comparisons'); const relatedComparisons = allComparisons .filter((c) => - data.relatedComparisons.includes(c.data.slug) && - c.data.slug !== data.slug && + data.relatedComparisons.includes(c.id) && + c.id !== comparison.id && c.data.language === data.language ) .slice(0, 3); diff --git a/picture/apps/landing/src/pages/features/[slug].astro b/picture/apps/landing/src/pages/features/[...slug].astro similarity index 96% rename from picture/apps/landing/src/pages/features/[slug].astro rename to picture/apps/landing/src/pages/features/[...slug].astro index 7defd200b..77e2653f5 100644 --- a/picture/apps/landing/src/pages/features/[slug].astro +++ b/picture/apps/landing/src/pages/features/[...slug].astro @@ -9,14 +9,10 @@ import FeatureCard from '@components/features/FeatureCard.astro'; export async function getStaticPaths() { const allFeatures = await getCollection('features'); - return allFeatures.map(feature => { - // Remove language prefix from slug (e.g., "en/cross-platform-apps" -> "cross-platform-apps") - const slug = feature.slug.split('/').pop() || feature.slug; - return { - params: { slug }, - props: { feature }, - }; - }); + return allFeatures.map(feature => ({ + params: { slug: feature.id }, + props: { feature }, + })); } interface Props { diff --git a/picture/apps/landing/src/pages/index.astro b/picture/apps/landing/src/pages/index.astro index 0b3f0a7ed..2abed5557 100644 --- a/picture/apps/landing/src/pages/index.astro +++ b/picture/apps/landing/src/pages/index.astro @@ -5,6 +5,92 @@ import Features from '@components/Features.astro'; import Testimonials from '@components/Testimonials.astro'; import CTA from '@components/CTA.astro'; import Footer from '@components/Footer.astro'; + +// Shared components +import StepsSection from '@manacore/shared-landing-ui/sections/StepsSection.astro'; +import PricingSection from '@manacore/shared-landing-ui/sections/PricingSection.astro'; + +// Steps data +const steps = [ + { + number: '1', + title: 'Describe Your Vision', + description: 'Enter a text prompt describing the image you want to create. Be as detailed or simple as you like.', + image: '/screenshots/prompt.png' + }, + { + number: '2', + title: 'Choose Your Model', + description: 'Select from multiple AI models - FLUX Schnell for speed, Dev for balance, or Pro for maximum quality.', + image: '/screenshots/model.png' + }, + { + number: '3', + title: 'Generate & Download', + description: 'Watch as your image is created in seconds. Download in high resolution or save to your gallery.', + image: '/screenshots/download.png' + } +]; + +// Pricing preview data +const pricingPlans = [ + { + name: 'Free', + price: '0', + period: '/month', + description: 'Perfect for trying out Picture', + features: [ + { text: '100 images/month', included: true }, + { text: 'FLUX Schnell model', included: true }, + { text: 'Basic editing tools', included: true }, + { text: 'Gallery storage (30 days)', included: true }, + { text: 'Pro models', included: false }, + { text: 'Priority generation', included: false } + ], + cta: { + text: 'Get Started Free', + href: '#' + } + }, + { + name: 'Pro', + price: '19', + period: '/month', + description: 'For professionals & creators', + features: [ + { text: '2,000 images/month', included: true }, + { text: 'All AI models', included: true }, + { text: 'Advanced editing', included: true }, + { text: 'Unlimited storage', included: true }, + { text: 'Priority queue', included: true }, + { text: 'Commercial usage', included: true } + ], + cta: { + text: 'Start Free Trial', + href: '#' + }, + highlighted: true, + badge: 'Most Popular' + }, + { + name: 'Enterprise', + price: 'Custom', + period: '', + description: 'For teams & businesses', + features: [ + { text: 'Unlimited images', included: true }, + { text: 'All models + early access', included: true }, + { text: 'Custom fine-tuning', included: true }, + { text: 'API access', included: true }, + { text: 'Team collaboration', included: true }, + { text: 'SLA guarantee', included: true } + ], + cta: { + text: 'Contact Sales', + href: '#' + } + } +]; --- + + + + +