Initial commit: Agile Project Manager
- Complete React + TypeScript application with Vite - Dashboard with charts and metrics visualization - Kanban board with drag-and-drop functionality - Product backlog management with user stories - Sprint planning and tracking features - Comprehensive UI component library (shadcn/ui) - Tailwind CSS styling with dark mode support - Context-based state management - Mock data for immediate testing and demonstration
This commit is contained in:
83
components/CommentDialog.tsx
Normal file
83
components/CommentDialog.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from './ui/dialog';
|
||||
import { Button } from './ui/button';
|
||||
import { Textarea } from './ui/textarea';
|
||||
import { useApp } from '../contexts/AppContext';
|
||||
import { Task } from '../types';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { Send } from 'lucide-react';
|
||||
|
||||
interface CommentDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
task: Task | null;
|
||||
}
|
||||
|
||||
export const CommentDialog: React.FC<CommentDialogProps> = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
task
|
||||
}) => {
|
||||
const { addComment } = useApp();
|
||||
const [commentText, setCommentText] = useState('');
|
||||
|
||||
const handlePost = () => {
|
||||
if (task && commentText.trim()) {
|
||||
addComment(task.id, commentText, 'Current User');
|
||||
setCommentText('');
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handlePost();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-[600px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Comments - {task?.title}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-4 max-h-[300px] overflow-y-auto">
|
||||
{task?.comments.length === 0 ? (
|
||||
<p className="text-center text-muted-foreground py-8">
|
||||
No comments yet. Be the first to comment!
|
||||
</p>
|
||||
) : (
|
||||
task?.comments.map(comment => (
|
||||
<div key={comment.id} className="border rounded-lg p-3 space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">{comment.author}</span>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{formatDistanceToNow(new Date(comment.timestamp), { addSuffix: true })}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm">{comment.text}</p>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Textarea
|
||||
value={commentText}
|
||||
onChange={(e) => setCommentText(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder="Write a comment... (Press Enter to post, Shift+Enter for new line)"
|
||||
rows={3}
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<Button onClick={handlePost} disabled={!commentText.trim()}>
|
||||
<Send className="mr-2 h-4 w-4" />
|
||||
Post
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user