タスク整理アプリ

https://github.com/yuto-s-0822/task-organizer-app

GitHub

TypeScript

CSS

Node.js

HTML

毎日の重点タスクと習慣を整理するアプリ

Yuto Shimizu

推しアイデア

今日・明日の重点タスクを最大3件に絞り、通常タスク、期限、定期タスク、チェックイン記録まで一つの画面で管理できます。

作った背景

タスクを登録するだけでは優先順位が曖昧になりやすいため、「今日何をするか」をすぐ決められ、継続状況も確認できる仕組みを作りました。

推し技術

TypeScript、Express、Prisma、PostgreSQLで構築し、競合時もデータが崩れないトランザクション処理と、Renderへの自動デプロイに対応しました。

プロジェクト詳細

このプロジェクトでは、タスクを登録して終わるのではなく、毎日アプリを開き、その日に取り組む内容を決め、実行結果を振り返るところまでを一つの流れとして扱えるタスク整理アプリを制作しました。

画面上部には、毎日最初に確認する情報として、チェックイン状況、連続記録、達成率、今日と明日の重点タスクを配置しています。一方で、タスクの追加フォームや定期タスクの設定フォームは折りたたみ式にし、普段はタスクの確認や実行に集中できる構成にしました。

タスクの構成

タスクは、単独のタスクとして登録できるほか、親タスクと子タスクの2階層で整理できます。

例えば、「実験レポートを完成させる」という親タスクの下に、

  • グラフを作成する
  • 考察を書く
  • 文章を校閲する

といった具体的な作業を子タスクとして登録できます。

階層を増やしすぎると管理が複雑になるため、親子関係は2階層までに制限しました。また、親タスクを完了した場合には子タスクもまとめて完了しますが、子タスクをすべて完了しても親タスクは自動では完了しない仕様にしています。これは、子作業が終わった後に、利用者自身が全体の完了を確認できるようにするためです。

完了済みの親タスクに未完了の子タスクを追加したり、子タスクを未完了へ戻したりすると状態が矛盾するため、そのような操作はサーバー側で禁止しています。

期限と重要度

各タスクには、3段階の重要度と任意の期限を設定できます。

期限は単に日付を表示するだけでなく、

  • 本日が期限
  • 明日が期限
  • 期限超過

をそれぞれ異なるバッジで表示します。完了済みやアーカイブ済みのタスクには期限超過警告を表示しないようにし、現在対応が必要なタスクだけが目立つようにしました。

親タスクより子タスクの期限が遅い場合は、登録を禁止するのではなく警告を表示します。現実には親タスクの期限後に補足作業を行う場合もあるため、強制的に制限するのではなく、利用者が確認して判断できる仕様にしています。

重点タスク

通常のタスク一覧とは別に、今日と明日の重点タスクをそれぞれ最大3件まで設定できます。

重点タスクに設定できるのは、未完了かつ現在作業対象となるタスクです。親タスクに未完了の子タスクがある場合は、より具体的な作業である子タスクを候補として扱います。

候補は、前日に未完了だったタスク、期限、重要度などを考慮して表示します。候補を自動的に重点タスクへ設定するのではなく、最終的には利用者自身が選択する方式にしました。自動決定にすると利用者の予定や作業時間を十分に反映できないため、候補提示によって判断を補助する設計としています。

候補が多い場合に画面が長くなりすぎないよう、候補一覧は折りたたみ式にしています。すでに重点タスクが選ばれている場合は候補を閉じ、必要なときだけ追加候補を確認できます。

チェックインと達成率

1日の終わりには、チェックインを行います。

チェックイン時には、その日に存在する有効な作業タスクのうち、何件を完了したかを基に達成率を記録します。親タスクに未完了の子タスクがある場合は、親ではなく実際の作業単位となる子タスクを集計対象にしています。

記録した内容は、

  • 本日の達成率
  • 連続チェックイン日数
  • 過去30日間のチェックイン率
  • カレンダー
  • チェックイン履歴

