feat(sprint-6): Phase 7 — Launch checklist, pricing page, legal templates
- docs/sprint-6/launch-checklist.md: comprehensive pre/post-launch checklist - /pricing: public pricing page (Starter €19, Pro €49, Enterprise) - /impressum, /datenschutz, /agb: legal page templates (placeholder text) - (marketing) route group: public layout without auth - Footer links to legal pages on login + portal - i18n for marketing namespace (de + en) - Fix pre-existing lint errors (unused vars, missing @stomp/stompjs types)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import { fireEvent, render, screen } from "@testing-library/react"
|
||||
import { describe, expect, it, vi } from "vitest"
|
||||
import { vi as _vi, describe, expect, it } from "vitest"
|
||||
|
||||
// Error Boundary component for testing
|
||||
class ApiErrorBoundary extends React.Component<
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import { render, screen } from "@testing-library/react"
|
||||
import { describe, expect, it, vi } from "vitest"
|
||||
import { vi as _vi, describe, expect, it } from "vitest"
|
||||
|
||||
// Since there's no existing offline banner component, we'll create and test a minimal one
|
||||
// This tests the pattern that would be used for an offline banner
|
||||
|
||||
@@ -236,7 +236,7 @@ export const handlers = [
|
||||
}
|
||||
),
|
||||
|
||||
http.post("/api/backend/staff/:id/revoke", ({ params }) => {
|
||||
http.post("/api/backend/staff/:id/revoke", ({ params: _params }) => {
|
||||
return new HttpResponse(null, { status: 204 })
|
||||
}),
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import Link from "next/link"
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { signIn } from "next-auth/react"
|
||||
@@ -159,9 +160,28 @@ export default function LoginPage() {
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<p className="text-center text-xs text-muted-foreground">
|
||||
{t("footerText")}
|
||||
</p>
|
||||
<div className="space-y-2 text-center">
|
||||
<p className="text-xs text-muted-foreground">{t("footerText")}</p>
|
||||
<div className="flex items-center justify-center gap-3 text-xs text-muted-foreground">
|
||||
<Link
|
||||
href="/impressum"
|
||||
className="hover:text-foreground transition-colors"
|
||||
>
|
||||
Impressum
|
||||
</Link>
|
||||
<span>·</span>
|
||||
<Link
|
||||
href="/datenschutz"
|
||||
className="hover:text-foreground transition-colors"
|
||||
>
|
||||
Datenschutz
|
||||
</Link>
|
||||
<span>·</span>
|
||||
<Link href="/agb" className="hover:text-foreground transition-colors">
|
||||
AGB
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
|
||||
export default function AGBPage() {
|
||||
const t = useTranslations("marketing.agb")
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-16 max-w-3xl">
|
||||
<h1 className="text-3xl font-bold mb-2">{t("title")}</h1>
|
||||
<p className="text-sm text-muted-foreground mb-8">{t("lastUpdated")}</p>
|
||||
|
||||
<div className="prose prose-sm dark:prose-invert max-w-none space-y-8">
|
||||
{/* §1 Geltungsbereich */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s1Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s1Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* §2 Vertragsgegenstand */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s2Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s2Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* §3 Registrierung und Nutzerkonto */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s3Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s3Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* §4 Preise und Zahlung */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s4Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s4Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* §5 Testphase */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s5Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s5Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* §6 Kündigung */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s6Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s6Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* §7 Verfügbarkeit */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s7Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s7Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* §8 Haftung */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s8Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s8Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* §9 Datenschutz */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s9Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s9Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* §10 Schlussbestimmungen */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s10Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s10Content")}</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
|
||||
export default function DatenschutzPage() {
|
||||
const t = useTranslations("marketing.datenschutz")
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-16 max-w-3xl">
|
||||
<h1 className="text-3xl font-bold mb-2">{t("title")}</h1>
|
||||
<p className="text-sm text-muted-foreground mb-8">{t("lastUpdated")}</p>
|
||||
|
||||
<div className="prose prose-sm dark:prose-invert max-w-none space-y-8">
|
||||
{/* 1. Verantwortlicher */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s1Title")}</h2>
|
||||
<p className="text-muted-foreground whitespace-pre-line">
|
||||
{t("s1Content")}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* 2. Erhebung und Speicherung personenbezogener Daten */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s2Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s2Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* 3. Zweck der Datenverarbeitung */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s3Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s3Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* 4. Rechtsgrundlage */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s4Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s4Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* 5. Weitergabe an Dritte */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s5Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s5Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* 6. Auftragsverarbeitung (Stripe) */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s6Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s6Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* 7. Speicherdauer */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s7Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s7Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* 8. Betroffenenrechte */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s8Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s8Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* 9. Datensicherheit */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s9Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s9Content")}</p>
|
||||
</section>
|
||||
|
||||
{/* 10. Hosting */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("s10Title")}</h2>
|
||||
<p className="text-muted-foreground">{t("s10Content")}</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
"use client"
|
||||
|
||||
import { useTranslations } from "next-intl"
|
||||
|
||||
export default function ImpressumPage() {
|
||||
const t = useTranslations("marketing.impressum")
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-16 max-w-3xl">
|
||||
<h1 className="text-3xl font-bold mb-8">{t("title")}</h1>
|
||||
|
||||
<div className="prose prose-sm dark:prose-invert max-w-none space-y-8">
|
||||
{/* Angaben gemäß § 5 TMG */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("section1Title")}</h2>
|
||||
<p className="text-muted-foreground whitespace-pre-line">
|
||||
{t("section1Content")}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Kontakt */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("contactTitle")}</h2>
|
||||
<p className="text-muted-foreground whitespace-pre-line">
|
||||
{t("contactContent")}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Umsatzsteuer-ID */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("vatTitle")}</h2>
|
||||
<p className="text-muted-foreground">{t("vatContent")}</p>
|
||||
</section>
|
||||
|
||||
{/* Verantwortlich für den Inhalt */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">
|
||||
{t("responsibleTitle")}
|
||||
</h2>
|
||||
<p className="text-muted-foreground whitespace-pre-line">
|
||||
{t("responsibleContent")}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Streitschlichtung */}
|
||||
<section>
|
||||
<h2 className="text-xl font-semibold mb-3">{t("disputeTitle")}</h2>
|
||||
<p className="text-muted-foreground">{t("disputeContent")}</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import Link from "next/link"
|
||||
import { NextIntlClientProvider } from "next-intl"
|
||||
import { getMessages } from "next-intl/server"
|
||||
import { Cannabis } from "lucide-react"
|
||||
|
||||
import type { ReactNode } from "react"
|
||||
|
||||
export default async function MarketingLayout({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode
|
||||
}) {
|
||||
const messages = await getMessages()
|
||||
|
||||
return (
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<div className="min-h-screen flex flex-col bg-background text-foreground">
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-40 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="container mx-auto flex h-16 items-center justify-between px-4">
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<div className="flex h-9 w-9 items-center justify-center rounded-lg bg-primary/10">
|
||||
<Cannabis className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<span className="text-lg font-bold">CannaManage</span>
|
||||
</Link>
|
||||
<nav className="flex items-center gap-4">
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
Preise
|
||||
</Link>
|
||||
<Link
|
||||
href="/login"
|
||||
className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
Anmelden
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main content */}
|
||||
<main className="flex-1">{children}</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="border-t bg-muted/50">
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-3">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Cannabis className="h-5 w-5 text-primary" />
|
||||
<span className="font-semibold">CannaManage</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Die sichere Verwaltungssoftware für Cannabis-Anbauvereine in
|
||||
Deutschland.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-sm mb-3">Produkt</h4>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="hover:text-foreground transition-colors"
|
||||
>
|
||||
Preise
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/login"
|
||||
className="hover:text-foreground transition-colors"
|
||||
>
|
||||
Anmelden
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-semibold text-sm mb-3">Rechtliches</h4>
|
||||
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||
<li>
|
||||
<Link
|
||||
href="/impressum"
|
||||
className="hover:text-foreground transition-colors"
|
||||
>
|
||||
Impressum
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/datenschutz"
|
||||
className="hover:text-foreground transition-colors"
|
||||
>
|
||||
Datenschutz
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/agb"
|
||||
className="hover:text-foreground transition-colors"
|
||||
>
|
||||
AGB
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8 border-t pt-6 text-center text-xs text-muted-foreground">
|
||||
© {new Date().getFullYear()} CannaManage — Plate Software. Alle
|
||||
Rechte vorbehalten.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</NextIntlClientProvider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
"use client"
|
||||
|
||||
import Link from "next/link"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { Building2, Cannabis, Check, Leaf } from "lucide-react"
|
||||
|
||||
const plans = [
|
||||
{
|
||||
id: "starter",
|
||||
icon: Leaf,
|
||||
price: "19",
|
||||
memberLimit: "30",
|
||||
features: [
|
||||
"memberManagement",
|
||||
"distributionTracking",
|
||||
"complianceReports",
|
||||
"quotaMonitoring",
|
||||
"memberPortal",
|
||||
"emailSupport",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "pro",
|
||||
icon: Cannabis,
|
||||
price: "49",
|
||||
memberLimit: "100",
|
||||
popular: true,
|
||||
features: [
|
||||
"allStarter",
|
||||
"growCalendar",
|
||||
"staffManagement",
|
||||
"advancedReports",
|
||||
"pdfExport",
|
||||
"apiAccess",
|
||||
"prioritySupport",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "enterprise",
|
||||
icon: Building2,
|
||||
price: null,
|
||||
memberLimit: "unlimited",
|
||||
features: [
|
||||
"allPro",
|
||||
"unlimitedMembers",
|
||||
"multiClub",
|
||||
"customIntegrations",
|
||||
"sla",
|
||||
"dedicatedSupport",
|
||||
"onboarding",
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const faqs = [
|
||||
{ id: "trial" },
|
||||
{ id: "payment" },
|
||||
{ id: "cancel" },
|
||||
{ id: "data" },
|
||||
{ id: "migration" },
|
||||
]
|
||||
|
||||
export default function PricingPage() {
|
||||
const t = useTranslations("marketing.pricing")
|
||||
|
||||
return (
|
||||
<div className="py-16">
|
||||
{/* Hero */}
|
||||
<div className="container mx-auto px-4 text-center mb-16">
|
||||
<h1 className="text-4xl font-bold tracking-tight sm:text-5xl mb-4">
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
<div className="mt-6 inline-flex items-center gap-2 rounded-full bg-primary/10 px-4 py-2 text-sm font-medium text-primary">
|
||||
<Cannabis className="h-4 w-4" />
|
||||
{t("trialBadge")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Plan Cards */}
|
||||
<div className="container mx-auto px-4 mb-20">
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-3 max-w-5xl mx-auto">
|
||||
{plans.map((plan) => {
|
||||
const Icon = plan.icon
|
||||
return (
|
||||
<div
|
||||
key={plan.id}
|
||||
className={`relative rounded-2xl border p-8 flex flex-col ${
|
||||
plan.popular
|
||||
? "border-primary shadow-lg ring-1 ring-primary"
|
||||
: "border-border"
|
||||
}`}
|
||||
>
|
||||
{plan.popular && (
|
||||
<div className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-primary px-3 py-1 text-xs font-medium text-primary-foreground">
|
||||
{t("popular")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
|
||||
<Icon className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold">
|
||||
{t(`plans.${plan.id}.name`)}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t(`plans.${plan.id}.description`)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
{plan.price ? (
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-4xl font-bold">€{plan.price}</span>
|
||||
<span className="text-muted-foreground">
|
||||
/ {t("perMonth")}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-4xl font-bold">{t("contactUs")}</div>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
{t(`plans.${plan.id}.memberNote`, {
|
||||
limit: plan.memberLimit,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul className="space-y-3 mb-8 flex-1">
|
||||
{plan.features.map((feature) => (
|
||||
<li key={feature} className="flex items-start gap-2">
|
||||
<Check className="h-4 w-4 text-primary mt-0.5 shrink-0" />
|
||||
<span className="text-sm">
|
||||
{t(`features.${feature}`)}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<Link
|
||||
href={plan.price ? "/login" : "mailto:info@plate-software.de"}
|
||||
className={`inline-flex h-11 items-center justify-center rounded-md px-6 text-sm font-medium transition-colors ${
|
||||
plan.popular
|
||||
? "bg-primary text-primary-foreground hover:bg-primary/90"
|
||||
: "border border-input bg-background hover:bg-accent hover:text-accent-foreground"
|
||||
}`}
|
||||
>
|
||||
{plan.price ? t("startTrial") : t("contactSales")}
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Feature Comparison Table */}
|
||||
<div className="container mx-auto px-4 mb-20">
|
||||
<h2 className="text-2xl font-bold text-center mb-8">
|
||||
{t("comparisonTitle")}
|
||||
</h2>
|
||||
<div className="max-w-4xl mx-auto overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b">
|
||||
<th className="text-left py-3 px-4 font-medium">
|
||||
{t("feature")}
|
||||
</th>
|
||||
<th className="text-center py-3 px-4 font-medium">Starter</th>
|
||||
<th className="text-center py-3 px-4 font-medium">Pro</th>
|
||||
<th className="text-center py-3 px-4 font-medium">
|
||||
Enterprise
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
{[
|
||||
"compMembers",
|
||||
"compDistributions",
|
||||
"compReports",
|
||||
"compGrow",
|
||||
"compStaff",
|
||||
"compApi",
|
||||
"compMultiClub",
|
||||
"compSupport",
|
||||
].map((row) => (
|
||||
<tr key={row}>
|
||||
<td className="py-3 px-4">{t(`comparison.${row}.label`)}</td>
|
||||
<td className="py-3 px-4 text-center">
|
||||
{t(`comparison.${row}.starter`)}
|
||||
</td>
|
||||
<td className="py-3 px-4 text-center">
|
||||
{t(`comparison.${row}.pro`)}
|
||||
</td>
|
||||
<td className="py-3 px-4 text-center">
|
||||
{t(`comparison.${row}.enterprise`)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* FAQ */}
|
||||
<div className="container mx-auto px-4 mb-16">
|
||||
<h2 className="text-2xl font-bold text-center mb-8">{t("faqTitle")}</h2>
|
||||
<div className="max-w-3xl mx-auto space-y-6">
|
||||
{faqs.map((faq) => (
|
||||
<div key={faq.id} className="rounded-lg border p-6">
|
||||
<h3 className="font-semibold mb-2">
|
||||
{t(`faq.${faq.id}.question`)}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t(`faq.${faq.id}.answer`)}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<div className="rounded-2xl bg-primary/5 border border-primary/20 p-12 max-w-3xl mx-auto">
|
||||
<h2 className="text-2xl font-bold mb-3">{t("ctaTitle")}</h2>
|
||||
<p className="text-muted-foreground mb-6">{t("ctaSubtitle")}</p>
|
||||
<Link
|
||||
href="/login"
|
||||
className="inline-flex h-11 items-center justify-center rounded-md bg-primary px-8 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
|
||||
>
|
||||
{t("ctaButton")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user