Files
Wabenhain/src/app/admin/patenschaften/_components/sponsorship-tabs.tsx
T
Nick SzaunigandClaude Opus 5.5 f227aeb8f9 feat: Bienenpatenschaft mit Urkunde, Abo, Medaillen und 3D-Konto
Setzt Teilprojekte 1-5 der Spec 2026-10-16 um:
- Urkunden-Engine (lib/certificate): ein Layout fuer PDF, Vorschau und
  3D-Textur, Vorlage classic@1, eingebettete OFL-Schriften, Testmatrix
- Einmal-Patenschaft ueber Traegerprodukte (Kategorie PATENSCHAFT),
  Paket wird im Checkout in Patenschaft (Regelsatz) und Honig aufgeteilt,
  Kontopflicht, rein digitale Bestellungen (FulfillmentMethod DIGITAL)
- Ausstellung nach Zahlung idempotent, Wiederholung im App-Prozess
  (instrumentation + background-jobs), Ablage in CERTIFICATE_DIR
- Verkaufsseite /patenschaft mit Konfigurator und Live-Vorschau,
  Warenkorb mit Schluessel je Position
- Kundenkonto: 3D-Ansicht von Urkunde und Medaille, Downloads, ZIP,
  Uebertragung per befristetem Einmal-Link, Treuestufen bis VIP
- Admin: Angebote, Patenschaften, Druck, Korrektur mit Revisionen,
  Abos, Erntefenster, Deckung, SEPA-Frist, Treuestufen
- Abo ueber Stripe Billing: Lieferung erst nach bezahlten Monaten,
  SEPA-Neukunden-Frist und Rueckbuchungsschutz, Honigreservierung,
  Kuendigungsbutton, Monatsrechnungen, Stripe-Einrichtungsskript

Startangebote werden inaktiv angelegt; Livebetrieb erst nach Freigabe.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-16 14:07:30 +02:00

39 lines
1.5 KiB
TypeScript

import Link from "next/link";
import type { SponsorshipAdminStrings } from "@/i18n/sponsorship/admin";
export type SponsorshipSection = "overview" | "offers" | "subscriptions" | "loyalty";
const SECTIONS: { key: SponsorshipSection; href: string }[] = [
{ key: "overview", href: "/admin/patenschaften" },
{ key: "offers", href: "/admin/patenschaften/angebote" },
{ key: "subscriptions", href: "/admin/patenschaften/abos" },
{ key: "loyalty", href: "/admin/patenschaften/treuestufen" },
];
/**
* Unternavigation des Bereichs. Die Seitenleiste fuehrt mit einem Eintrag
* "Patenschaften" hierher; die vier Teile liegen als Reiter darunter — so
* bleibt die Seitenleiste kurz, und es gibt dort nie zwei aktive Eintraege.
*/
export function SponsorshipTabs({ active, labels }: { active: SponsorshipSection; labels: SponsorshipAdminStrings["tabs"] }) {
return (
<nav aria-label={labels.ariaLabel} className="mb-6 flex gap-1 overflow-x-auto border-b border-ink/10">
{SECTIONS.map((section) => {
const selected = section.key === active;
return (
<Link
key={section.key}
href={section.href}
aria-current={selected ? "page" : undefined}
className={`-mb-px whitespace-nowrap border-b-2 px-4 py-2 text-sm transition-colors ${
selected ? "border-heading text-heading" : "border-transparent text-ink/60 hover:text-heading"
}`}
>
{labels[section.key]}
</Link>
);
})}
</nav>
);
}