Files
thrilltrack-explorer/src-old/components/homepage/RecentChangeCard.tsx

133 lines
5.1 KiB
TypeScript

import { Link } from 'react-router-dom';
import { Card } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Clock, User } from 'lucide-react';
import { formatDistanceToNow } from 'date-fns';
interface RecentChangeCardProps {
entityType: 'park' | 'ride' | 'company';
entityId: string;
entityName: string;
entitySlug: string;
parkSlug?: string;
imageUrl?: string | null;
changeType: string;
changedAt: string;
changedByUsername?: string | null;
changedByAvatar?: string | null;
changeReason?: string | null;
}
const changeTypeColors = {
created: 'bg-green-500/10 text-green-700 dark:text-green-400 border-green-500/20',
updated: 'bg-blue-500/10 text-blue-700 dark:text-blue-400 border-blue-500/20',
deleted: 'bg-red-500/10 text-red-700 dark:text-red-400 border-red-500/20',
restored: 'bg-purple-500/10 text-purple-700 dark:text-purple-400 border-purple-500/20',
archived: 'bg-muted text-muted-foreground border-muted',
};
const entityTypeColors = {
park: 'bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 border-emerald-500/20',
ride: 'bg-orange-500/10 text-orange-700 dark:text-orange-400 border-orange-500/20',
company: 'bg-indigo-500/10 text-indigo-700 dark:text-indigo-400 border-indigo-500/20',
};
const formatEntityType = (type: string): string => {
return type
.split('_')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
};
const formatChangeType = (type: string): string => {
return type
.split('_')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
};
export function RecentChangeCard({
entityType,
entityId,
entityName,
entitySlug,
parkSlug,
imageUrl,
changeType,
changedAt,
changedByUsername,
changedByAvatar,
changeReason,
}: RecentChangeCardProps) {
const getEntityPath = () => {
if (entityType === 'park') return `/parks/${entitySlug}`;
if (entityType === 'ride') {
// For rides, use park slug if available, otherwise fallback to global rides list
if (parkSlug) {
return `/parks/${parkSlug}/rides/${entitySlug}`;
}
return `/rides`;
}
// Company paths - link to the appropriate company page
return '/';
};
return (
<Link to={getEntityPath()}>
<Card className="group overflow-hidden border-border/50 bg-gradient-to-br from-card via-card to-card/80 hover:shadow-2xl hover:shadow-primary/20 hover:border-primary/30 transition-all duration-300 hover:scale-[1.02] h-full relative before:absolute before:inset-0 before:rounded-lg before:p-[1px] before:bg-gradient-to-br before:from-primary/20 before:via-transparent before:to-accent/20 before:-z-10 before:opacity-0 hover:before:opacity-100 before:transition-opacity before:duration-300">
{imageUrl && (
<div className="aspect-[3/2] w-full overflow-hidden bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 relative">
<img
src={imageUrl}
alt={entityName}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
loading="lazy"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent opacity-80 group-hover:opacity-60 transition-opacity duration-300" />
</div>
)}
<div className="p-2.5 space-y-1.5 border-t border-border/30">
<div className="space-y-2">
<div className="flex items-center gap-2 flex-wrap">
<Badge variant="outline" className={entityTypeColors[entityType]}>
{formatEntityType(entityType)}
</Badge>
<Badge variant="outline" className={changeTypeColors[changeType as keyof typeof changeTypeColors] || changeTypeColors.archived}>
{formatChangeType(changeType)}
</Badge>
</div>
<h3 className="font-bold line-clamp-2 text-sm group-hover:text-primary transition-all duration-300 group-hover:drop-shadow-[0_0_8px_rgba(139,92,246,0.5)]">{entityName}</h3>
</div>
{changeReason && (
<p className="text-xs text-muted-foreground line-clamp-2 italic">
{changeReason}
</p>
)}
<div className="flex items-center justify-between text-xs text-muted-foreground gap-2">
<div className="flex items-center gap-1">
<Clock className="h-3 w-3" />
<span className="line-clamp-1">{formatDistanceToNow(new Date(changedAt), { addSuffix: true })}</span>
</div>
{changedByUsername && (
<div className="flex items-center gap-1">
<Avatar className="h-4 w-4">
<AvatarImage src={changedByAvatar || undefined} />
<AvatarFallback>
<User className="h-2 w-2" />
</AvatarFallback>
</Avatar>
<span className="line-clamp-1">{changedByUsername}</span>
</div>
)}
</div>
</div>
</Card>
</Link>
);
}