gift voucher language fixes + move into user menu
This commit is contained in:
@@ -10,34 +10,30 @@ import { Textarea } from '@/components/ui/textarea';
|
||||
import {
|
||||
fetchGiftVoucherTiers,
|
||||
createGiftVoucherCheckout,
|
||||
fetchGiftVoucherByCode,
|
||||
type GiftVoucherTier,
|
||||
type GiftVoucherLookupResponse,
|
||||
} from '@/lib/giftVouchers';
|
||||
import { useLocalizedRoutes } from '@/hooks/useLocalizedRoutes';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useRateLimitHelper } from '@/hooks/useRateLimitHelper';
|
||||
|
||||
function useGiftVoucherTiers(initial: GiftVoucherTier[] = []) {
|
||||
const [tiers, setTiers] = React.useState<GiftVoucherTier[]>(initial);
|
||||
const [loading, setLoading] = React.useState(initial.length === 0);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
const { locale } = useLocalizedRoutes();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (initial.length > 0) {
|
||||
setTiers(initial.filter((tier) => tier.currency === 'EUR'));
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
fetchGiftVoucherTiers()
|
||||
.then((data) => {
|
||||
const preferredCurrency = locale === 'en' ? 'USD' : 'EUR';
|
||||
const preferred = data.filter((tier) => tier.currency === preferredCurrency && tier.can_checkout);
|
||||
setTiers(preferred.length > 0 ? preferred : data);
|
||||
const eurOnly = data.filter((tier) => tier.currency === 'EUR');
|
||||
setTiers(eurOnly.length > 0 ? eurOnly : data);
|
||||
})
|
||||
.catch((err) => setError(err?.message || 'Failed to load tiers'))
|
||||
.finally(() => setLoading(false));
|
||||
}, [initial, locale]);
|
||||
}, [initial]);
|
||||
|
||||
return { tiers, loading, error };
|
||||
}
|
||||
@@ -57,11 +53,6 @@ function GiftVoucherPage({ tiers: initialTiers = [] }: { tiers: GiftVoucherTier[
|
||||
accept_terms: false,
|
||||
});
|
||||
const [errors, setErrors] = React.useState<Record<string, string | null>>({});
|
||||
const [lookupCode, setLookupCode] = React.useState('');
|
||||
const [lookupResult, setLookupResult] = React.useState<GiftVoucherLookupResponse | null>(null);
|
||||
const [lookupError, setLookupError] = React.useState<string | null>(null);
|
||||
const [lookupLoading, setLookupLoading] = React.useState(false);
|
||||
const rateLimit = useRateLimitHelper('voucher');
|
||||
|
||||
const selectedTierKey = form.tier_key;
|
||||
|
||||
@@ -129,31 +120,6 @@ function GiftVoucherPage({ tiers: initialTiers = [] }: { tiers: GiftVoucherTier[
|
||||
}
|
||||
};
|
||||
|
||||
const onLookup = async () => {
|
||||
if (rateLimit.isLimited(lookupCode)) {
|
||||
setLookupError(t('gift.too_many_attempts'));
|
||||
return;
|
||||
}
|
||||
|
||||
setLookupLoading(true);
|
||||
setLookupError(null);
|
||||
setLookupResult(null);
|
||||
try {
|
||||
const result = await fetchGiftVoucherByCode(lookupCode);
|
||||
if (result) {
|
||||
setLookupResult(result);
|
||||
rateLimit.clear(lookupCode);
|
||||
} else {
|
||||
setLookupError(t('gift.lookup_not_found'));
|
||||
rateLimit.bump(lookupCode);
|
||||
}
|
||||
} catch (error: any) {
|
||||
setLookupError(error?.message || t('gift.lookup_not_found'));
|
||||
rateLimit.bump(lookupCode);
|
||||
} finally {
|
||||
setLookupLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<MarketingLayout title={t('gift.title')}>
|
||||
@@ -294,52 +260,6 @@ function GiftVoucherPage({ tiers: initialTiers = [] }: { tiers: GiftVoucherTier[
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
<Card className="lg:col-span-3">
|
||||
<CardHeader>
|
||||
<CardTitle>{t('gift.lookup_title')}</CardTitle>
|
||||
<CardDescription>{t('gift.lookup_subtitle')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-3 md:grid-cols-[2fr,1fr,auto]">
|
||||
<div className="space-y-1 md:col-span-2">
|
||||
<Label htmlFor="lookup_code">{t('gift.lookup_label')}</Label>
|
||||
<Input
|
||||
id="lookup_code"
|
||||
placeholder="GIFT-XXXXXX"
|
||||
value={lookupCode}
|
||||
onChange={(e) => setLookupCode(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-end">
|
||||
<Button onClick={onLookup} disabled={lookupLoading || !lookupCode.trim()}>
|
||||
{lookupLoading ? t('gift.processing') : t('gift.lookup_cta')}
|
||||
</Button>
|
||||
</div>
|
||||
{lookupError && <p className="md:col-span-3 text-sm text-destructive">{lookupError}</p>}
|
||||
{lookupResult && (
|
||||
<div className="md:col-span-3 space-y-1 rounded-lg border bg-muted/40 p-4">
|
||||
<p className="text-sm font-semibold">
|
||||
{t('gift.lookup_result_code', { code: lookupResult.code })}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('gift.lookup_result_value', {
|
||||
amount: lookupResult.amount.toFixed(2),
|
||||
currency: lookupResult.currency,
|
||||
})}
|
||||
</p>
|
||||
{lookupResult.expires_at && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('gift.lookup_result_expires', {
|
||||
date: new Date(lookupResult.expires_at).toLocaleDateString(locale || undefined),
|
||||
})}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t(`gift.lookup_status.${lookupResult.status}`, lookupResult.status)}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user