본문 바로가기
개인프로젝트/listPlz

listplz - DB에 변수 추가하기, taskProgressBar

by useSword 2024. 2. 6.

taskProgressBar를 만들어서 현재 진행률을 확인할 수 있도록 만들고 싶었다.
기존에 indexedDB에 있던 DailyTask에 isDone이라는 변수를 만들고 boolean 값을 가지도록 수정했다.
DB를 확인하여 isDone이 false이면 '완료하기'버튼이 나오고 true면 '완료됨'으로 변경되도록 만들었다.


taskProgressBar는 총 100으로 두고 DailyTask만큼 나눈뒤 DailyTask의 isDone의 true값 만큼 활성화되도록 만들었다.
추가적으로 디자인을 손 볼 예정이다.



그리고 taskProgressBar가 100까지 가득차면 아래의 화면이 나타나도록 만들었다.

 

 

몇몇 게임들의 ui가 이뻐서 todolist를 만들고 싶었다. 점점 어느정도 보이니 재미가 있다.

//관련된 코드

 

남은 시간

  const calculateRemainingTime = () => {
    const now = new Date();
    const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 24, 0, 0);
    const diffMs = endOfDay - now;
    const diffHrs = Math.floor((diffMs % 86400000) / 3600000); 
    const diffMins = Math.round(((diffMs % 86400000) % 3600000) / 60000);
    return `${diffHrs}시간 ${diffMins}분 남음`;
  };

 

 

taskProgressBar

// 완료하기 관련 로직
  const markTaskAsDone = async (taskId, e) => {
    e.stopPropagation();
    const updatedTasks = dailyTasks.map(task =>
      task.id === taskId ? { ...task, isDone: true } : task
    );
    setDailyTasks(updatedTasks);

    const taskToUpdate = updatedTasks.find(task => task.id === taskId);
    await updateData('DailyTasks', taskId, taskToUpdate);
  };



//남은 시간(하루기준)
  const calculateRemainingTime = () => {
    const now = new Date();
    const endOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 24, 0, 0);
    const diffMs = endOfDay - now;
    const diffHrs = Math.floor((diffMs % 86400000) / 3600000);
    const diffMins = Math.round(((diffMs % 86400000) % 3600000) / 60000);
    return `${diffHrs}시간 ${diffMins}분 남음`;
  };


//taskProgressBar
  const calculateProgress = (tasks) => {
    const totalTasks = tasks.length;
    const completedTasks = tasks.filter(task => task.isDone).length;
    return (completedTasks / totalTasks) * 100;
  };

//선택한 Task의 id를 확인
  const handleTaskClick = (taskId) => {
    setSelectedTaskId(selectedTaskId === taskId ? null : taskId);
  };

// Task삭제
  const handleDeleteTask = async (taskId) => {
    try {
      await deleteData('DailyTasks', taskId);
      setDailyTasks(dailyTasks.filter(task => task.id !== taskId));
      setSelectedTaskId(null);
    } catch (error) {
      console.error('태스크 삭제에 실패했습니다:', error);
    }
  };

