155 lines
6 KiB
TypeScript
155 lines
6 KiB
TypeScript
import { CardWithDetails } from '../types/kanban';
|
||
import { Link } from 'react-router-dom';
|
||
|
||
interface CardPreviewModalProps {
|
||
card: CardWithDetails;
|
||
onClose: () => void;
|
||
}
|
||
|
||
export function CardPreviewModal({ card, onClose }: CardPreviewModalProps) {
|
||
return (
|
||
<div className="bg-gray-800 rounded-lg p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto">
|
||
<div className="flex justify-between items-start mb-4">
|
||
<Link
|
||
to={`/boards/${card.board_id}/cards/${card.id}`}
|
||
className="text-2xl font-bold text-blue-400 hover:text-blue-300 hover:underline transition-colors"
|
||
>
|
||
{card.name}
|
||
</Link>
|
||
<button
|
||
onClick={onClose}
|
||
className="text-gray-400 hover:text-white transition-colors text-2xl leading-none"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
|
||
{/* Labels */}
|
||
{card.labels && card.labels.length > 0 && (
|
||
<div className="mb-4">
|
||
<div className="flex flex-wrap gap-2">
|
||
{card.labels.map((label) => (
|
||
<span
|
||
key={label.id}
|
||
className="px-3 py-1 rounded-full text-sm font-medium text-white"
|
||
style={{ backgroundColor: label.color }}
|
||
>
|
||
{label.name}
|
||
</span>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Description */}
|
||
{card.description && (
|
||
<div className="mb-4">
|
||
<h3 className="text-lg font-semibold text-white mb-2">Description</h3>
|
||
<p className="text-gray-300 whitespace-pre-wrap">{card.description}</p>
|
||
</div>
|
||
)}
|
||
|
||
{/* Checklists */}
|
||
{card.checklists && card.checklists.length > 0 && (
|
||
<div className="mb-4">
|
||
<h3 className="text-lg font-semibold text-white mb-2">Checklists</h3>
|
||
<div className="space-y-3">
|
||
{card.checklists.map((checklist) => {
|
||
const completedItems = checklist.items.filter(
|
||
(item) => item.state === 'complete'
|
||
).length;
|
||
const totalItems = checklist.items.length;
|
||
const progress = totalItems > 0 ? (completedItems / totalItems) * 100 : 0;
|
||
|
||
return (
|
||
<div key={checklist.id} className="bg-gray-700 rounded-lg p-4">
|
||
<div className="flex justify-between items-center mb-3">
|
||
<h4 className="font-semibold text-white">{checklist.name}</h4>
|
||
<span className="text-sm text-gray-400">
|
||
{completedItems}/{totalItems}
|
||
</span>
|
||
</div>
|
||
<div className="w-full h-2 bg-gray-600 rounded-full overflow-hidden mb-3">
|
||
<div
|
||
className="h-full bg-green-500 transition-all duration-300"
|
||
style={{ width: `${progress}%` }}
|
||
/>
|
||
</div>
|
||
{/* Checklist Items */}
|
||
<div className="space-y-2">
|
||
{checklist.items.map((item) => (
|
||
<div key={item.id} className="flex items-start gap-2 text-sm">
|
||
<div
|
||
className={`w-4 h-4 mt-0.5 rounded border-2 flex items-center justify-center flex-shrink-0 ${
|
||
item.state === 'complete'
|
||
? 'bg-green-500 border-green-500'
|
||
: 'border-gray-500'
|
||
}`}
|
||
>
|
||
{item.state === 'complete' && (
|
||
<svg
|
||
className="w-3 h-3 text-white"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
>
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth="3"
|
||
d="M5 13l4 4L19 7"
|
||
/>
|
||
</svg>
|
||
)}
|
||
</div>
|
||
<span
|
||
className={`text-gray-300 ${
|
||
item.state === 'complete' ? 'line-through text-gray-500' : ''
|
||
}`}
|
||
>
|
||
{item.name}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Comments Preview */}
|
||
{card.comments && card.comments.length > 0 && (
|
||
<div>
|
||
<h3 className="text-lg font-semibold text-white mb-2">Comments</h3>
|
||
<div className="space-y-3 max-h-64 overflow-y-auto">
|
||
{card.comments.slice(0, 3).map((comment) => (
|
||
<div key={comment.id} className="bg-gray-700 rounded-lg p-3">
|
||
<div className="flex items-center gap-2 mb-2">
|
||
<div className="w-6 h-6 bg-blue-600 rounded-full flex items-center justify-center text-white font-bold text-xs">
|
||
{comment.user?.username.charAt(0).toUpperCase() || '?'}
|
||
</div>
|
||
<div>
|
||
<p className="text-white text-sm font-medium">
|
||
{comment.user?.username || 'Unknown'}
|
||
</p>
|
||
<p className="text-gray-400 text-xs">
|
||
{new Date(comment.created_at).toLocaleDateString()}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<p className="text-gray-300 text-sm line-clamp-3">{comment.text}</p>
|
||
</div>
|
||
))}
|
||
{card.comments.length > 3 && (
|
||
<p className="text-gray-400 text-sm text-center">
|
||
+{card.comments.length - 3} more comments
|
||
</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|