'use client'; import * as React from 'react'; import Avatar from '@mui/material/Avatar'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Checkbox from '@mui/material/Checkbox'; import Chip from '@mui/material/Chip'; import Dialog from '@mui/material/Dialog'; import DialogContent from '@mui/material/DialogContent'; import Divider from '@mui/material/Divider'; import FormControlLabel from '@mui/material/FormControlLabel'; import IconButton from '@mui/material/IconButton'; import InputAdornment from '@mui/material/InputAdornment'; import LinearProgress from '@mui/material/LinearProgress'; import Link from '@mui/material/Link'; import OutlinedInput from '@mui/material/OutlinedInput'; import Paper from '@mui/material/Paper'; import Stack from '@mui/material/Stack'; import Tab from '@mui/material/Tab'; import Tabs from '@mui/material/Tabs'; import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { Archive as ArchiveIcon } from '@phosphor-icons/react/dist/ssr/Archive'; import { File as FileIcon } from '@phosphor-icons/react/dist/ssr/File'; import { PaperPlaneTilt as PaperPlaneTiltIcon } from '@phosphor-icons/react/dist/ssr/PaperPlaneTilt'; import { PencilSimple as PencilSimpleIcon } from '@phosphor-icons/react/dist/ssr/PencilSimple'; import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus'; import { X as XIcon } from '@phosphor-icons/react/dist/ssr/X'; import { dayjs } from '@/lib/dayjs'; import type { Comment, Task } from './types'; export interface TaskModalProps { onClose?: () => void; onTaskDelete?: (taskId: string) => void; onTaskUpdate?: (taskId: string, params: { title?: string; description?: string }) => void; onCommentAdd?: (taskId: string, content: string) => void; open: boolean; task: Task; } export function TaskModal({ onClose, onTaskDelete, onTaskUpdate, onCommentAdd, open, task, }: TaskModalProps): React.JSX.Element { const { assignees = [], attachments = [], comments = [], labels = [], subtasks = [], description = '', id, title, } = task; const [tab, setTab] = React.useState('overview'); return ( { setTab(value); }} sx={{ px: 3 }} value={tab} > {tab === 'overview' ? ( { onTaskUpdate?.(id, params); }} title={title} /> } spacing={2} sx={{ flex: '1 1 auto' }}> Created by
{task.author.name} @{task.author.username}
Assignees {assignees.map( (assignee): React.JSX.Element => ( ) )} Due date Labels {labels.map((label) => ( { // noop }} size="small" variant="soft" /> ))} Attachments {attachments.map( (attachment): React.JSX.Element => (
{attachment.name} {attachment.size}
) )}
) : null} {tab === 'subtasks' ? ( {subtasks.length ? ( {countDoneSubtasks(subtasks)} of 5 {subtasks.map( (subtask): React.JSX.Element => ( } key={subtask.id} label={subtask.title} /> ) )} ) : null}
) : null} {tab === 'comments' ? ( {comments.length ? ( {comments.map( (comment, index): React.JSX.Element => ( ) )} ) : ( No comments yet )} { onCommentAdd?.(id, content); }} /> ) : null}
); } interface EditableDetailsProps { description: string; onUpdate?: (params: { title: string; description: string }) => void; title: string; } function EditableDetails({ description: initialDescription, onUpdate, title: initialTitle, }: EditableDetailsProps): React.JSX.Element { const [title, setTitle] = React.useState(''); const [description, setDescription] = React.useState(''); const [edit, setEdit] = React.useState(false); React.useEffect((): void => { setTitle(initialTitle); }, [initialTitle]); React.useEffect((): void => { setDescription(initialDescription); }, [initialDescription]); const handleSave = React.useCallback((): void => { if (!title) { return; } onUpdate?.({ title, description }); setEdit(false); }, [title, description, onUpdate]); if (edit) { return ( ): void => { setTitle(event.target.value); }} value={title} /> ): void => { if (!edit) { setEdit(true); } setDescription(event.target.value); }} placeholder="No description" value={description} /> ); } return ( {title} {description} { setEdit(true); }} > ); } interface CommentItemProps { connector?: boolean; comment: Comment; } function CommentItem({ comment, connector }: CommentItemProps): React.JSX.Element { const { author, content, createdAt, comments } = comment; const canReply = author.id !== 'USR-000'; // authenticated user return ( {connector ? ( ) : null}
{author.name} {createdAt ? ( {dayjs(createdAt).fromNow()} ) : null} {content} {canReply ? (
Reply
) : null}
{comments?.length ? ( {comments.map( (subComment, index): React.JSX.Element => ( ) )} ) : null}
); } interface CommentAddProps { onAdd?: (content: string) => void; } function CommentAdd({ onAdd }: CommentAddProps): React.JSX.Element { const [content, setContent] = React.useState(''); const handleAdd = React.useCallback((): void => { if (!content) { return; } onAdd?.(content); setContent(''); }, [content, onAdd]); return ( { handleAdd(); }} > } onChange={(event: React.ChangeEvent): void => { setContent(event.target.value); }} onKeyUp={(event: React.KeyboardEvent): void => { if (event.key === 'Enter') { handleAdd(); } }} placeholder="Add a comment..." startAdornment={ } sx={{ '--Input-paddingBlock': '12px' }} value={content} /> ); } function countDoneSubtasks(subtasks: Task['subtasks'] = []): number { return subtasks.reduce((acc, curr) => acc + (curr.done ? 1 : 0), 0); }