// Task를 전부 완료했을 때의 이벤트
  const allTasksCompleted = dailyTasks.length > 0 && dailyTasks.every(task => task.isDone);


  return (
    <div className="todoListSection">
      <div className="headerSection">
        <h2>DAILY MISSION</h2>
        <h5>I</h5>
        <div className="missionRequirement">
          <div className="timeRemaining">{calculateRemainingTime()}</div>
        </div>
        <div className="taskProgressBar">
          <div className="progress" style={{ width: `${calculateProgress(dailyTasks)}%` }}></div>
        </div>

      </div>
      <div className="DailyTasksSection">
        <ul>
          {dailyTasks.map(task => (
            <li key={task.id} className={`taskItem ${selectedTaskId === task.id ? 'selected' : ''}`}>
              <div className={`taskContent ${selectedTaskId === task.id ? 'open' : ''}`} onClick={() => handleTaskClick(task.id)}>
                <span className="taskEmoticon">{selectedTaskId === task.id ? '▼' : '▶'}</span>
                <div className="taskMainContent">
                  <span className="taskTitle">{task.title}</span>
                </div>
                <button onClick={(e) => markTaskAsDone(task.id, e)} className="taskActionButton">
                  {task.isDone ? '완료됨' : '완료하기'}
                </button>
              </div>
              {selectedTaskId === task.id && (
                <div className={`taskDetails ${selectedTaskId === task.id ? 'open' : ''}`}>
                  <p className="taskDescription">{task.description}</p>
                  <button onClick={(e) => handleDeleteTask(task.id, e)} className="taskDeleteButton">삭제</button>
                </div>
              )}
            </li>
          ))}
        </ul>
        
        //다 완성하면 컴포넌트 MissionComplete를 불러옴.
        {/* {allTasksCompleted && <MissionComplete />} */}
      </div>
    </div>
  );

 

 

//scss

/* TodoListSection.scss */
.todoListSection {
  width: 80%;
  background: #FFF;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  padding: 20px;
}

.headerSection {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;

  h5 {
    position: absolute;
    font-size: 3em;
    color: #3b4be2fd;

  }
  h2 {
    padding-left: 4%;
    font-size: 1.5em;
    color: #000;
  }



  .missionRequirement {
    display: flex;
    align-items: center;
    font-size: 0.9em;
    color: #555;
    
    .timeRemaining {
      margin-left: 10px;
      color: #007bff;
    }
  }
  .taskProgressBar {
    top: 10%;
    position: absolute;
    width: 60%;
    background-color: #dcdce6;
    border-radius: 10px;
    height: 20px;

    .progress {
      background-color: #4e90ff;
      height: 100%;
      border-radius: 10px;
      width: 0;
      transition: width 0.3s ease;
    }
  }
}

.DailyTasksSection {
  .taskItem {
    padding-top: 10px;
    list-style: none;
    background-color: #f9f9f9;
    margin-bottom: 10px;
    padding: 15px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    transition: background-color 0.3s;

    &:hover {
      background-color: #f4f4f8;
    }

    &.selected {
      background-color: #e7e7ee;
    }

    &.done {
      background-color: #e0ffe0;
  
      .taskEmoticon {
        color: green;
      }
  
      .taskTitle {
        text-decoration: line-through;
      }
  
      .taskActionButton {
        background-color: #ccc;
        cursor: default;
      }
    }
    .taskContent {
      display: flex;
      justify-content: space-between;
      align-items: center;
      cursor: pointer;

      .taskMainContent {
        display: flex;
        align-items: center;
      }

      .taskEmoticon {
        font-size: 1.5rem;
        margin-right: 10px;
      }

      .taskTitle {
        font-weight: bold;
        margin-right: 20px;
      }


      .taskActionButton {
        background-color: #4e90ff;
        color: white;
        padding: 5px 20px;
        border: none;
        border-radius: 20px;
        cursor: pointer;
      }
    }

    .taskDetails {
      padding-top: 10px;
      border-top: 1px solid #dcdce6;
      justify-content: space-between;
      align-items: flex-start;
      display: none;
      &.open {
        display: block;
      }

      .taskDescription {
        flex-grow: 1;
        text-align: left;
        font-size: 0.9rem;
        color: #555;
        margin-right: 10px;
      }
    
      .taskDeleteButton {
        padding: 5px 20px;
        background-color: #ff4e4e;
        color: white;
        border: none;
        border-radius: 20px;
        cursor: pointer;
        align-self: flex-end;
      }
    }
  }
}

'개인프로젝트 > listPlz' 카테고리의 다른 글

listplz 데이터 상호작용  (1) 2024.02.02
indexedDB  (0) 2024.01.31
react-electron 환경설정(윈도우)  (0) 2024.01.23