Files
Wabenhain/src/lib/certificate/art/logomark.ts
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

70 lines
2.4 KiB
TypeScript

import { CERT_COLORS, type ImageElement, type PathElement } from "@/lib/certificate/elements";
import { compose, polyline, scale, transformPath, translate, rect } from "@/lib/certificate/geometry";
/**
* Das Waben-Zeichen (components/logomark.tsx, lib/invoice-pdf-brand.ts) als
* Vektorpfade: Hexagon in Honiggold, darin drei Tannen in Waldgruen. Gleiche
* Geometrie wie im Web-Logo (viewBox 100). Die Strichstaerken skalieren mit,
* genau wie dort — das Zeichen sieht in jeder Groesse gleich aus.
*
* Wird gezeichnet, wenn kein Logo hochgeladen ist. Das hochgeladene Logo
* selbst wird nie nachgezeichnet oder umgefaerbt (Spec "Markengrundlage").
*/
const HEXAGON = polyline(
[
{ x: 50, y: 5 },
{ x: 88.97, y: 27.5 },
{ x: 88.97, y: 72.5 },
{ x: 50, y: 95 },
{ x: 11.03, y: 72.5 },
{ x: 11.03, y: 27.5 },
],
true
);
const TREES = [
{ x: 20, y: 45, scale: 0.7 },
{ x: 38, y: 32, scale: 0.95 },
{ x: 56, y: 45, scale: 0.7 },
] as const;
const TREE_PARTS = [
polyline([{ x: 10, y: 2 }, { x: 5, y: 13 }, { x: 15, y: 13 }], true),
polyline([{ x: 10, y: 10 }, { x: 2, y: 22 }, { x: 18, y: 22 }], true),
rect({ x: 8, y: 22, width: 4, height: 4 }),
];
/** Waben-Zeichen in einem Quadrat der Kantenlaenge `size` mit linker oberer Ecke (x, y). */
export function logomarkPaths(x: number, y: number, size: number): PathElement[] {
const unit = size / 100;
const place = compose(scale(unit), translate(x, y));
const hexagon: PathElement = {
kind: "path",
role: "logo",
d: transformPath(HEXAGON, place),
stroke: CERT_COLORS.honey,
strokeWidth: 2.5 * unit,
lineJoin: "miter",
};
const trees = TREES.map((tree): PathElement => {
const matrix = compose(scale(tree.scale), translate(tree.x, tree.y), place);
return {
kind: "path",
role: "logo",
d: TREE_PARTS.map((part) => transformPath(part, matrix)).join(""),
stroke: CERT_COLORS.forest,
strokeWidth: 1.4 * tree.scale * unit,
lineJoin: "round",
};
});
return [hexagon, ...trees];
}
/** Quadrat fuer das Waben-Zeichen innerhalb der Logo-Box, nach deren Ausrichtung. */
export function logoFallbackPaths(box: ImageElement): PathElement[] {
const size = Math.min(box.width, box.height);
const offset = box.align === "left" ? 0 : box.align === "right" ? box.width - size : (box.width - size) / 2;
return logomarkPaths(box.x + offset, box.y + (box.height - size) / 2, size);
}