From 067d12a1d5817ad608c3c8916dc5082c21b991a8 Mon Sep 17 00:00:00 2001
From: KIM_DEAHO <102588838+DHowor1d@users.noreply.github.com>
Date: Sat, 13 Sep 2025 20:51:19 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=B4=EC=8A=88=20=EC=83=81=EC=84=B8?=
=?UTF-8?q?=20=EC=88=98=EC=A0=95,=20=EB=8C=93=EA=B8=80=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/TaskDetail.jsx | 475 ------------------
src/components/common/task/TaskActions.jsx | 37 ++
src/components/common/task/TaskComments.jsx | 239 +++++++++
.../common/task/TaskDescription.jsx | 31 ++
src/components/common/task/TaskDetail.jsx | 124 +++++
src/components/common/task/TaskHeader.jsx | 211 ++++++++
src/components/common/task/TaskMetadata.jsx | 42 ++
src/components/common/task/TaskSchedule.jsx | 85 ++++
src/components/kanban/Kanban.jsx | 4 +-
src/components/kanban/KanbanCard.jsx | 4 +-
src/components/meeting/MeetingCreate.jsx | 6 +-
src/data/taskComments.js | 105 ++++
src/index.css | 4 +
13 files changed, 885 insertions(+), 482 deletions(-)
delete mode 100644 src/components/common/TaskDetail.jsx
create mode 100644 src/components/common/task/TaskActions.jsx
create mode 100644 src/components/common/task/TaskComments.jsx
create mode 100644 src/components/common/task/TaskDescription.jsx
create mode 100644 src/components/common/task/TaskDetail.jsx
create mode 100644 src/components/common/task/TaskHeader.jsx
create mode 100644 src/components/common/task/TaskMetadata.jsx
create mode 100644 src/components/common/task/TaskSchedule.jsx
create mode 100644 src/data/taskComments.js
diff --git a/src/components/common/TaskDetail.jsx b/src/components/common/TaskDetail.jsx
deleted file mode 100644
index 574ceef..0000000
--- a/src/components/common/TaskDetail.jsx
+++ /dev/null
@@ -1,475 +0,0 @@
-import { useState } from "react";
-import {
- ArrowBack,
- CalendarToday,
- Person,
- Flag,
- Assignment,
- Delete,
- Schedule,
- Edit,
- Save,
- Cancel,
-} from "@mui/icons-material";
-import BadgeComponent from "./BadgeComponent";
-import Dropdown from "./Dropdown";
-import { membersData } from "../../data/employees";
-
-function TaskDetail({ task, onBack, onUpdate, onDelete }) {
- const [isEditing, setIsEditing] = useState(false);
- const [editData, setEditData] = useState({
- ...task,
- memberId:
- task?.memberId || task?.memberName
- ? membersData.find((m) => m.name === task.memberName)?.id
- : null,
- });
-
- // 편집 핸들러
- const handleEdit = () => {
- setIsEditing(true);
- setEditData({
- ...task,
- memberId:
- task?.memberId || task?.memberName
- ? membersData.find((m) => m.name === task.memberName)?.id
- : null,
- });
- };
-
- const handleDelete = () => {
- if (window.confirm("이 작업을 삭제하시겠습니까?")) {
- if (onDelete) {
- onDelete(task.id);
- }
- }
- };
-
- const handleSave = () => {
- if (onUpdate) {
- onUpdate(task.id, editData);
- }
- setIsEditing(false);
- };
-
- const handleCancel = () => {
- setEditData({
- ...task,
- memberId:
- task?.memberId || task?.memberName
- ? membersData.find((m) => m.name === task.memberName)?.id
- : null,
- });
- setIsEditing(false);
- };
-
- // 드롭다운 옵션 데이터
- const statusOptions = [
- { value: "TODO", label: "할 일" },
- { value: "PROGRESS", label: "진행 중" },
- { value: "DONE", label: "완료" },
- ];
-
- const priorityOptions = [
- { value: "HIGH", label: "높음" },
- { value: "MEDIUM", label: "보통" },
- { value: "LOW", label: "낮음" },
- ];
-
- const memberOptions = membersData.map((member) => ({
- value: member.id,
- label: member.name,
- }));
- if (!task) {
- return (
-
-
-
작업 정보를 찾을 수 없습니다.
-
-
-
- );
- }
-
- // 날짜 포맷팅
- const formatDate = (dateString) => {
- const date = new Date(dateString);
- return date.toLocaleDateString("ko-KR", {
- year: "numeric",
- month: "long",
- day: "numeric",
- weekday: "long",
- });
- };
-
- // 우선순위 표시
- const getPriorityInfo = (priority) => {
- switch (priority?.toUpperCase()) {
- case "HIGH":
- return { label: "높음", color: "text-red-600 bg-red-50" };
- case "MEDIUM":
- return { label: "보통", color: "text-yellow-600 bg-yellow-50" };
- case "LOW":
- return { label: "낮음", color: "text-green-600 bg-green-50" };
- default:
- return { label: priority, color: "text-gray-600 bg-gray-50" };
- }
- };
-
- // 상태 표시
- const getStatusInfo = (status) => {
- switch (status?.toUpperCase()) {
- case "TODO":
- return { label: "할 일", color: "text-gray-600 bg-gray-100" };
- case "PROGRESS":
- return { label: "진행 중", color: "text-blue-600 bg-blue-100" };
- case "DONE":
- return { label: "완료", color: "text-green-600 bg-green-100" };
- default:
- return { label: status, color: "text-gray-600 bg-gray-100" };
- }
- };
-
- const priorityInfo = getPriorityInfo(task.priority);
- const statusInfo = getStatusInfo(task.status);
-
- // 메타데이터 구성
- const metadataItems = [
- { label: "작업 ID", value: `${task.id}` },
- { label: "프로젝트 ID", value: `${task.projectId}` },
- {
- label: "생성일",
- value: task.createdAt
- ? new Date(task.createdAt).toLocaleDateString("ko-KR")
- : "-",
- },
- {
- label: "수정일",
- value: task.updatedAt
- ? new Date(task.updatedAt).toLocaleDateString("ko-KR")
- : "-",
- },
- ];
-
- return (
-
-
- {/* 헤더 */}
-
-
-
-
-
- {isEditing ? (
-
- setEditData({ ...editData, title: e.target.value })
- }
- className="text-2xl font-semibold text-gray-900 bg-white border border-gray-300 rounded-lg px-3 py-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500"
- placeholder="작업 제목을 입력하세요"
- />
- ) : (
-
- {task.title}
-
- )}
-
- {/* 상태 */}
-
-
- {isEditing ? (
-
- setEditData({ ...editData, status: value })
- }
- width="w-32"
- className="text-sm"
- />
- ) : (
-
- {statusInfo.label}
-
- )}
-
-
- {/* 우선순위 */}
-
-
- {isEditing ? (
-
- setEditData({ ...editData, priority: value })
- }
- width="w-32"
- className="text-sm"
- />
- ) : (
-
- {priorityInfo.label}
-
- )}
-
-
- {/* 담당자 */}
-
-
- {isEditing ? (
-
{
- const selectedMember = membersData.find(
- (m) => m.id === value
- );
- setEditData({
- ...editData,
- memberId: value,
- memberName: selectedMember?.name || "",
- });
- }}
- width="w-36"
- className="text-sm"
- placeholder="담당자 선택"
- />
- ) : (
-
- {task.memberName || "담당자 미지정"}
-
- )}
-
-
- {/* 마감일 */}
-
-
- {isEditing ? (
-
- setEditData({ ...editData, endDate: e.target.value })
- }
- className="text-sm text-gray-700 border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500"
- />
- ) : (
- task.endDate && (
-
- {formatDate(task.endDate)}
-
- )
- )}
-
-
-
- {/* 태그 */}
- {task.tags && task.tags.length > 0 && (
-
-
태그:
-
- {task.tags.map((tag, index) => (
-
- {typeof tag === "object" ? tag.name : tag}
-
- ))}
-
-
- )}
-
-
-
- {/* 편집 버튼 영역 */}
-
- {isEditing ? (
- <>
-
-
- >
- ) : (
-
-
-
-
- )}
-
-
-
-
- {/* 메인 컨텐츠 */}
-
-
- {/* 작업 설명 */}
-
-
- {/* 메타 정보 */}
-
-
작업 정보
-
- {metadataItems.map((item, index) => (
-
-
- {item.label}
-
-
- {item.value}
-
-
- ))}
-
-
-
- {/* 일정 정보 */}
-
-
-
- {/* 우측 영역 - 댓글 공간 (추후 구현) */}
-
-
-
-
- );
-}
-
-export default TaskDetail;
diff --git a/src/components/common/task/TaskActions.jsx b/src/components/common/task/TaskActions.jsx
new file mode 100644
index 0000000..92cbf44
--- /dev/null
+++ b/src/components/common/task/TaskActions.jsx
@@ -0,0 +1,37 @@
+import { Delete, Edit } from "@mui/icons-material";
+
+function TaskActions({
+ isEditing,
+ onEdit,
+ onDelete,
+ onSave,
+ onCancel
+}) {
+ return (
+ <>
+ {isEditing ? (
+ <>
+
+
+ >
+ ) : (
+
+
+
+
+ )}
+ >
+ );
+}
+
+export default TaskActions;
\ No newline at end of file
diff --git a/src/components/common/task/TaskComments.jsx b/src/components/common/task/TaskComments.jsx
new file mode 100644
index 0000000..ea76931
--- /dev/null
+++ b/src/components/common/task/TaskComments.jsx
@@ -0,0 +1,239 @@
+import React, { useState, useEffect } from 'react';
+import { getCommentsByTaskId, addComment, updateComment, deleteComment } from '../../../data/taskComments';
+import { membersData } from '../../../data/employees';
+
+function TaskComments({ taskId = '4-1' }) {
+ const [comments, setComments] = useState([]);
+ const [newComment, setNewComment] = useState('');
+ const [editingCommentId, setEditingCommentId] = useState(null);
+ const [editContent, setEditContent] = useState('');
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ // 현재 사용자 정보 (실제 구현시 AuthContext에서 가져옴)
+ const currentUser = membersData[0]; // 첫 번째 사용자를 현재 사용자로 가정
+
+ useEffect(() => {
+ // 댓글 목록 로드
+ const taskComments = getCommentsByTaskId(taskId);
+ setComments(taskComments);
+ }, [taskId]);
+
+ // 새 댓글 작성
+ const handleSubmitComment = async (e) => {
+ e.preventDefault();
+ if (!newComment.trim() || isSubmitting) return;
+
+ setIsSubmitting(true);
+ try {
+ const comment = addComment(
+ taskId,
+ currentUser.id,
+ currentUser.name,
+ currentUser.position,
+ newComment.trim()
+ );
+
+ setComments(prev => [...prev, comment]);
+ setNewComment('');
+ } catch (error) {
+ console.error('댓글 작성 실패:', error);
+ } finally {
+ setIsSubmitting(false);
+ }
+ };
+
+ // 댓글 수정 시작
+ const handleStartEdit = (comment) => {
+ setEditingCommentId(comment.id);
+ setEditContent(comment.content);
+ };
+
+ // 댓글 수정 취소
+ const handleCancelEdit = () => {
+ setEditingCommentId(null);
+ setEditContent('');
+ };
+
+ // 댓글 수정 저장
+ const handleSaveEdit = async (commentId) => {
+ if (!editContent.trim()) return;
+
+ try {
+ const updatedComment = updateComment(commentId, editContent.trim());
+ if (updatedComment) {
+ setComments(prev =>
+ prev.map(comment =>
+ comment.id === commentId ? updatedComment : comment
+ )
+ );
+ setEditingCommentId(null);
+ setEditContent('');
+ }
+ } catch (error) {
+ console.error('댓글 수정 실패:', error);
+ }
+ };
+
+ // 댓글 삭제
+ const handleDeleteComment = async (commentId) => {
+ if (!window.confirm('댓글을 삭제하시겠습니까?')) return;
+
+ try {
+ const deletedComment = deleteComment(commentId);
+ if (deletedComment) {
+ setComments(prev => prev.filter(comment => comment.id !== commentId));
+ }
+ } catch (error) {
+ console.error('댓글 삭제 실패:', error);
+ }
+ };
+
+ // 시간 포맷팅
+ const formatDate = (dateString) => {
+ const date = new Date(dateString);
+ const now = new Date();
+ const diffInHours = Math.floor((now - date) / (1000 * 60 * 60));
+
+ if (diffInHours < 1) {
+ return '방금 전';
+ } else if (diffInHours < 24) {
+ return `${diffInHours}시간 전`;
+ } else {
+ return date.toLocaleDateString('ko-KR', {
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+ }
+ };
+
+ return (
+
+
+
+ 댓글 ({comments.length})
+
+
+ {/* 댓글 목록 */}
+
+ {comments.length === 0 ? (
+
+ ) : (
+ comments.map((comment) => (
+
+
+
+
+
+ {comment.authorName[0]}
+
+
+
+
+ {comment.authorName}
+
+
+ {comment.authorPosition}
+
+
+
+
+ {comment.authorId === currentUser.id && (
+
+
+
+
+ )}
+
+
+ {editingCommentId === comment.id ? (
+
+ ) : (
+
+
+ {comment.content}
+
+
+ {formatDate(comment.createdAt)}
+ {comment.isEdited && (
+ (수정됨)
+ )}
+
+
+ )}
+
+ ))
+ )}
+
+
+ {/* 새 댓글 작성 폼 */}
+
+
+
+ );
+}
+
+export default TaskComments;
\ No newline at end of file
diff --git a/src/components/common/task/TaskDescription.jsx b/src/components/common/task/TaskDescription.jsx
new file mode 100644
index 0000000..bb7a2b2
--- /dev/null
+++ b/src/components/common/task/TaskDescription.jsx
@@ -0,0 +1,31 @@
+function TaskDescription({
+ task,
+ isEditing,
+ editData,
+ setEditData
+}) {
+ return (
+
+
작업 설명
+
+ {isEditing ? (
+
+
+ );
+}
+
+export default TaskDescription;
\ No newline at end of file
diff --git a/src/components/common/task/TaskDetail.jsx b/src/components/common/task/TaskDetail.jsx
new file mode 100644
index 0000000..dd7a420
--- /dev/null
+++ b/src/components/common/task/TaskDetail.jsx
@@ -0,0 +1,124 @@
+import { useState } from "react";
+import TaskHeader from "./TaskHeader";
+import TaskActions from "./TaskActions";
+import TaskDescription from "./TaskDescription";
+import TaskMetadata from "./TaskMetadata";
+import TaskSchedule from "./TaskSchedule";
+import TaskComments from "./TaskComments";
+import { membersData } from "../../../data/employees";
+
+function TaskDetail({ task, onBack, onUpdate, onDelete }) {
+ const [isEditing, setIsEditing] = useState(false);
+ const [editData, setEditData] = useState({
+ ...task,
+ memberId:
+ task?.memberId || task?.memberName
+ ? membersData.find((m) => m.name === task.memberName)?.id
+ : null,
+ });
+
+ // 편집 핸들러
+ const handleEdit = () => {
+ setIsEditing(true);
+ setEditData({
+ ...task,
+ memberId:
+ task?.memberId || task?.memberName
+ ? membersData.find((m) => m.name === task.memberName)?.id
+ : null,
+ });
+ };
+
+ const handleDelete = () => {
+ if (window.confirm("이 작업을 삭제하시겠습니까?")) {
+ if (onDelete) {
+ onDelete(task.id);
+ }
+ }
+ };
+
+ const handleSave = () => {
+ if (onUpdate) {
+ onUpdate(task.id, editData);
+ }
+ setIsEditing(false);
+ };
+
+ const handleCancel = () => {
+ setEditData({
+ ...task,
+ memberId:
+ task?.memberId || task?.memberName
+ ? membersData.find((m) => m.name === task.memberName)?.id
+ : null,
+ });
+ setIsEditing(false);
+ };
+ if (!task) {
+ return (
+
+
+
작업 정보를 찾을 수 없습니다.
+
+
+
+ );
+ }
+
+ return (
+
+
+ {/* 헤더 */}
+
+
+
+
+ {/* 메인 컨텐츠 */}
+
+
+ {/* 작업 설명 */}
+
+
+ {/* 메타 정보*/}
+
+
+ {/* 일정*/}
+
+
+
+ {/* 댓글 */}
+
+
+
+
+ );
+}
+
+export default TaskDetail;
diff --git a/src/components/common/task/TaskHeader.jsx b/src/components/common/task/TaskHeader.jsx
new file mode 100644
index 0000000..7d6921a
--- /dev/null
+++ b/src/components/common/task/TaskHeader.jsx
@@ -0,0 +1,211 @@
+import { ArrowBack, Person, Flag, Assignment, Schedule } from "@mui/icons-material";
+import Dropdown from "../Dropdown";
+import { membersData } from "../../../data/employees";
+
+function TaskHeader({
+ task,
+ isEditing,
+ editData,
+ setEditData,
+ onBack,
+ children
+}) {
+ // 드롭다운 옵션 데이터
+ const statusOptions = [
+ { value: "TODO", label: "할 일" },
+ { value: "PROGRESS", label: "진행 중" },
+ { value: "DONE", label: "완료" },
+ ];
+
+ const priorityOptions = [
+ { value: "HIGH", label: "높음" },
+ { value: "MEDIUM", label: "보통" },
+ { value: "LOW", label: "낮음" },
+ ];
+
+ const memberOptions = membersData.map((member) => ({
+ value: member.id,
+ label: member.name,
+ }));
+
+ // 날짜 포맷팅
+ const formatDate = (dateString) => {
+ const date = new Date(dateString);
+ return date.toLocaleDateString("ko-KR", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ weekday: "long",
+ });
+ };
+
+ // 우선순위 표시
+ const getPriorityInfo = (priority) => {
+ switch (priority?.toUpperCase()) {
+ case "HIGH":
+ return { label: "높음", color: "text-red-600 bg-red-50" };
+ case "MEDIUM":
+ return { label: "보통", color: "text-yellow-600 bg-yellow-50" };
+ case "LOW":
+ return { label: "낮음", color: "text-green-600 bg-green-50" };
+ default:
+ return { label: priority, color: "text-gray-600 bg-gray-50" };
+ }
+ };
+
+ // 상태 표시
+ const getStatusInfo = (status) => {
+ switch (status?.toUpperCase()) {
+ case "TODO":
+ return { label: "할 일", color: "text-gray-600 bg-gray-100" };
+ case "PROGRESS":
+ return { label: "진행 중", color: "text-blue-600 bg-blue-100" };
+ case "DONE":
+ return { label: "완료", color: "text-green-600 bg-green-100" };
+ default:
+ return { label: status, color: "text-gray-600 bg-gray-100" };
+ }
+ };
+
+ const priorityInfo = getPriorityInfo(task.priority);
+ const statusInfo = getStatusInfo(task.status);
+
+ return (
+
+
+
+
+
+ {isEditing ? (
+
+ setEditData({ ...editData, title: e.target.value })
+ }
+ className="text-2xl font-semibold focus:outline-none hover:border-gray-400 text-gray-900 bg-white border border-gray-300 rounded-lg px-3 py-2 w-full"
+ placeholder="작업 제목을 입력하세요"
+ />
+ ) : (
+
+ {task.title}
+
+ )}
+
+ {/* 상태 */}
+
+
+ {isEditing ? (
+
+ setEditData({ ...editData, status: value })
+ }
+ width="w-32"
+ className="text-sm"
+ />
+ ) : (
+
+ {statusInfo.label}
+
+ )}
+
+
+ {/* 우선순위 */}
+
+
+ {isEditing ? (
+
+ setEditData({ ...editData, priority: value })
+ }
+ width="w-32"
+ className="text-sm"
+ />
+ ) : (
+
+ {priorityInfo.label}
+
+ )}
+
+
+ {/* 담당자 */}
+
+
+ {isEditing ? (
+
{
+ const selectedMember = membersData.find(
+ (m) => m.id === value
+ );
+ setEditData({
+ ...editData,
+ memberId: value,
+ memberName: selectedMember?.name || "",
+ });
+ }}
+ width="w-36"
+ className="text-sm"
+ placeholder="담당자 선택"
+ />
+ ) : (
+
+ {task.memberName || "담당자 미지정"}
+
+ )}
+
+
+ {/* 마감일 */}
+
+
+ {isEditing ? (
+
+ setEditData({ ...editData, endDate: e.target.value })
+ }
+ className="text-sm text-gray-700 border border-gray-300 rounded px-2 py-2 focus:outline-none"
+ />
+ ) : (
+ task.endDate && (
+
+ {formatDate(task.endDate)}
+
+ )
+ )}
+
+
+
+
+
+ {/* 편집*/}
+
+ {children}
+
+
+
+ );
+}
+
+export default TaskHeader;
\ No newline at end of file
diff --git a/src/components/common/task/TaskMetadata.jsx b/src/components/common/task/TaskMetadata.jsx
new file mode 100644
index 0000000..e9db054
--- /dev/null
+++ b/src/components/common/task/TaskMetadata.jsx
@@ -0,0 +1,42 @@
+function TaskMetadata({ task }) {
+ // 메타데이터 구성
+ const metadataItems = [
+ { label: "작업 ID", value: `${task.id}` },
+ { label: "프로젝트 ID", value: `${task.projectId}` },
+ {
+ label: "생성일",
+ value: task.createdAt
+ ? new Date(task.createdAt).toLocaleDateString("ko-KR")
+ : "-",
+ },
+ {
+ label: "수정일",
+ value: task.updatedAt
+ ? new Date(task.updatedAt).toLocaleDateString("ko-KR")
+ : "-",
+ },
+ ];
+
+ return (
+
+
작업 정보
+
+ {metadataItems.map((item, index) => (
+
+
+ {item.label}
+
+
+ {item.value}
+
+
+ ))}
+
+
+ );
+}
+
+export default TaskMetadata;
\ No newline at end of file
diff --git a/src/components/common/task/TaskSchedule.jsx b/src/components/common/task/TaskSchedule.jsx
new file mode 100644
index 0000000..2df6c5a
--- /dev/null
+++ b/src/components/common/task/TaskSchedule.jsx
@@ -0,0 +1,85 @@
+function TaskSchedule({
+ task,
+ isEditing,
+ editData,
+ setEditData
+}) {
+ // 날짜 포맷팅
+ const formatDate = (dateString) => {
+ const date = new Date(dateString);
+ return date.toLocaleDateString("ko-KR", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ weekday: "long",
+ });
+ };
+
+ return (
+
+ );
+}
+
+export default TaskSchedule;
\ No newline at end of file
diff --git a/src/components/kanban/Kanban.jsx b/src/components/kanban/Kanban.jsx
index c2b06a3..233cac3 100644
--- a/src/components/kanban/Kanban.jsx
+++ b/src/components/kanban/Kanban.jsx
@@ -3,7 +3,7 @@ import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'
import KanbanColumn from './KanbanColumn'
import KanbanCard from './KanbanCard'
import KanbanHeader from './KanbanHeader'
-import TaskDetail from '../common/TaskDetail'
+import TaskDetail from '../common/task/TaskDetail'
import { Add } from '@mui/icons-material'
function Kanban({ projectId }) {
@@ -43,7 +43,7 @@ function Kanban({ projectId }) {
id: `${projectId}-3`,
title: 'API 개발',
description: '사용자 인증 API 개발 진행 중',
- assignee: '이백엔드',
+ assignee: '이백퍼',
priority: 'high',
dueDate: '2024-03-18'
},
diff --git a/src/components/kanban/KanbanCard.jsx b/src/components/kanban/KanbanCard.jsx
index 65ba4a2..e89d69b 100644
--- a/src/components/kanban/KanbanCard.jsx
+++ b/src/components/kanban/KanbanCard.jsx
@@ -135,7 +135,7 @@ function KanbanCard({ item, index, onUpdate, onDelete, onCardClick }) {
{/* 태그 */}
- {/* {item.tags && item.tags.length > 0 && (
+ {item.tags && item.tags.length > 0 && (
{item.tags.map((tag, tagIndex) => (
))}
- )} */}
+ )}
{/* 하단 정보 */}
diff --git a/src/components/meeting/MeetingCreate.jsx b/src/components/meeting/MeetingCreate.jsx
index e947c25..4b2063f 100644
--- a/src/components/meeting/MeetingCreate.jsx
+++ b/src/components/meeting/MeetingCreate.jsx
@@ -211,7 +211,7 @@ function MeetingCreate({ onBack, onSave, meeting = null, isEditing = false }) {
@@ -220,8 +220,8 @@ function MeetingCreate({ onBack, onSave, meeting = null, isEditing = false }) {
disabled={isLoading}
className={`px-4 py-2 rounded-lg transition-colors ${
isLoading
- ? 'bg-gray-400 text-white cursor-not-allowed'
- : 'bg-green-400 text-white hover:bg-green-500'
+ ? 'cancelBtn'
+ : 'createBtn'
}`}
>
{isLoading ? '처리중...' : isEditing ? '수정' : '저장'}
diff --git a/src/data/taskComments.js b/src/data/taskComments.js
new file mode 100644
index 0000000..df9011a
--- /dev/null
+++ b/src/data/taskComments.js
@@ -0,0 +1,105 @@
+// 작업 댓글 더미 데이터
+export const taskCommentsData = [
+ {
+ id: 1,
+ taskId: '4-1', // 작업 ID와 연결
+ authorId: 1,
+ authorName: '박서호',
+ authorPosition: '사원',
+ content: '새로운 대시보드 UI 디자인 검토했습니다. 전반적으로 깔끔하고 사용자 친화적인 것 같습니다.',
+ createdAt: '2024-03-15T09:30:00',
+ updatedAt: '2024-03-15T09:30:00',
+ isEdited: false
+ },
+ {
+ id: 2,
+ taskId: '4-1',
+ authorId: 2,
+ authorName: '이지민',
+ authorPosition: '대리',
+ content: '컬러 팔레트 부분에서 접근성을 고려한 색상 대비 검토가 필요할 것 같습니다. WCAG 가이드라인을 참고해주세요.',
+ createdAt: '2024-03-15T10:15:00',
+ updatedAt: '2024-03-15T10:20:00',
+ isEdited: true
+ },
+ {
+ id: 3,
+ taskId: '4-1',
+ authorId: 3,
+ authorName: '최우식',
+ authorPosition: '주임',
+ content: '좋은 지적입니다. 다음 주 회의에서 접근성 가이드라인에 대해 논의해보겠습니다.',
+ createdAt: '2024-03-15T11:00:00',
+ updatedAt: '2024-03-15T11:00:00',
+ isEdited: false
+ },
+ {
+ id: 4,
+ taskId: '4-1',
+ authorId: 4,
+ authorName: '이수민',
+ authorPosition: '사원',
+ content: '대시보드의 반응형 디자인도 확인이 필요합니다. 모바일에서의 표시 방식은 어떻게 계획하고 계신가요?',
+ createdAt: '2024-03-15T14:30:00',
+ updatedAt: '2024-03-15T14:30:00',
+ isEdited: false
+ },
+ {
+ id: 5,
+ taskId: '4-1',
+ authorId: 1,
+ authorName: '박서호',
+ authorPosition: '사원',
+ content: '모바일 버전은 별도로 디자인하고 있습니다. 이번 주 내로 프로토타입을 공유드리겠습니다.',
+ createdAt: '2024-03-15T15:45:00',
+ updatedAt: '2024-03-15T15:45:00',
+ isEdited: false
+ }
+];
+
+// 작업 ID별 댓글 필터링 함수
+export const getCommentsByTaskId = (taskId) => {
+ return taskCommentsData.filter(comment => comment.taskId === taskId);
+};
+
+// 새 댓글 추가 함수 (실제 구현시 API 호출로 대체)
+export const addComment = (taskId, authorId, authorName, authorPosition, content) => {
+ const newComment = {
+ id: taskCommentsData.length + 1,
+ taskId,
+ authorId,
+ authorName,
+ authorPosition,
+ content,
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ isEdited: false
+ };
+
+ taskCommentsData.push(newComment);
+ return newComment;
+};
+
+// 댓글 수정 함수
+export const updateComment = (commentId, newContent) => {
+ const commentIndex = taskCommentsData.findIndex(comment => comment.id === commentId);
+ if (commentIndex !== -1) {
+ taskCommentsData[commentIndex] = {
+ ...taskCommentsData[commentIndex],
+ content: newContent,
+ updatedAt: new Date().toISOString(),
+ isEdited: true
+ };
+ return taskCommentsData[commentIndex];
+ }
+ return null;
+};
+
+// 댓글 삭제 함수
+export const deleteComment = (commentId) => {
+ const commentIndex = taskCommentsData.findIndex(comment => comment.id === commentId);
+ if (commentIndex !== -1) {
+ return taskCommentsData.splice(commentIndex, 1)[0];
+ }
+ return null;
+};
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index e358fa5..904ba4e 100644
--- a/src/index.css
+++ b/src/index.css
@@ -41,6 +41,10 @@ body {
@apply px-4 py-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors flex items-center gap-2
}
+.cancelBtn {
+ @apply px-4 py-2 text-gray-800 hover:bg-gray-100 rounded-lg transition-colors disabled:opacity-50
+}
+
/* 스크롤바 숨기기 */
.hide-scrollbar {
/* IE, Edge 및 Firefox */