upgrade to tamagui v2 and guest pwa overhaul

This commit is contained in:
Codex Agent
2026-02-02 13:01:20 +01:00
parent 2e78f3ab8d
commit 7c6e14ffe2
168 changed files with 47462 additions and 8914 deletions

View File

@@ -274,9 +274,9 @@ export default function MobileBillingPage() {
<ContextHelpLink slug="billing-packages-exports" />
</XStack>
{pendingCheckout && (checkoutStatus === 'failed' || checkoutStatus === 'cancelled') ? (
<MobileCard borderColor={danger} backgroundColor="$red1" space="$2">
<MobileCard borderColor={danger} backgroundColor="$red1" gap="$2">
<XStack alignItems="center" justifyContent="space-between">
<YStack space="$0.5" flex={1}>
<YStack gap="$0.5" flex={1}>
<Text fontSize="$sm" fontWeight="800" color={danger}>
{t('billing.checkoutFailedTitle', 'Checkout failed')}
</Text>
@@ -296,7 +296,7 @@ export default function MobileBillingPage() {
{t('billing.checkoutFailedBadge', 'Failed')}
</PillBadge>
</XStack>
<XStack space="$2">
<XStack gap="$2">
<CTAButton
label={t('billing.checkoutFailedRetry', 'Try again')}
onPress={() => navigate(shopLink)}
@@ -312,9 +312,9 @@ export default function MobileBillingPage() {
</MobileCard>
) : null}
{pendingCheckout && checkoutStatus === 'requires_customer_action' ? (
<MobileCard borderColor={accentSoft} backgroundColor={accentSoft} space="$2">
<MobileCard borderColor={accentSoft} backgroundColor={accentSoft} gap="$2">
<XStack alignItems="center" justifyContent="space-between">
<YStack space="$0.5" flex={1}>
<YStack gap="$0.5" flex={1}>
<Text fontSize="$sm" fontWeight="800" color={textStrong}>
{t('billing.checkoutActionTitle', 'Action required')}
</Text>
@@ -326,7 +326,7 @@ export default function MobileBillingPage() {
{t('billing.checkoutActionBadge', 'Action needed')}
</PillBadge>
</XStack>
<XStack space="$2">
<XStack gap="$2">
<CTAButton
label={t('billing.checkoutActionButton', 'Continue checkout')}
onPress={() => {
@@ -348,9 +348,9 @@ export default function MobileBillingPage() {
</MobileCard>
) : null}
{pendingCheckout && checkoutStatus !== 'failed' && checkoutStatus !== 'cancelled' && checkoutStatus !== 'requires_customer_action' ? (
<MobileCard borderColor={accentSoft} backgroundColor={accentSoft} space="$2">
<MobileCard borderColor={accentSoft} backgroundColor={accentSoft} gap="$2">
<XStack alignItems="center" justifyContent="space-between">
<YStack space="$0.5" flex={1}>
<YStack gap="$0.5" flex={1}>
<Text fontSize="$sm" fontWeight="800" color={textStrong}>
{t('billing.checkoutPendingTitle', 'Activating your package')}
</Text>
@@ -365,7 +365,7 @@ export default function MobileBillingPage() {
{t('billing.checkoutPendingBadge', 'Pending')}
</PillBadge>
</XStack>
<XStack space="$2">
<XStack gap="$2">
<CTAButton label={t('billing.checkoutPendingRefresh', 'Refresh')} onPress={load} fullWidth={false} />
<CTAButton
label={t('billing.checkoutPendingDismiss', 'Dismiss')}
@@ -377,8 +377,8 @@ export default function MobileBillingPage() {
</MobileCard>
) : null}
<MobileCard space="$2" ref={packagesRef as any}>
<XStack alignItems="center" space="$2">
<MobileCard gap="$2" ref={packagesRef as any}>
<XStack alignItems="center" gap="$2">
<Package size={18} color={textStrong} />
<Text fontSize="$md" fontWeight="800" color={textStrong}>
{t('billing.sections.packages.title', 'Packages')}
@@ -397,7 +397,7 @@ export default function MobileBillingPage() {
{t('common.loading', 'Lädt...')}
</Text>
) : (
<YStack space="$2">
<YStack gap="$2">
{activePackage ? (
<PackageCard
pkg={activePackage}
@@ -415,8 +415,8 @@ export default function MobileBillingPage() {
)}
</MobileCard>
<MobileCard space="$2" ref={invoicesRef as any}>
<XStack alignItems="center" space="$2">
<MobileCard gap="$2" ref={invoicesRef as any}>
<XStack alignItems="center" gap="$2">
<Receipt size={18} color={textStrong} />
<Text fontSize="$md" fontWeight="800" color={textStrong}>
{t('billing.sections.invoices.title', 'Invoices & Payments')}
@@ -430,7 +430,7 @@ export default function MobileBillingPage() {
{t('common.loading', 'Lädt...')}
</Text>
) : transactions.length === 0 ? (
<YStack space="$2">
<YStack gap="$2">
<Text fontSize="$sm" color={text}>
{t('billing.sections.invoices.empty', 'Keine Zahlungen gefunden.')}
</Text>
@@ -438,7 +438,7 @@ export default function MobileBillingPage() {
<CTAButton label={t('billing.actions.contactSupport', 'Contact support')} tone="ghost" onPress={openSupport} />
</YStack>
) : (
<YStack space="$1.5">
<YStack gap="$1.5">
{transactions.slice(0, 8).map((trx) => (
<XStack key={trx.id} alignItems="center" justifyContent="space-between" borderBottomWidth={1} borderColor={border} paddingVertical="$1.5">
<YStack>
@@ -475,8 +475,8 @@ export default function MobileBillingPage() {
)}
</MobileCard>
<MobileCard space="$2">
<XStack alignItems="center" space="$2">
<MobileCard gap="$2">
<XStack alignItems="center" gap="$2">
<Sparkles size={18} color={textStrong} />
<Text fontSize="$md" fontWeight="800" color={textStrong}>
{t('billing.sections.addOns.title', 'Add-ons')}
@@ -494,7 +494,7 @@ export default function MobileBillingPage() {
{t('billing.sections.addOns.empty', 'Keine Add-ons gebucht.')}
</Text>
) : (
<YStack space="$1.5">
<YStack gap="$1.5">
{addons.slice(0, 8).map((addon) => (
<AddonRow key={addon.id} addon={addon} />
))}
@@ -550,7 +550,7 @@ function PackageCard({
borderColor={isActive ? primary : border}
borderWidth={isActive ? 2 : 1}
backgroundColor={isActive ? accentSoft : undefined}
space="$2"
gap="$2"
>
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$md" fontWeight="800" color={textStrong}>
@@ -563,7 +563,7 @@ function PackageCard({
{expires}
</Text>
) : null}
<XStack space="$2" marginTop="$2" flexWrap="wrap">
<XStack gap="$2" marginTop="$2" flexWrap="wrap">
<PillBadge tone="muted">{remainingText}</PillBadge>
{pkg.price !== null && pkg.price !== undefined ? (
<PillBadge tone="muted">{formatAmount(pkg.price, pkg.currency ?? 'EUR')}</PillBadge>
@@ -578,7 +578,7 @@ function PackageCard({
</Text>
) : null}
{limitEntries.length ? (
<YStack space="$1.5" marginTop="$2">
<YStack gap="$1.5" marginTop="$2">
<Text fontSize="$xs" color={muted}>
{t('mobileBilling.details.limitsTitle', 'Limits')}
</Text>
@@ -595,12 +595,12 @@ function PackageCard({
</YStack>
) : null}
{featureKeys.length ? (
<YStack space="$1.5" marginTop="$2">
<YStack gap="$1.5" marginTop="$2">
<Text fontSize="$xs" color={muted}>
{t('mobileBilling.details.featuresTitle', 'Features')}
</Text>
{featureKeys.map((feature) => (
<XStack key={feature} alignItems="center" space="$2">
<XStack key={feature} alignItems="center" gap="$2">
<Sparkles size={14} color={primary} />
<Text fontSize="$xs" color={textStrong}>
{getPackageFeatureLabel(feature, t)}
@@ -610,7 +610,7 @@ function PackageCard({
</YStack>
) : null}
{usageMetrics.length ? (
<YStack space="$2" marginTop="$2">
<YStack gap="$2" marginTop="$2">
{usageMetrics.map((metric) => (
<UsageBar key={metric.key} metric={metric} />
))}
@@ -684,12 +684,12 @@ function UsageBar({ metric }: { metric: PackageUsageMetric }) {
const fillColor = status === 'danger' ? danger : status === 'warning' ? warningText : primary;
return (
<YStack space="$1.5">
<YStack gap="$1.5">
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$xs" color={muted}>
{labelMap[metric.key]}
</Text>
<XStack alignItems="center" space="$1.5">
<XStack alignItems="center" gap="$1.5">
{statusLabel ? <PillBadge tone={status === 'danger' ? 'danger' : 'warning'}>{statusLabel}</PillBadge> : null}
<Text fontSize="$xs" color={textStrong} fontWeight="700">
{valueText}
@@ -737,7 +737,7 @@ function AddonRow({ addon }: { addon: TenantAddonHistoryEntry }) {
const eventPath = addon.event?.slug ? ADMIN_EVENT_VIEW_PATH(addon.event.slug) : null;
const hasImpact = Boolean(addon.extra_photos || addon.extra_guests || addon.extra_gallery_days);
const impactBadges = hasImpact ? (
<XStack space="$2" marginTop="$1.5" flexWrap="wrap">
<XStack gap="$2" marginTop="$1.5" flexWrap="wrap">
{addon.extra_photos ? (
<PillBadge tone="muted">{t('mobileBilling.extra.photos', '+{{count}} photos', { count: addon.extra_photos })}</PillBadge>
) : null}
@@ -751,7 +751,7 @@ function AddonRow({ addon }: { addon: TenantAddonHistoryEntry }) {
) : null;
return (
<MobileCard borderColor={border} padding="$3" space="$1.5">
<MobileCard borderColor={border} padding="$3" gap="$1.5">
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$sm" fontWeight="700" color={textStrong}>
{addon.label ?? addon.addon_key}