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

181 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 18:14:02 +00:00
import { useLabels } from '../hooks/useLabels';
import { useLabelMutations, useCardLabelMutations } from '../hooks/useLabelMutations';
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 18:14:02 +00:00
import { CardLabels } from '../components/CardLabels';
2026-02-27 12:41:44 +00:00
import { EditCardModal } from '../components/EditCardModal';
import { DeleteCardModal } from '../components/DeleteCardModal';
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
2026-02-27 18:14:02 +00:00
// Labels functionality
const { labels, refetch: refetchLabels } = useLabels(parseInt(boardId || '0'));
const { addLabel } = useLabelMutations(parseInt(boardId || '0'), refetchLabels);
const { addLabelToCardMutation, removeLabelFromCardMutation } = useCardLabelMutations(
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 */}
2026-02-27 18:14:02 +00:00
<CardLabels
labels={labels}
cardLabels={card.labels || []}
addLabel={addLabel}
addLabelToCard={addLabelToCardMutation}
removeLabelFromCard={removeLabelFromCardMutation}
refetchLabels={refetchLabels}
refetchCard={fetchCard}
/>
2026-02-27 12:41:44 +00:00
{/* 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>
);
}