Hintergründe zum EventInvitePage Layout Customizer hinzugefügt. Badge und CTA entfernt, Textfelder zu Textareas gemacht. Geschenkgutscheine verbessert, E-Mail-Versand ergänzt + Resend + Confirmationseite mit Code-Copy und Link zur Package-Seite, die den Code als URL-Parameter enthält.
This commit is contained in:
@@ -7,6 +7,7 @@ import { useLocalizedRoutes } from '@/hooks/useLocalizedRoutes';
|
||||
import { useLocale } from '@/hooks/useLocale';
|
||||
import { ADMIN_HOME_PATH } from '@/admin/constants';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { fetchGiftVoucherByCheckout, type GiftVoucherLookupResponse } from '@/lib/giftVouchers';
|
||||
|
||||
type SuccessProps = {
|
||||
type?: string;
|
||||
@@ -16,6 +17,58 @@ const GiftSuccess: React.FC = () => {
|
||||
const { t } = useTranslation('marketing');
|
||||
const { localizedPath } = useLocalizedRoutes();
|
||||
const locale = useLocale();
|
||||
const [voucher, setVoucher] = React.useState<GiftVoucherLookupResponse | null>(null);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
const [copied, setCopied] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const checkoutId = params.get('checkout_id') || sessionStorage.getItem('gift_checkout_id');
|
||||
const transactionId = params.get('transaction_id');
|
||||
|
||||
fetchGiftVoucherByCheckout(checkoutId || undefined, transactionId || undefined)
|
||||
.then((data) => {
|
||||
if (data) {
|
||||
setVoucher(data);
|
||||
} else {
|
||||
setError(t('success.gift_lookup_failed'));
|
||||
}
|
||||
})
|
||||
.catch(() => setError(t('success.gift_lookup_failed')))
|
||||
.finally(() => setLoading(false));
|
||||
}, [t]);
|
||||
|
||||
const onCopy = async () => {
|
||||
if (!voucher?.code) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(voucher.code);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch (e) {
|
||||
setError(t('success.gift_copy_failed'));
|
||||
}
|
||||
};
|
||||
|
||||
const onShare = async () => {
|
||||
if (!voucher?.code) return;
|
||||
const text = t('success.gift_share_text', {
|
||||
code: voucher.code,
|
||||
amount: voucher.amount.toFixed(2),
|
||||
currency: voucher.currency,
|
||||
});
|
||||
|
||||
if (navigator.share) {
|
||||
try {
|
||||
await navigator.share({ title: t('success.gift_title'), text });
|
||||
return;
|
||||
} catch (e) {
|
||||
// fall back to copy
|
||||
}
|
||||
}
|
||||
|
||||
await onCopy();
|
||||
};
|
||||
|
||||
return (
|
||||
<MarketingLayout title={t('success.gift_title')}>
|
||||
@@ -24,6 +77,42 @@ const GiftSuccess: React.FC = () => {
|
||||
<CheckCircle className="mx-auto h-12 w-12 text-green-500" />
|
||||
<h1 className="text-3xl font-bold text-foreground">{t('success.gift_title')}</h1>
|
||||
<p className="text-muted-foreground">{t('success.gift_description')}</p>
|
||||
<div className="rounded-xl border bg-card p-6 text-left shadow-sm">
|
||||
<h2 className="text-lg font-semibold">{t('success.gift_code_title')}</h2>
|
||||
{loading && <p className="text-muted-foreground">{t('success.gift_loading')}</p>}
|
||||
{error && <p className="text-destructive">{error}</p>}
|
||||
{voucher && (
|
||||
<div className="mt-3 space-y-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3 rounded-lg border bg-muted/50 px-4 py-3">
|
||||
<div className="text-left">
|
||||
<p className="text-xs uppercase tracking-wide text-muted-foreground">{t('success.gift_code_label')}</p>
|
||||
<p className="font-mono text-lg font-bold">{voucher.code}</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm" variant="secondary" onClick={onCopy}>
|
||||
{copied ? t('success.gift_copied') : t('success.gift_copy')}
|
||||
</Button>
|
||||
<Button size="sm" onClick={onShare}>
|
||||
{t('success.gift_share')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('success.gift_value', {
|
||||
amount: voucher.amount.toFixed(2),
|
||||
currency: voucher.currency,
|
||||
})}
|
||||
</p>
|
||||
{voucher.expires_at && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('success.gift_expires', {
|
||||
date: new Date(voucher.expires_at).toLocaleDateString(locale || undefined),
|
||||
})}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="rounded-xl border bg-card p-6 text-left shadow-sm">
|
||||
<h2 className="text-lg font-semibold">{t('success.gift_bullets_title')}</h2>
|
||||
<ul className="mt-3 list-disc space-y-2 pl-5 text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user