SkateSpots/src/components/ShopBountyCard.tsx
SkateSpots 0b796440f1 feat(i18n): Spanish batch 2 — nav/chrome, events/shops/tours, crews/DMs, settings/auth, wallet/stash/newsletter/stats
~3,470 new keys (5,770 total, EN=ES), ~115 more files wired.
- nav + chromeUi: header, More menu, burger menu, footer, map toolbar widgets
  (sort/trust/weather), spot-type label helper.
- placesUi: events, shops, tours, food. socialUi: crews, DMs, notifications,
  collectives, crew chat/coordinator. settingsUi: settings, auth/signup,
  onboarding, profile edit, legal. walletUi: wallet/Cashu, stash, mints,
  app shop, newsletter, community stats, leaderboards, profile.
- Fixed 738 usages where an agent dropped the socialUi. prefix (caught by check:i18n).
2026-10-02 10:11:06 +07:00

185 lines
5.3 KiB
TypeScript

import { Card, CardContent } from '@/components/ui/card';
import { Zap, Check, Circle, Camera, FileText, MapPin, Phone, Clock } from 'lucide-react';
import { cn } from '@/lib/utils';
import { useTranslation } from '@/contexts/language';
interface ShopBountyCardProps {
// Current form state to calculate potential reward
hasName: boolean;
hasAddress: boolean;
hasCoordinates: boolean;
hasDescription: boolean;
hasPhone: boolean;
hasWebsite: boolean;
hasHours: boolean;
photoCount: number;
}
interface QualityCheckItem {
id: string;
label: string;
points: number;
completed: boolean;
icon: React.ComponentType<{ className?: string }>;
}
export function ShopBountyCard({
hasName,
hasAddress,
hasCoordinates,
hasDescription,
hasPhone,
hasWebsite,
hasHours,
photoCount,
}: ShopBountyCardProps) {
const { t } = useTranslation();
const checks: QualityCheckItem[] = [
{
id: 'name',
label: t('placesUi.bounty.shopName'),
points: 15,
completed: hasName,
icon: MapPin,
},
{
id: 'location',
label: t('placesUi.bounty.addressCoords'),
points: 20,
completed: hasAddress && hasCoordinates,
icon: MapPin,
},
{
id: 'photo1',
label: t('placesUi.bounty.onePhoto'),
points: 15,
completed: photoCount >= 1,
icon: Camera,
},
{
id: 'photo3',
label: t('placesUi.bounty.threePhotos'),
points: 10,
completed: photoCount >= 3,
icon: Camera,
},
{
id: 'description',
label: t('placesUi.bounty.description'),
points: 15,
completed: hasDescription,
icon: FileText,
},
{
id: 'contact',
label: t('placesUi.bounty.contactInfo'),
points: 10,
completed: hasPhone || hasWebsite,
icon: Phone,
},
{
id: 'hours',
label: t('placesUi.bounty.openingHours'),
points: 15,
completed: hasHours,
icon: Clock,
},
];
const completedChecks = checks.filter(c => c.completed);
const score = completedChecks.reduce((sum, c) => sum + c.points, 0);
const maxScore = checks.reduce((sum, c) => sum + c.points, 0);
const percentage = Math.round((score / maxScore) * 100);
const getScoreColor = () => {
if (percentage >= 80) return 'text-green-400';
if (percentage >= 50) return 'text-[#ccff00]';
if (percentage >= 30) return 'text-yellow-400';
return 'text-orange-400';
};
const getProgressColor = () => {
if (percentage >= 80) return 'bg-green-500';
if (percentage >= 50) return 'bg-[#ccff00]';
if (percentage >= 30) return 'bg-yellow-500';
return 'bg-orange-500';
};
const getScoreLabel = () => {
if (percentage >= 80) return t('placesUi.bounty.excellent');
if (percentage >= 50) return t('placesUi.bounty.good');
if (percentage >= 30) return t('placesUi.bounty.basic');
return t('placesUi.bounty.gettingStarted');
};
return (
<Card className="bg-gradient-to-br from-[#ccff00]/5 to-transparent border-[#ccff00]/20">
<CardContent className="p-4">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center gap-2">
<Zap className="h-4 w-4 text-[#ccff00]" />
<span className="text-sm font-bold uppercase text-gray-300">{t('placesUi.bounty.earningPotential')}</span>
</div>
<div className="flex items-center gap-2">
<span className={cn('text-sm font-bold', getScoreColor())}>
{getScoreLabel()}
</span>
<span className="text-xs text-gray-500">
{percentage}%
</span>
</div>
</div>
{/* Progress Bar */}
<div className="relative h-3 bg-gray-800 rounded-full overflow-hidden mb-4">
<div
className={cn('h-full transition-all duration-500 ease-out rounded-full', getProgressColor())}
style={{ width: `${percentage}%` }}
/>
</div>
{/* Checklist */}
<div className="space-y-2">
{checks.map((check) => {
const Icon = check.icon;
return (
<div
key={check.id}
className={cn(
'flex items-center gap-2 text-xs transition-all',
check.completed ? 'text-green-400' : 'text-gray-500'
)}
>
<div className="flex-shrink-0">
{check.completed ? (
<Check className="h-3.5 w-3.5" />
) : (
<Circle className="h-3.5 w-3.5" />
)}
</div>
<Icon className="h-3 w-3 flex-shrink-0" />
<span className={check.completed ? 'line-through opacity-70' : ''}>
{check.label}
</span>
<span className={cn(
'ml-auto text-[10px] font-bold',
check.completed ? 'text-green-400' : 'text-gray-600'
)}>
+{check.points}%
</span>
</div>
);
})}
</div>
{/* Tip */}
<div className="mt-4 pt-3 border-t border-gray-800">
<p className="text-[10px] text-gray-500">
{t('placesUi.bounty.tip')}
</p>
</div>
</CardContent>
</Card>
);
}