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>
|
|
|
|
|
);
|
|
|
|
|
}
|