feat(db): mesh data model — meshes, members, invites, audit log

- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-04 21:19:32 +01:00
commit d3163a5bff
1384 changed files with 314925 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
"use client";
import { memo, useState } from "react";
import {
RecurringInterval,
RecurringIntervalDuration,
config,
getPriceWithHighestDiscount,
} from "@turbostarter/billing";
import { useTranslation } from "@turbostarter/i18n";
import { Skeleton } from "@turbostarter/ui-web/skeleton";
import { Section, SectionHeader } from "~/modules/marketing/layout/section";
import { PricingHeader } from "./layout/header";
import { Plans, PlansSkeleton } from "./plans/plans";
import type { User } from "@turbostarter/auth";
import type { BillingModel } from "@turbostarter/billing";
interface PricingSectionProps {
readonly user: User | null;
readonly model: BillingModel;
}
export const PricingSection = memo<PricingSectionProps>(({ user, model }) => {
const { t } = useTranslation("billing");
const intervals = [
...new Set(
config.plans.flatMap((plan) =>
plan.prices
.flatMap((price) => ("interval" in price ? price.interval : null))
.filter((x): x is RecurringInterval => !!x),
),
),
].sort((a, b) => RecurringIntervalDuration[a] - RecurringIntervalDuration[b]);
const [activeInterval, setActiveInterval] = useState<RecurringInterval>(
intervals[0] ?? RecurringInterval.MONTH,
);
const priceWithDiscount = getPriceWithHighestDiscount(
config.plans,
config.discounts,
);
return (
<Section id="pricing" className="gap-10 sm:gap-12 md:gap-16 lg:gap-20">
<PricingHeader
currency={t("currency")}
model={model}
intervals={intervals}
activeInterval={activeInterval}
onIntervalChange={setActiveInterval}
{...(priceWithDiscount && { priceWithDiscount })}
/>
<Plans
plans={config.plans}
interval={activeInterval}
model={model}
currency={t("currency")}
discounts={config.discounts}
user={user}
/>
</Section>
);
});
export const PricingSectionSkeleton = () => {
return (
<Section id="pricing" className="gap-10 sm:gap-12 md:gap-16 lg:gap-20">
<SectionHeader className="flex flex-col items-center justify-center gap-3">
<Skeleton className="h-8 w-32" />
<Skeleton className="mt-4 h-12 w-72" />
<Skeleton className="h-8 w-96" />
</SectionHeader>
<PlansSkeleton />
</Section>
);
};
PricingSection.displayName = "PricingSection";