feat: add official EmDash marketing template

- Marketing landing page with hero, features, testimonials, FAQ, pricing
- EmDash CMS with pages collection and marketing blocks
- Full seed data with all content sections
- Dockerfile with entrypoint for database persistence
- Responsive design with CSS variables

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kunthawat Greethong
2026-04-30 20:58:06 +07:00
commit 065d92636a
23 changed files with 1580 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
---
interface Props {
node: {
_key?: string;
headline?: string;
items: Array<{ question: string; answer: string }>;
};
}
const { node } = Astro.props;
const { _key, headline, items } = node;
---
{headline && <h2>{headline}</h2>}
{items?.map((item) => (
<div class="faq-item">
<h3>{item.question}</h3>
<p>{item.answer}</p>
</div>
))}

View File

@@ -0,0 +1,39 @@
---
interface Props {
node: {
_key?: string;
headline?: string;
subheadline?: string;
features: Array<{
icon: string;
title: string;
description: string;
}>;
};
}
const { node } = Astro.props;
const { _key, headline, subheadline, features } = node;
---
{(headline || subheadline) && (
<div class="features-header">
{headline && <h2>{headline}</h2>}
{subheadline && <p>{subheadline}</p>}
</div>
)}
{features?.map((feature) => (
<div class="feature-item" key={feature.icon}>
<div class="feature-icon">{feature.icon}</div>
<h3>{feature.title}</h3>
<p>{feature.description}</p>
</div>
))}
<style>
.features-header {
text-align: center;
margin-bottom: var(--spacing-4xl);
}
</style>

View File

@@ -0,0 +1,4 @@
---
const { entry: page } = Astro.props;
---
<h1>{entry.data.title}</h1>

View File

@@ -0,0 +1,35 @@
---
interface Props {
node: {
_key?: string;
headline?: string;
plans: Array<{
name: string;
price: string;
period?: string;
description?: string;
features: string[];
cta: { label: string; url: string };
highlighted?: boolean;
}>;
};
}
const { node } = Astro.props;
const { headline, plans } = node;
---
{headline && <h2>{headline}</h2>}
{plans?.map((plan) => (
<div class="plan" data-highlighted={plan.highlighted}>
{plan.highlighted && <span class="badge">Most popular</span>}
<h3>{plan.name}</h3>
<p class="price">{plan.price}{plan.period && <small>{plan.period}</small>}</p>
{plan.description && <p class="description">{plan.description}</p>}
<ul>
{plan.features?.map((f) => <li>{f}</li>)}
</ul>
<a href={plan.cta.url}>{plan.cta.label}</a>
</div>
))}

View File

@@ -0,0 +1,29 @@
---
interface Props {
node: {
_key?: string;
headline?: string;
testimonials: Array<{
quote: string;
author: string;
role?: string;
company?: string;
}>;
};
}
const { node } = Astro.props;
const { headline, testimonials } = node;
---
{headline && <h2>{headline}</h2>}
{testimonials?.map((t) => (
<blockquote>
<p>"{t.quote}"</p>
<footer>
<cite>{t.author}</cite>
{t.role && <span>{t.role}{t.company && ` at ${t.company}`}</span>}
</footer>
</blockquote>
))}

View File

@@ -0,0 +1,5 @@
export { default as Hero } from "./Hero.astro";
export { default as Features } from "./Features.astro";
export { default as Testimonials } from "./Testimonials.astro";
export { default as Pricing } from "./Pricing.astro";
export { default as FAQ } from "./FAQ.astro";