Files
placebo.mk/frontend/src/components/features/live-blog/LiveBlogUpdate.tsx
T
2026-01-29 11:27:45 +01:00

145 lines
4.4 KiB
TypeScript

import type { LiveBlogUpdate as ApiLiveBlogUpdate } from '@/lib/api';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import {
Pin,
PinOff,
Edit,
Trash2,
Clock,
User
} from 'lucide-react';
interface LiveBlogUpdateProps {
update: ApiLiveBlogUpdate;
isEditing?: boolean;
onEdit?: (update: ApiLiveBlogUpdate) => void;
onDelete?: (update: ApiLiveBlogUpdate) => void;
onPinToggle?: (update: ApiLiveBlogUpdate) => void;
showAdminControls?: boolean;
}
export function LiveBlogUpdate({
update,
isEditing = false,
onEdit,
onDelete,
onPinToggle,
showAdminControls = false,
}: LiveBlogUpdateProps) {
const isPinned = update.isPinned;
return (
<div
className={cn(
'relative p-4 rounded-lg border transition-all duration-200',
isPinned && 'border-primary bg-primary/5 shadow-sm',
!isPinned && 'border-border hover:border-muted-foreground/20'
)}
>
{/* Pinned indicator */}
{isPinned && (
<div className="absolute -top-2 -left-2 bg-primary text-primary-foreground text-xs px-2 py-1 rounded-full flex items-center gap-1">
<Pin className="w-3 h-3" />
Pinned
</div>
)}
{/* Admin controls */}
{showAdminControls && (
<div className="absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<Button
variant="ghost"
size="sm"
onClick={() => onPinToggle?.(update)}
className="h-8 w-8 p-0"
>
{isPinned ? <PinOff className="w-4 h-4" /> : <Pin className="w-4 h-4" />}
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => onEdit?.(update)}
className="h-8 w-8 p-0"
>
<Edit className="w-4 h-4" />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => onDelete?.(update)}
className="h-8 w-8 p-0 text-destructive hover:text-destructive"
>
<Trash2 className="w-4 h-4" />
</Button>
</div>
)}
<div className="flex items-start justify-between mb-3">
<div className="flex items-center gap-3">
{update.author ? (
<>
{update.author.avatar ? (
<img
src={update.author.avatar}
alt={update.author.name}
className="w-8 h-8 rounded-full object-cover"
/>
) : (
<div className="w-8 h-8 rounded-full bg-muted flex items-center justify-center">
<User className="w-4 h-4 text-muted-foreground" />
</div>
)}
<div>
<div className="font-medium text-sm">{update.author.name}</div>
{update.author.slug && (
<div className="text-xs text-muted-foreground">@{update.author.slug}</div>
)}
</div>
</>
) : (
<div className="flex items-center gap-2 text-muted-foreground">
<User className="w-4 h-4" />
<span className="text-sm">Anonymous</span>
</div>
)}
</div>
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<Clock className="w-3 h-3" />
<span>{new Date(update.createdAt).toLocaleTimeString()}</span>
</div>
</div>
{/* Update content */}
<div className="prose prose-sm max-w-none">
{isEditing ? (
<textarea
defaultValue={update.content}
className="w-full p-2 border rounded-md resize-none focus:outline-none focus:ring-2 focus:ring-primary"
rows={3}
autoFocus
/>
) : (
<div className="text-sm leading-relaxed whitespace-pre-wrap">
{update.content}
</div>
)}
</div>
{/* Scheduled indicator */}
{update.scheduledAt && new Date(update.scheduledAt) > new Date() && (
<div className="mt-2 text-xs text-muted-foreground flex items-center gap-1">
<Clock className="w-3 h-3" />
Scheduled for: {new Date(update.scheduledAt).toLocaleString()}
</div>
)}
{/* Update metadata */}
<div className="mt-2 text-xs text-muted-foreground">
ID: {update.id.slice(0, 8)}
</div>
</div>
);
}