import React, { useState, useRef } from 'react'; import { Plus, User, Trash, Paperclip, Clone, Share, Tags, Checkmark, CheckSquareOutline, At, Smile, } from 'shared/icons'; import Editor from 'rich-markdown-editor'; import dark from 'shared/utils/editorTheme'; import styled from 'styled-components'; import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; import dayjs from 'dayjs'; import ActivityMessage from './ActivityMessage'; import Task from 'shared/icons/Task'; import { ActivityItemHeader, ActivityItemTimestamp, ActivityItem, TaskDetailLabel, CommentContainer, MetaDetailContent, TaskDetailsAddLabelIcon, ActionButton, AssignUserIcon, AssignUserLabel, AssignUsersButton, AssignedUsersSection, ViewRawButton, DueDateTitle, Container, LeftSidebar, ContentContainer, LeftSidebarContent, LeftSidebarSection, SidebarTitle, SidebarButton, SidebarButtonText, MarkCompleteButton, HeaderContainer, HeaderLeft, HeaderInnerContainer, TaskDetailsTitleWrapper, TaskDetailsTitle, ExtraActionsSection, HeaderRight, HeaderActionIcon, EditorContainer, InnerContentContainer, DescriptionContainer, Labels, ChecklistSection, MemberList, TaskMember, TabBarSection, TabBarItem, CommentTextArea, CommentEditorContainer, CommentEditorActions, CommentEditorActionIcon, CommentEditorSaveButton, CommentProfile, CommentInnerWrapper, ActivitySection, TaskDetailsEditor, ActivityItemHeaderUser, ActivityItemHeaderTitle, ActivityItemHeaderTitleName, } from './Styles'; import Checklist, { ChecklistItem, ChecklistItems } from '../Checklist'; import onDragEnd from './onDragEnd'; import TaskAssignee from 'shared/components/TaskAssignee'; const ChecklistContainer = styled.div``; type TaskLabelProps = { label: TaskLabel; onClick: ($target: React.RefObject) => void; }; const TaskLabelItem: React.FC = ({ label, onClick }) => { const $label = useRef(null); return ( { onClick($label); }} ref={$label} color={label.projectLabel.labelColor.colorHex} > {label.projectLabel.name} ); }; type DetailsEditorProps = { description: string; onTaskDescriptionChange: (newDescription: string) => void; onCancel: () => void; }; type TaskDetailsProps = { task: Task; me?: TaskUser | null; onTaskNameChange: (task: Task, newName: string) => void; onTaskDescriptionChange: (task: Task, newDescription: string) => void; onDeleteTask: (task: Task) => void; onAddItem: (checklistID: string, name: string, position: number) => void; onDeleteItem: (checklistID: string, itemID: string) => void; onChangeItemName: (itemID: string, itemName: string) => void; onToggleTaskComplete: (task: Task) => void; onToggleChecklistItem: (itemID: string, complete: boolean) => void; onOpenAddMemberPopup: (task: Task, $targetRef: React.RefObject) => void; onOpenAddLabelPopup: (task: Task, $targetRef: React.RefObject) => void; onOpenDueDatePopop: (task: Task, $targetRef: React.RefObject) => void; onOpenAddChecklistPopup: (task: Task, $targetRef: React.RefObject) => void; onMemberProfile: ($targetRef: React.RefObject, memberID: string) => void; onChangeChecklistName: (checklistID: string, name: string) => void; onDeleteChecklist: ($target: React.RefObject, checklistID: string) => void; onCloseModal: () => void; onChecklistDrop: (checklist: TaskChecklist) => void; onChecklistItemDrop: (prevChecklistID: string, checklistID: string, checklistItem: TaskChecklistItem) => void; }; const TaskDetails: React.FC = ({ me, task, onDeleteChecklist, onTaskNameChange, onOpenAddChecklistPopup, onChangeChecklistName, onChecklistDrop, onChecklistItemDrop, onToggleTaskComplete, onTaskDescriptionChange, onChangeItemName, onDeleteItem, onDeleteTask, onCloseModal, onOpenAddMemberPopup, onOpenAddLabelPopup, onOpenDueDatePopop, onAddItem, onToggleChecklistItem, onMemberProfile, }) => { const [taskName, setTaskName] = useState(task.name); const [editTaskDescription, setEditTaskDescription] = useState(() => { if (task.description) { if (task.description.trim() === '' || task.description.trim() === '\\') { return true; } return false; } return true; }); const [saveTimeout, setSaveTimeout] = useState(null); const [showRaw, setShowRaw] = useState(false); const [showCommentActions, setShowCommentActions] = useState(false); const taskDescriptionRef = useRef(task.description ?? ''); const $noMemberBtn = useRef(null); const $addMemberBtn = useRef(null); const $dueDateBtn = useRef(null); const saveDescription = () => { onTaskDescriptionChange(task, taskDescriptionRef.current); }; return ( TASK GROUP {task.taskGroup.name} DUE DATE { onOpenDueDatePopop(task, $dueDateBtn); }} > {task.dueDate ? ( {dayjs(task.dueDate).format('MMM D [at] h:mm A')} ) : ( No due date )} MEMBERS {task.assigned && task.assigned.length !== 0 ? ( {task.assigned.map(m => ( { onMemberProfile($target, m.id); }} /> ))} { onOpenAddMemberPopup(task, $addMemberBtn); }} > ) : ( { onOpenAddMemberPopup(task, $noMemberBtn); }} > No members )} ACTIONS { onOpenAddLabelPopup(task, $target); }} icon={} > Labels { onOpenAddChecklistPopup(task, $target); }} icon={} > Checklist Cover { onToggleTaskComplete(task); }} > {task.complete ? 'Completed' : 'Mark complete'} onDeleteTask(task)}> { setTaskName(e.currentTarget.value); }} onBlur={() => { if (taskName !== task.name) { onTaskNameChange(task, taskName); } }} /> {task.labels.length !== 0 && ( {task.labels.map(label => { return ( { onOpenAddLabelPopup(task, $target); }} /> ); })} )} {showRaw ? ( ) : ( { if (!editTaskDescription) { setEditTaskDescription(true); } }} > { setSaveTimeout(() => { clearTimeout(saveTimeout); return setTimeout(saveDescription, 2000); }); const text = value(); taskDescriptionRef.current = text; }} /> )} setShowRaw(!showRaw)}>{showRaw ? 'Show editor' : 'Show raw'} onDragEnd(result, task, onChecklistDrop, onChecklistItemDrop)}> {dropProvided => ( {task.checklists && task.checklists .slice() .sort((a, b) => a.position - b.position) .map((checklist, idx) => ( {provided => ( onChangeChecklistName(checklist.id, newName)} onToggleItem={onToggleChecklistItem} onDeleteItem={onDeleteItem} onAddItem={n => { if (task.checklists) { let position = 65535; const [lastItem] = checklist.items .sort((a, b) => a.position - b.position) .slice(-1); if (lastItem) { position = lastItem.position * 2 + 1; } onAddItem(checklist.id, n, position); } }} onChangeItemName={onChangeItemName} > {checklistDrop => ( <> {checklist.items .slice() .sort((a, b) => a.position - b.position) .map((item, itemIdx) => ( {itemDrop => ( { onToggleChecklistItem(item.id, complete); }} /> )} ))} {checklistDrop.placeholder} )} )} ))} {dropProvided.placeholder} )} Activity {task.activity && task.activity.map(activity => ( {activity.causedBy.fullName} {dayjs(activity.createdAt).format('MMM D [at] h:mm A')} ))} {me && ( { onMemberProfile($target, me.id); }} /> { setShowCommentActions(true); }} onBlur={() => { setShowCommentActions(false); }} /> Save )} ); }; export default TaskDetails;