に反映されます。

カレンダーでは、最初のチェックインより前の日付は空欄とし、記録を始めた後にチェックインしなかった日だけを「×」で表示します。アプリを使い始める前の日まで未達成として扱わないようにしました。

日付処理はすべて日本時間を基準にし、サーバーの配置場所や実行環境によって今日の日付がずれないようにしています。

定期タスク

毎日、毎週、毎月繰り返すタスクは、定期タスクのテンプレートとして登録できます。

テンプレートには、

  • タスク名
  • 重要度
  • 繰り返し方法
  • 開始日
  • 終了日

を設定します。

該当する日になると、通常のタスクとして自動生成されます。生成されたタスクは通常タスクと同じように、期限、完了、重点タスクなどの機能を利用できます。

同じ日のタスクが複数回生成されないよう、テンプレートと発生日の組み合わせを生成履歴として保存しています。そのため、生成されたタスクを後から完全削除した場合でも、同じ日付のタスクが再生成されることはありません。

月次タスクでは、31日を指定して31日が存在しない月になった場合、その月の最終日に生成します。ただし基準日は31日のまま保持するため、次に31日が存在する月では再び31日に生成されます。

定期タスクを一時停止している間の発生日は後から補充せず、再開後の発生日から生成する仕様にしました。

アーカイブ

完了したタスクや現在表示する必要がないタスクは、アーカイブできます。

親タスクをアーカイブすると、子タスクもまとめてアーカイブされます。一方で、子タスクだけを個別にアーカイブすることもできます。

アーカイブ済みタスクは通常一覧から分離し、必要な場合だけ表示します。親子関係を理解するために必要な親タスクは「参照表示」として表示しますが、その行から編集などの操作はできないようにしました。

完全削除はアーカイブ済みの末端タスクだけに限定し、誤操作によって親子関係や関連データが失われにくいようにしています。

データの整合性

このアプリでは、親タスクの完了と子タスクの更新、重点タスクの並び替え、定期タスクの生成など、複数のデータを同時に更新する処理があります。

同じタスクに対して複数の操作がほぼ同時に行われた場合でも、

  • 完了済みの親の下に未完了の子が残る
  • 同じ順位の重点タスクが複数できる
  • 同じ定期タスクが二重生成される

といった不整合が起きないよう、データベースのトランザクションを使用しています。

競合が起きた場合は一定回数だけ処理を再試行し、それでも解消しない場合は409エラーとして画面側に返します。画面側ではデータを再取得し、古い表示のまま操作を続けないようにしています。

また、通信中は操作対象ごとにボタンを無効化し、同じ操作が二重送信されることを防いでいます。

画面設計

デスクトップでは、タスク一覧を中心に表示し、タスク追加と定期タスク作成を右側へ配置しています。画面幅が狭くなると自動的に1列表示へ切り替わります。

主な対応画面幅として、

  • デスクトップ
  • タブレット
  • スマートフォン

を確認し、390px幅でも横スクロールが発生しないようにしました。

タスクの状態は色だけに依存せず、「完了」「期限超過」「アーカイブ済み」などの文字と記号を併用しています。キーボード操作時にはフォーカス位置を明確に表示し、カレンダーには今日の日付や各日の状態を説明する属性を設定しています。

開発と公開

開発時には、各機能を追加するたびにAPIテストと単体テストを実施しました。さらに、実際のブラウザ操作を自動化し、タスク作成、親子操作、重点タスク、定期タスク、チェックイン、画面幅ごとの表示を確認しました。

データベースは開発初期にはSQLiteを使用していましたが、公開環境でデータを永続的に保存するため、最終的にPostgreSQLへ移行しました。移行時には、既存の機能だけでなく、複数操作が同時に発生した場合の競合処理もPostgreSQL上で検証しました。

完成したアプリはGitHubで管理し、Render上にWebサービスとPostgreSQLデータベースを作成して公開しています。

Yuto Shimizu

@8e0d28178d0c8424