feat(addons): finalize event addon catalog and ai styling upgrade flow
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-07 12:35:07 +01:00
parent 8cc0918881
commit d2808ffa4f
36 changed files with 1372 additions and 457 deletions

View File

@@ -130,25 +130,16 @@ export default function MobileEventRecapPage() {
};
}, [event?.status, i18n.language, invites, t]);
const handleCheckout = async (addonKey: string) => {
if (!slug || busyScope) return;
setBusyScope(addonKey);
try {
const { checkout_url } = await createEventAddonCheckout(slug, {
addon_key: addonKey,
success_url: window.location.href,
cancel_url: window.location.href,
});
if (checkout_url) {
window.location.href = checkout_url;
}
} catch (err) {
toast.error(getApiErrorMessage(err, t('events.errors.checkoutFailed', 'Bezahlvorgang konnte nicht gestartet werden.')));
setBusyScope(null);
const handleCheckout = (addonKey: string) => {
if (!slug || busyScope) {
return;
}
setBusyScope(addonKey);
setConsentOpen(true);
};
const handleConsentConfirm = async (consents: { acceptedTerms: boolean }) => {
const handleConsentConfirm = async (consents: { acceptedTerms: boolean; acceptedWaiver: boolean }) => {
if (!slug || !busyScope) return;
try {
const { checkout_url } = await createEventAddonCheckout(slug, {
@@ -156,10 +147,16 @@ export default function MobileEventRecapPage() {
success_url: window.location.href,
cancel_url: window.location.href,
accepted_terms: consents.acceptedTerms,
} as any);
accepted_waiver: consents.acceptedWaiver,
});
if (checkout_url) {
window.location.href = checkout_url;
return;
}
toast.error(t('events.errors.checkoutMissing', 'Checkout konnte nicht gestartet werden.'));
setBusyScope(null);
setConsentOpen(false);
} catch (err) {
toast.error(getApiErrorMessage(err, t('events.errors.checkoutFailed', 'Bezahlvorgang konnte nicht gestartet werden.')));
setBusyScope(null);
@@ -200,6 +197,9 @@ export default function MobileEventRecapPage() {
const activeInvite = invites.find((i) => i.is_active) ?? invites[0] ?? null;
const guestLink = activeInvite?.url ?? '';
const galleryExtensionAddons = addons
.filter((addon) => addon.key.startsWith('extend_gallery_') || Number(addon.increments?.extra_gallery_days ?? 0) > 0)
.sort((left, right) => Number(left.increments?.extra_gallery_days ?? 0) - Number(right.increments?.extra_gallery_days ?? 0));
return (
<MobileShell
@@ -397,16 +397,19 @@ export default function MobileEventRecapPage() {
</Text>
<YStack gap="$2">
{addons
.filter((a) => a.key === 'gallery_extension')
.map((addon) => (
<CTAButton
key={addon.key}
label={t('events.recap.buyExtension', 'Galerie um 30 Tage verlängern')}
onPress={() => handleCheckout(addon.key)}
loading={busyScope === addon.key}
/>
))}
{galleryExtensionAddons.map((addon) => (
<CTAButton
key={addon.key}
label={addon.label || t('events.recap.buyExtension', 'Galerie um 30 Tage verlängern')}
onPress={() => handleCheckout(addon.key)}
loading={busyScope === addon.key}
/>
))}
{galleryExtensionAddons.length === 0 ? (
<Text fontSize="$sm" color={muted}>
{t('events.recap.noAddonAvailable', 'Aktuell sind keine Galerie-Add-ons verfügbar.')}
</Text>
) : null}
</YStack>
</MobileCard>
</YStack>