104 lines
4.0 KiB
TypeScript
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;
|