- Datenmodell: Testphasen-Lebenszyklus am Mandanten (plan, trialEndsAt, readOnlySince, deletionDueAt, Versandmarker), TrialSignup (Plattform, Hashes statt Klartext), TenantExport (RLS), Onboarding-Status - /testen: 5-Schritte-Wizard (Betrieb, Admin-Konto, Enddatum, Einrichtung, Zusammenfassung), Bestätigung per POST, direkte Anmeldung über login-ticket; Rate-Limit je IP/E-Mail, Honeypot, Enumeration-Schutz, Slug-Kollisionen - Plattform: Wizard „Testmandant anlegen“ mit Einladung, Badges/Filter, Enddatum ändern, umwandeln, beenden, Löschung vormerken/abbrechen (Bestätigung + Audit) - Schreibsperre nach Ablauf zentral in moduleGuard und requireApiContext (non-GET über withApi), Upload-Routen, Einstellungen/Nutzerverwaltung, Worker-Jobs; Banner Backoffice + mobil - Datenexport (ZIP mit CSV/JSON + Dateien) als Worker-Job, auch im Nur-Lesen-Zustand - Täglicher Job trial-lifecycle: Erinnerungen 7/3/1, Ablauf, Löschhinweis, Löschung über das Offboarding - Erste-Schritte-Checkliste im Dashboard, Mail-Vorlagen de/en, Tests + Smoke, Betriebsdoku Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
19 lines
1.1 KiB
TypeScript
19 lines
1.1 KiB
TypeScript
import { getLocale, getTranslations } from "next-intl/server";
|
||
import { Pill } from "@/components/mockup-ui";
|
||
import { formatDateKey, formatInstantDate } from "@/lib/trial/dates";
|
||
import { computeTrialState, type TenantTrialRow } from "@/server/services/trial/state";
|
||
|
||
/** L15 Testphase: plan badge(s) in the platform tenant list ("Test bis …", "abgelaufen – nur lesen", "Löschung am …"). */
|
||
export async function TrialBadge({ tenant }: { tenant: TenantTrialRow }) {
|
||
const [t, locale] = await Promise.all([getTranslations("trial.platform"), getLocale()]);
|
||
if (tenant.trialDeletedAt) return <Pill tone="mut">{t("badgeDeleted")}</Pill>;
|
||
const state = computeTrialState(tenant);
|
||
if (!state.isTrial) return <Pill tone="ok">{t("badgeFull")}</Pill>;
|
||
return (
|
||
<span className="flex flex-wrap gap-1.5">
|
||
{state.expired ? <Pill tone="warn">{t("badgeExpired")}</Pill> : <Pill tone="warn">{t("badgeUntil", { date: formatDateKey(state.endDateKey!, locale) })}</Pill>}
|
||
{state.deletionDueAt && <Pill tone="mut">{t("badgeDeletion", { date: formatInstantDate(state.deletionDueAt, locale) })}</Pill>}
|
||
</span>
|
||
);
|
||
}
|