推しアイデア
一覧を見せずに1件だけ大きく出すこと。やる気が出ない日ほどタスク一覧は開きたくないので、選択肢を隠すこと自体を機能にしました。24時間で消える「供養」も、残り時間を予告してから消すことで、責められる感じにならないよう工夫しています。
一覧を見せずに1件だけ大きく出すこと。やる気が出ない日ほどタスク一覧は開きたくないので、選択肢を隠すこと自体を機能にしました。24時間で消える「供養」も、残り時間を予告してから消すことで、責められる感じにならないよう工夫しています。
やる気が出ない日ほどタスク管理アプリを開きたくなくなる、という自分の経験から作りました。全部並ぶ一覧を見ると量に圧倒されて閉じてしまう。漏れなく管理するより、今の自分でも着手できる1件を差し出すほうが助かると思ったのがきっかけです。
24時間で消す処理を定期実行(cron)に頼らず、一覧を読む直前に必ず一括更新する方式にしたこと。Renderの無料プランはサーバーが止まるので、常駐処理を前提にできません。読むときに最新化すれば、何度止まっても結果が変わりません。
■ 何をつくったか 「やる気サポーター」は、今の自分のやる気レベル(1〜5)を選ぶと、その日の自分でも着手できるタスクを1件だけ提案してくれるタスク管理アプリです。
タスクには「ヤバ度(緊急度)1〜5」と「必要やる気1〜5」を設定します。今のやる気以上の気力を要求するタスクは候補から外れ、残った中でヤバ度が一番高いものを1件だけ大きく表示します。気が乗らなければ「別のを出す」で次の候補に切り替えられます。
もうひとつの軸が「24時間供養」です。登録から24時間経った未完了タスクは自動的に一覧から消え、「供養リスト」に移ります。タスクが無限に積み上がってアプリを開くのが嫌になる状態を、仕組みで防いでいます。完了するとヤバ度×10ポイントと、ランダムなご褒美メッセージがもらえます。
■ どんなデータを保持しているか PostgreSQLに3つのテーブルを持っています。
・users … 名前、メール、パスワードのハッシュ、合計ポイント ・categories … 講義名などのカテゴリ(ユーザーごと) ・tasks … タイトル、ヤバ度、必要やる気、状態(active/completed/expired)、作成日時、期限
ポイントは3つです。
■ どうやって作ったか(構成) 講義で扱った3層構造にそっています。
・フロントエンド: React + Vite + Tailwind CSS + Framer Motion ・バックエンド: Node.js + Express + TypeScript + Prisma ・データベース: PostgreSQL ・認証: express-session(Cookieセッション) ・デプロイ: Render(render.yamlのBlueprintでDB・API・静的サイトを一括構成)
フロントは表示と操作だけを担当し、判断のロジックは持たせていません。「やる気で絞る」「ヤバ度順に並べる」「24時間で供養する」といったルールはすべてバックエンドのservices層に置き、controllersはHTTPの入口(入力検証とステータスコードの決定)に徹しています。この分け方のおかげで、ルールを変えたいときに触る場所が1か所で済みました。
■ 最もこだわったところ 一覧ではなく「1件だけ」を主役にしたことです。最初は一覧画面をメインにするつもりでしたが、それだと既存のアプリと同じで、「やる気が出ない日に開きたくない」という最初の問題が解決しません。トップではおすすめの1件だけを大きく出し、一覧はボタンを押さないと開かないようにしました。
もうひとつは、やる気レベルと画面の色を連動させたことです。やる気1(青)から5(赤)へ色相を等間隔に動かしていて、選んだレベルに応じてカードの枠線・ボタン・進捗バーの色が一斉に変わります。数値だけだと「3を選んだ」という事実しか残りませんが、画面全体の色が変わると「今日はこういう日だ」という感覚が残ります。
■ 学び 一番苦労したのはロジックではなくデプロイまわりでした。ローカルではフロントもAPIも localhost なのでCookieは何も考えなくても送られますが、本番はドメインが分かれるため SameSite=None と Secure を指定しないとログイン状態が保てません。CORSの許可オリジンもURLが1文字違うと弾かれます。「ローカルで動く」と「デプロイして動く」は別物だと実感しました。
また、無料プランのサーバーは止まるという制約から設計が決まった部分があります。供養処理をcronに頼らず「読む直前に必ず最新化する」方式にしたのも、セッションをメモリではなくDBに置いたのもそのためです。制約を知って初めて設計が決まる、というのが今回一番の学びでした。