見出し画像

タスク一覧を「判断できる画面」へ。React初心者がDashboardに込めた3つの工夫

こんばんは、しゅんです。

仕事や育児の合間にReactやTypeScriptを学びながら、勤怠・タスク管理アプリ「Team Visualizer」を制作しています。

前回は、職場で感じた仕事量の偏りと、Team Visualizerを作り始めた理由について書きました。

今回からは、実際に制作した画面について紹介していきます。

最初に紹介するのは、アプリの中心となるDashboardです。

Dashboardは、ただ数字を並べる画面ではない

Team Visualizerには、タスクを登録・管理するTasksページがあります。
Tasksページは次回お見せします。

Tasksページでは、次のような情報を登録しています。

  • タスク名

  • 担当者

  • 期限

  • ステータス

  • 優先度

  • 進捗率

しかし、タスクが一覧で並んでいるだけでは、チーム全体の状況をすぐに把握することはできません。

例えば、

「現在、対応中の仕事はいくつあるのか」

「誰に仕事が集中しているのか」

「期限が近い仕事は何か」

を確認するには、一つひとつのタスクを見る必要があります。

そこで、Tasksページに登録されたデータを集計し、チームが次に何をするべきか判断できる画面としてDashboardを作りました。

Dashboardで特に意識したのは、次の3つです。

  1. チーム全体の状況をすぐに確認できる

  2. 仕事の偏りと期限を確認できる

  3. 最近追加されたタスクを確認できる


ダッシュボード

1.チーム全体の状況をカードで確認する

Dashboardで最も重要視したのが、画面上部にある4つのカードです。

  • 進行中タスク

  • 完了タスク

  • 期限超過タスク

  • 全体の進捗率

この4つを画面上部に配置した理由は、Dashboardを開いたときに、チーム全体の状況を短時間で確認できるようにするためです。


4つのカード

この数字をDashboard専用に入力しているわけではありません。

Tasksページで登録したタスクを読み込み、ステータスや期限、進捗率をもとに計算しています。

流れを簡単に表すと、次のようになります。

Tasksページでタスクを登録

localStorageに保存

Dashboardでタスクを読み込む

ステータスなどをもとに集計する

useStateでタスクを管理する

Dashboardでは、読み込んだタスクを管理するためにuseStateを使用しています。

const [tasks, setTasks] = useState<Task[]>([]);

それぞれの役割は次のとおりです。

  • tasks:Dashboardで使用するタスクの一覧

  • setTasks:タスクの一覧を更新する関数

  • []:最初はタスクが入っていない空の配列

useStateは、画面の中で変化する値をReactに覚えてもらうための機能です。

今回の場合は、Tasksページから読み込んだタスクをtasksとして覚えてもらいます。

setTasksによってタスクが更新されると、ReactがDashboardを自動的に再表示してくれます。

私の中では、useStateを「画面が覚えておくメモ」と考えると理解しやすくなりました。

useEffectでlocalStorageから読み込む

Tasksページで登録したタスクは、localStorageに保存しています。

Dashboardを表示したときにlocalStorageからタスクを読み込むため、useEffectを使用しています。

useEffect(() => {
  const savedTasks = localStorage.getItem("tasks");

  if (savedTasks) {
    setTasks(JSON.parse(savedTasks));
  }
}, []);

処理の流れは次のとおりです。

  1. Dashboardを表示する

  2. localStorageからタスクを取得する

  3. 保存されている文字列を配列に戻す

  4. setTasksでタスクを更新する

  5. Dashboardが再表示される

最後にある[]は、この処理をDashboardが最初に表示されたときに実行するための指定です。

}, []);

今回のコードでは、次のように役割を分けています。

  • useState:読み込んだタスクを覚える

  • useEffect:localStorageからタスクを読み込む

filterを使ってタスク数を計算する

進行中・完了・期限超過の件数は、tasksから条件に合うデータを絞り込んで計算します。

そこで使用するのがfilter()です。

進行中のタスク

const inProgressCount = tasks.filter(
  (task) => task.status === "進行中"
).length;

filter()でステータスが「進行中」のタスクだけを残し、最後のlengthで件数を取得しています。

完了したタスク

const completedCount = tasks.filter(
  (task) => task.status === "完了"
).length;

こちらは、ステータスが「完了」のタスクだけを数えています。

期限を過ぎたタスク

const overdueCount = tasks.filter((task) => {
  const isNotCompleted = task.status !== "完了";
  const isOverdue = new Date(task.deadline) < new Date();

  return isNotCompleted && isOverdue;
}).length;

期限超過では、次の2つを確認しています。

  • タスクが完了していない

  • 期限が今日より前になっている

この両方に当てはまるタスクを、期限超過として数えています。

全体の進捗率を計算する

全体の進捗率は、それぞれのタスクが持っているprogressを合計し、タスク数で割って求めます。

const totalProgress = tasks.reduce(
  (total, task) => total + task.progress,
  0
);

const overallProgress =
  tasks.length === 0
    ? 0
    : Math.round(totalProgress / tasks.length);

ここではreduce()を使っています。

