SkateSpots/src/components/TradeConfirmRow.tsx
SkateSpots 09735375c2 feat(i18n): Spanish (Castellano) — infra + Map/Spots, Marketplace, Lab (batch 1)
- First-visit language auto-detect (Spanish devices land in es; stored choice wins).
- Sasquatch AI replies in the app language (skate trick names kept English).
- i18n safety net: scripts/check-i18n.mjs wired into npm test — fails on any t()
  key missing from en/es or any en/es key drift.
- ~1,680 new keys (2,297 total) across mapUi / spotUi / marketUi / labUi and
  ~65 files wired to t(): Map, spots (detail/create/edit/chat), marketplace,
  trades, offers, escrow, shipping, The Lab, /become/* forms, coaches, skaters.
- Policy: only skate TRICK names stay English; user-generated content untouched.
2026-10-01 23:57:20 +07:00

72 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, type ComponentType, type ReactNode } from 'react';
import { Link } from 'react-router-dom';
import { ArrowRight, CheckCircle, Loader2 } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { DeckRating } from '@/components/DeckRating';
import { cn } from '@/lib/utils';
import { useTranslation } from '@/contexts/language';
interface TradeConfirmRowProps {
tone: 'sale' | 'purchase';
icon: ComponentType<{ className?: string }>;
text: ReactNode;
listingUrl: string;
busy: boolean;
disabled: boolean;
actionLabel: string;
/** Called with the chosen deck rating (0 = none). */
onConfirm: (rating: number) => void;
}
/**
* A pending trade to confirm, with a 1–10 skateboard-deck rating the reviewer
* can set before confirming. The rating rides along on the kind-31871
* attestation, so it becomes the review.
*/
export function TradeConfirmRow({
tone,
icon: Icon,
text,
listingUrl,
busy,
disabled,
actionLabel,
onConfirm,
}: TradeConfirmRowProps) {
const { t } = useTranslation();
const [rating, setRating] = useState(0);
const cardCls = tone === 'sale' ? 'border-[#ff6600]/30 bg-[#ff6600]/5' : 'border-[#ccff00]/30 bg-[#ccff00]/5';
const iconCls = tone === 'sale' ? 'text-[#ff6600]' : 'text-[#ccff00]';
const btnCls = tone === 'sale' ? 'bg-[#ff6600] hover:bg-[#ff6600]/90' : 'bg-[#ccff00] hover:bg-[#ccff00]/90';
return (
<Card className={cn('border-2', cardCls)}>
<CardContent className="flex flex-col gap-2 p-3">
<div className="flex items-center gap-3">
<Icon className={cn('h-5 w-5 shrink-0', iconCls)} />
<div className="min-w-0 flex-1">
<p className="text-sm text-white">{text}</p>
<Link to={listingUrl} className="text-xs text-gray-400 hover:text-white">
{t('marketUi.trades.viewListing')} <ArrowRight className="inline h-3 w-3" />
</Link>
</div>
<Button
size="sm"
onClick={() => onConfirm(rating)}
disabled={disabled}
className={cn('shrink-0 gap-1.5 font-bold text-black', btnCls)}
>
{busy ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <CheckCircle className="h-3.5 w-3.5" />}
{actionLabel}
</Button>
</div>
<div className="flex items-center gap-2 pl-8">
<span className="shrink-0 text-[11px] text-gray-500">{t('marketUi.confirmRow.rateTrade')}</span>
<DeckRating value={rating} onChange={setRating} size="sm" />
</div>
</CardContent>
</Card>
);
}