Files
fotospiel-app/resources/js/pages/marketing/GiftVoucherStatus.tsx
2025-12-08 20:18:01 +01:00

104 lines
4.0 KiB
TypeScript

import React from 'react';
import MarketingLayout from '@/layouts/mainWebsite';
import { useTranslation } from 'react-i18next';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { fetchGiftVoucherByCode, type GiftVoucherLookupResponse } from '@/lib/giftVouchers';
import { useRateLimitHelper } from '@/hooks/useRateLimitHelper';
const GiftVoucherStatus: React.FC = () => {
const { t, i18n } = useTranslation('marketing');
const [code, setCode] = React.useState('');
const [result, setResult] = React.useState<GiftVoucherLookupResponse | null>(null);
const [error, setError] = React.useState<string | null>(null);
const [loading, setLoading] = React.useState(false);
const rateLimit = useRateLimitHelper('voucher');
const onLookup = async () => {
if (rateLimit.isLimited(code)) {
setError(t('gift.too_many_attempts'));
return;
}
setLoading(true);
setError(null);
setResult(null);
try {
const data = await fetchGiftVoucherByCode(code);
if (data) {
setResult(data);
rateLimit.clear(code);
} else {
setError(t('gift.lookup_not_found'));
rateLimit.bump(code);
}
} catch (e: any) {
setError(e?.message || t('gift.lookup_not_found'));
rateLimit.bump(code);
} finally {
setLoading(false);
}
};
return (
<MarketingLayout title={t('gift.lookup_title')}>
<section className="bg-gradient-to-b from-background via-muted/30 to-background py-16">
<div className="mx-auto flex max-w-3xl flex-col gap-8 px-4 sm:px-6">
<div className="space-y-3 text-center">
<h1 className="text-3xl font-bold text-foreground">{t('gift.lookup_title')}</h1>
<p className="text-muted-foreground">{t('gift.lookup_subtitle')}</p>
</div>
<Card>
<CardHeader>
<CardTitle>{t('gift.lookup_label')}</CardTitle>
<CardDescription>{t('gift.lookup_subtitle')}</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-1">
<Label htmlFor="code">{t('gift.lookup_label')}</Label>
<Input
id="code"
placeholder="GIFT-XXXXXX"
value={code}
onChange={(e) => setCode(e.target.value)}
/>
</div>
<Button onClick={onLookup} disabled={loading || !code.trim()}>
{loading ? t('gift.processing', 'Checking...') : t('gift.lookup_cta')}
</Button>
{error && <p className="text-sm text-destructive">{error}</p>}
{result && (
<div className="space-y-2 rounded-md border bg-muted/30 p-4 text-sm">
<p className="font-semibold">{t('gift.lookup_result_code', { code: result.code })}</p>
<p className="text-muted-foreground">
{t('gift.lookup_result_value', {
amount: result.amount.toFixed(2),
currency: result.currency,
})}
</p>
{result.expires_at && (
<p className="text-muted-foreground">
{t('gift.lookup_result_expires', {
date: new Date(result.expires_at).toLocaleDateString(i18n.language),
})}
</p>
)}
<p className="text-muted-foreground">
{t(`gift.lookup_status.${result.status}`, result.status)}
</p>
</div>
)}
</CardContent>
</Card>
</div>
</section>
</MarketingLayout>
);
};
GiftVoucherStatus.layout = (page: React.ReactNode) => page;
export default GiftVoucherStatus;