reduce()は、配列に入っている複数の値を一つにまとめるときに使用します。

今回の場合は、すべてのタスクの進捗率を合計しています。

タスクが0件のときに割り算をすると正しく計算できないため、tasks.length === 0の場合は進捗率を0にしています。

Dashboardに表示している4つの数字は、別々に入力しているわけではありません。

Tasksページのデータから、自動的に計算して表示しています。

この仕組みによって、Tasksページのタスクが変われば、Dashboardの数字にも反映できるようになります。

2.仕事の偏りと近日中の期限を確認する

Dashboardの中央には、次の2つを並べています。

  • メンバーの負荷タスク

  • 近日中の期限

メンバーの負荷タスクと近日中の期限

メンバーの負荷タスク

メンバーの負荷タスクでは、担当者ごとのタスク数を表示しています。

数字だけでは仕事量の違いを比較しにくいため、タスク数に合わせてバーの長さが変わるようにしました。

担当者ごとのタスクは、filter()を使って絞り込みます。

const memberTasks = tasks.filter(
  (task) => task.assignee === member.name
);

例えば、担当者が「Aさん」の場合は、assigneeが「Aさん」になっているタスクだけを取り出します。

その件数をもとにバーの長さを変えることで、誰に仕事が集中しているのかを視覚的に確認できます。

この機能は、Team Visualizerを作ろうと思った理由である「仕事量の偏りを見える化すること」に直接つながっています。

近日中の期限

近日中の期限では、完了していないタスクを期限が近い順に並べています。

const upcomingTasks = [...tasks]
  .filter((task) => task.status !== "完了")
  .sort(
    (a, b) =>
      new Date(a.deadline).getTime() -
      new Date(b.deadline).getTime()
  )
  .slice(0, 4);

ここでは、3つの処理をつなげています。

  1. filter()で完了していないタスクを残す

  2. sort()で期限が近い順に並べる

  3. slice(0, 4)で最初の4件を取り出す

期限を表示することで、優先して対応する必要がある仕事に気づきやすくしています。

3.追加されたタスクの状況を確認する

Dashboardの下部には、Tasksページで追加したタスクを表示しています。

最新タスク状況

表示する主な情報は次のとおりです。

  • タスク

  • プロジェクト

  • 担当者

  • ステータス

  • 進捗率

現在は、tasks配列に保存されている順番を使い、先頭から4件を表示しています。

const displayedTasks = tasks.slice(0, 4);

slice(0, 4)は、タスク一覧の0番目から4件を取り出す処理です。

画面では、map()を使ってタスクを一つずつ表示します。

{displayedTasks.map((task) => (
  <tr key={task.id}>
    <td>{task.title}</td>
    <td>{task.project}</td>
    <td>{task.assignee}</td>
    <td>{task.status}</td>
    <td>{task.progress}%</td>
  </tr>
))}

map()は、配列に入っているデータを一つずつ取り出し、同じ形で画面に表示するときに使用します。

Tasksページで追加されたデータをDashboardでも使用することで、同じタスクをもう一度入力する必要がありません。

難しそうに見えても、処理を分ければ理解できる

Dashboard全体を見ると、多くの数字やカード、グラフが表示されているため、最初は難しい画面に見えます。

しかし、実際に行っていることを分けると、次のようになります。

  1. useEffectでタスクを読み込む

  2. useStateでタスクを管理する

  3. filter()で必要なタスクを絞り込む

  4. reduce()で進捗率を計算する

  5. sort()で期限順に並べる

  6. slice()で表示件数を絞る

  7. map()で画面に表示する

私も最初から、すべての処理を理解して作れたわけではありません。

一つの大きなDashboardを作ろうとするのではなく、最初はカードを一つ作り、次にタスク数を表示し、そのあとに期限やメンバーの負荷を追加しました。

小さな処理を一つずつ組み合わせたことで、少しずつ現在のDashboardになりました。

これからオリジナルアプリを作る方へ

オリジナルアプリを作るときは、最初から難しい機能を考える必要はないと思います。

まずは、

「自分が困っていることは何か」

「画面を見た人に何を伝えたいか」

を考えるところから始められます。

私の場合は、「チームの状況を短時間で確認したい」という目的から、Dashboardの4つのカードを作りました。

目的が決まると、必要なデータや機能も少しずつ見えてきます。

この記事を読んで、

「複雑そうな画面も、小さな処理の組み合わせで作れる」

「自分にもオリジナルアプリを作れそう」

と思ってもらえたらうれしいです。

最後に

今回は、Team VisualizerのDashboardに込めた3つの工夫を紹介しました。

特に重要視したのは、Tasksページのデータをもとに、チーム全体の状況を4つのカードで確認できるようにしたことです。

Dashboardは、ただ情報を並べる画面ではありません。

登録されたタスクを、チームが次に何をすべきか判断できる情報へ変える画面です。

Team Visualizerには、まだ未完成の部分も多くあります。今後も、制作を通して学んだことやエラーの解決方法を記録しながら、少しずつ改善を重ねて完成に近づけていきます。

最後まで読んでいただき、ありがとうございました。

いいなと思ったら応援しよう!