SkateSpots/src/components/ShopBountyCard.tsx
SkateSpots d1f8870379 chore(lint): remove all explicit any + sweep dead imports
- Fix all 12 no-explicit-any (NPool, NostrEvent[], LabProfile, structural
  Cluster type, explicit SkateSpot cast) -> 0 remaining
- Remove unused imports/vars across 48 files (manual + a verified codemod for
  unused import specifiers only); tsc + tests + boot smoke all green
- Warning backlog: 182 -> 76 (remaining: ~55 no-unused-vars locals,
  14 react-hooks/exhaustive-deps, 7 react-refresh)
- AGENTS.md baseline updated
2026-09-26 21:40:42 +07:00

183 lines
5.1 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';
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 checks: QualityCheckItem[] = [
{
id: 'name',
label: 'Shop name',
points: 15,
completed: hasName,
icon: MapPin,
},
{
id: 'location',
label: 'Address & coordinates',
points: 20,
completed: hasAddress && hasCoordinates,
icon: MapPin,
},
{
id: 'photo1',
label: 'At least 1 photo',
points: 15,
completed: photoCount >= 1,
icon: Camera,
},
{
id: 'photo3',
label: '3+ photos',
points: 10,
completed: photoCount >= 3,
icon: Camera,
},
{
id: 'description',
label: 'Description',
points: 15,
completed: hasDescription,
icon: FileText,
},
{
id: 'contact',
label: 'Contact info',
points: 10,
completed: hasPhone || hasWebsite,
icon: Phone,
},
{
id: 'hours',
label: 'Opening hours',
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 'Excellent!';
if (percentage >= 50) return 'Good';
if (percentage >= 30) return 'Basic';
return 'Getting started';
};
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">Earning Potential</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">
💡 Higher quality = More zaps from the community!
</p>
</div>
</CardContent>
</Card>
);
}