kanban-app/frontend/src/pages/CardDetail.tsx

183 lines
6 KiB
TypeScript
Raw Normal View History

2026-02-27 12:41:44 +00:00
import { useParams, Link, useNavigate } from 'react-router-dom';
import { useCard } from '../hooks/useCard';
import { useCardDetailMutations } from '../hooks/useCardDetailMutations';
2026-02-27 16:12:01 +00:00
import { useChecklistMutations } from '../hooks/useChecklistMutations';
2026-02-27 12:41:44 +00:00
import { useModal } from '../context/modals/useModal';
import { CardSidebar } from '../components/CardSidebar';
import { CardComments } from '../components/CardComments';
2026-02-27 16:12:01 +00:00
import { CardChecklists } from '../components/CardChecklists';
2026-02-27 12:41:44 +00:00
import { EditCardModal } from '../components/EditCardModal';
import { DeleteCardModal } from '../components/DeleteCardModal';
import TagIcon from '../components/icons/TagIcon';
import Trash2Icon from '../components/icons/Trash2Icon';
import ArrowLeftIcon from '../components/icons/ArrowLeftIcon';
import Edit2Icon from '../components/icons/Edit2Icon';
export function CardDetail() {
const { id: boardId, cardId } = useParams<{ id: string; cardId: string }>();
const navigate = useNavigate();
const { card, fetchCard } = useCard(parseInt(cardId || '0'));
const {
updateCardNameAndDescription,
deleteCardWithConfirmation,
addComment,
editComment,
deleteCommentWithConfirmation,
} = useCardDetailMutations(parseInt(cardId || '0'), card, fetchCard);
const { openModal } = useModal();
2026-02-27 16:12:01 +00:00
const checklistMutations = useChecklistMutations(parseInt(cardId || '0'), fetchCard);
2026-02-27 12:41:44 +00:00
const handleEditCard = () => {
if (!card) return;
openModal((props) => (
<EditCardModal
card={card}
onSave={async (name, description) => {
return await updateCardNameAndDescription(name, description);
}}
onClose={props.onClose}
/>
));
};
const handleDeleteCard = () => {
if (!card) return;
openModal((props) => (
<DeleteCardModal
cardName={card.name}
onDelete={async () => {
deleteCardWithConfirmation(() => {
props.onClose();
navigate(`/boards/${boardId}`);
});
}}
onClose={props.onClose}
/>
));
};
if (!card) {
return null;
}
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
};
return (
<div className="space-y-6">
<div className="flex justify-between items-start">
<div>
<Link
to={`/boards/${boardId}`}
className="text-gray-400 hover:text-white transition-colors text-sm flex items-center gap-1"
>
<span className="w-4 h-4">
<ArrowLeftIcon />
</span>
Back to Board
</Link>
<div className="flex items-center gap-3 mt-2">
<h1 className="text-3xl font-bold text-white">{card.name}</h1>
<button
onClick={handleEditCard}
className="text-gray-400 hover:text-white transition-colors"
title="Edit card"
>
<span className="w-5 h-5">
<Edit2Icon />
</span>
</button>
</div>
<p className="text-gray-400 text-sm mt-1">
In list Created {formatDate(card.created_at)}
</p>
</div>
<button
onClick={handleDeleteCard}
className="bg-red-600 hover:bg-red-700 text-white font-medium py-2 px-4 rounded-lg transition-colors flex items-center gap-2"
>
<span className="w-4 h-4">
<Trash2Icon />
</span>
Delete Card
</button>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2 space-y-6">
{/* Description Section */}
<div className="bg-gray-800 rounded-lg p-6">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-bold text-white flex items-center gap-2">Description</h2>
<button
onClick={handleEditCard}
className="text-blue-400 hover:text-blue-300 text-sm font-medium"
>
Edit
</button>
</div>
<p className="text-gray-300 whitespace-pre-wrap">
{card.description || 'No description added yet.'}
</p>
</div>
{/* Labels Section */}
{card.labels && card.labels.length > 0 && (
<div className="bg-gray-800 rounded-lg p-6">
<h2 className="text-xl font-bold text-white flex items-center gap-2 mb-4">
<span className="w-5 h-5">
<TagIcon />
</span>
Labels
</h2>
<div className="flex flex-wrap gap-2">
{card.labels.map((label: any) => (
<span
key={label.id}
className="px-3 py-1 rounded-full text-sm font-medium"
style={{ backgroundColor: label.color, color: 'white' }}
>
{label.name}
</span>
))}
</div>
</div>
)}
{/* Checklists Section */}
2026-02-27 16:12:01 +00:00
<CardChecklists
checklists={card.checklists || []}
cardId={parseInt(cardId || '0')}
addChecklist={checklistMutations.addChecklist}
removeChecklist={checklistMutations.removeChecklist}
addCheckItem={checklistMutations.addCheckItem}
toggleCheckItem={checklistMutations.toggleCheckItem}
editCheckItem={checklistMutations.editCheckItem}
removeCheckItem={checklistMutations.removeCheckItem}
/>
2026-02-27 12:41:44 +00:00
<CardComments
card={card}
addComment={addComment}
editComment={editComment}
deleteCommentWithConfirmation={deleteCommentWithConfirmation}
openModal={openModal}
/>
</div>
<CardSidebar card={card} />
</div>
</div>
);
}