栽培管理アプリを作ってます④:基本機能の実装と農薬マスタ設計

前回の記事では、栽培管理アプリの設計について書きましたが、今回は実際に基本機能を実装していきました。

設計を形にしていく過程で、いくつか新しい課題や気づきもあったので、その辺りも含めて書いていきます。

目次

実装した基本機能

前回設計したフェーズ1の機能を、ほぼ実装できました。

1. データベースのマイグレーション

まず、前回設計したテーブル構造を実際のデータベースに反映しました。

主な変更:

  • cult_fieldsに作物情報のカラムを追加(current_crop_name, current_crop_started_at, current_crop_ended_at
  • cult_plantingsテーブルを削除(作物情報をcult_fieldsに統合)
  • cult_logsに詳細項目を追加(amount, work_minutes, memo, fertilizer_typeなど)
  • 作業マスタ関連のテーブルを作成(cult_work_types, cult_crop_work_settings, cult_pesticide_usage

すべてのテーブルにRLS(Row Level Security)ポリシーを設定して、ユーザーごとにデータを分離しています。

2. ハウス新規登録機能

ハウスを登録する画面を作りました。

登録画面(UIの改善は置いといて、登録できるかを優先)

機能:

  • 単棟ハウスと連棟ハウスの両方に対応
  • 幅・長さ・畝数を入力すると、畝が自動生成される
  • 連棟の場合は、2〜6棟まで選択可能

Next.js 16のApp Routerを使っているので、以下の3ファイル構成にしました:

  • page.tsx: Server Component(認証チェック、初期データ取得)
  • Client.tsx: Client Component(UIロジック)
  • actions.ts: Server Actions(データベース登録処理)

この構成、最初は「ファイルが増えて面倒だな」と思ったんですが、役割がクリアに分かれるので逆に管理しやすいです。

3. メイン画面(パターンA)の実装

畝を選択して作業記録するメイン画面を作りました。

前回、パターンAとパターンBの2つを試すと書きましたが、まずパターンA(フッター固定)から実装しました。

UIの特徴:

  • ハウスカードはアコーディオン式(閉じた状態/開いた状態を切り替え)
  • 畝をタップすると選択状態(緑色にハイライト)
  • 複数の畝を選択可能
  • 「全選択」「全解除」ボタンあり
  • 画面下部に作業アイコン(灌水、施肥、防除、その他)を固定

直近作業の表示:
ハウスを開いたときに、各畝の直近作業が表示されます。

直近作業:
畝1: 12/1
畝2: 12/1 12/1 12/1
畝3: 12/1 12/1
畝4: 12/1

各作業種類(灌水、施肥、防除など)ごとに、最新1件だけを表示しています。
これによって、「あれ、もう1週間も水やってないな」みたいなことが一目でわかるように。

履歴詳細の表示方法:
さらに履歴アイコンをタップすると、
画面下部に固定エリアを作って、タップしたら詳細が表示されるように。

畝を選択しているときは作業メニュー、履歴アイコンを選択しているときは履歴詳細。両方選択していれば、両方表示されます。

画像では、12/1の畝2を選択→施肥記録の詳細を表示。
肥料名、農薬名はまだ表示されてないので、後々実装予定。

4. 作業記録の保存

Server Actionを使って、作業記録をデータベースに保存する処理を実装しました。

処理内容:

  • 選択された畝ごとに、個別のレコードを作成
  • 使用量の計算(合計量入力 or 畝単位入力を選択可能)
  • 施肥の場合は、肥料タイプ(液肥/固形)も保存

登録が成功したら、「作業記録を登録しました!」というメッセージが1.5秒間表示されてから、モーダルが自動で閉じます。

最初はメッセージなしで閉じるだけだったんですが、「ちゃんと登録されたのか不安」という気持ちになったので、メッセージを追加しました。

5. 作業履歴一覧ページ

記録した作業を確認する一覧ページも作りました。

機能:

  • テーブル形式で表示(日付、ハウス、畝、作業、使用量、時間、メモ)
  • ハウスと作業種類でフィルタ可能
  • 作業時間の合計を表示

まだ細かい修正点はありますが、とりあえず動く状態にはなりました。

農薬マスタの設計で詰まった話

基本機能の実装が進んだので、次は「肥料・農薬マスタの管理」に取り掛かろうとしました。

最初の設計

肥料マスタ:

  • 肥料名
  • 種類(液肥/固形)
  • メモ欄

農薬マスタ:

  • 農薬名
  • 種類(殺菌/殺虫/展着剤/葉面散布)
  • 希釈倍率
  • 使用回数制限
  • 対象病害虫
  • メモ欄

問題1: 農薬のデータが多すぎる

農薬は種類が多すぎるため、手動で全部登録するのは面倒。

問題2: 同じ農薬でも、対象によって倍率が違う

さらに厄介なのが、「同じ農薬でも、対象病害虫によって希釈倍率が違う」ということ。

例えば:

ジクワット・パラコート液剤(除草剤)
- 野菜類(一年生雑草): 600〜1000倍
- アスパラガス(一年生雑草): 600〜1000倍
- アスパラガス(スギナ): 1000〜2000倍

同じ農薬なのに、スギナに対しては倍率が違うんです。

これ、マスタに「希釈倍率」をどう登録すればいいんでしょう?

解決策: 農薬マスタと詳細を分離

調べてみたら、農林水産省の「農薬登録情報提供システム」からCSVでデータをダウンロードできることがわかりました。

ただし、このCSVデータは「農薬名」と「対象病害虫」の組み合わせごとに1行になっています。

つまり、同じ農薬でも対象病害虫が違えば別の行になる。

そこで、テーブル設計を以下のように変更することにしました。

農薬マスタテーブル (cult_pesticides):

  • 農薬名
  • 種類(殺菌/殺虫/展着剤/葉面散布)
  • メモ欄

希釈倍率や使用回数は、マスタには入れない。

農薬適用詳細テーブル (cult_pesticide_applications):

  • 農薬ID(マスタへの参照)
  • 対象病害虫名
  • 使用方法
  • 使用時期
  • 希釈倍率(最小・最大)
  • 使用回数制限

こうすることで、同じ農薬でも対象病害虫ごとに異なる倍率を管理できます。

作業記録時のUI

防除作業を記録するときは、以下のような流れになります。

1. 畝を選択: 畝1, 畝2

2. 農薬を選択(マスタから):
   [▼ジクワット・パラコート液剤]

3. 対象病害虫を選択(詳細から):
   [ ] 野菜類(一年生雑草) - 600〜1000倍
   [ ] アスパラガス(一年生雑草) - 600〜1000倍
   [✓] アスパラガス(スギナ) - 1000〜2000倍

4. 希釈倍率が自動表示:
   推奨倍率: 1000〜2000倍
   実際の倍率を入力: [1500] 倍

5. 使用量を入力:
   原液: [100] ml
   → 散布量: 150L(自動計算)

「農薬を選ぶ」→「対象病害虫を選ぶ」→「倍率が自動で入る」という流れ。

これなら、間違った倍率で使用するミスを防げます。

CSVインポート機能

農水省のデータをCSVでダウンロードして、アプリにアップロードすれば、自動で整理してデータベースに登録する機能を作る予定です。

処理の流れ:

  1. CSVをアップロード
  2. 同じ農薬名でグループ化
  3. 農薬マスタに1件登録
  4. 対象病害虫ごとに詳細テーブルに登録

これで、手動で何十件も登録する手間が省けます。

今後の予定

次にやること

  1. 農薬・肥料マスタのマイグレーションSQL作成
  2. CSVインポート機能の実装
  3. 農薬マスタ管理画面(一覧・編集・削除)
  4. 防除作業記録UIの実装

後回しにしたこと

以下は、一旦後回しにすることにしました。

  • パターンBの実装: パターンAで十分使えそうなので、後回し
  • モバイル最適化: 基本的にはモバイルファーストで作っているので、細かい調整は後で
  • カレンダービュー: あったら便利だけど、なくても困らない
  • 天気自動取得: OpenWeatherMap APIを使えば実装できるけど、優先度は低い
  • 農場マップ機能: ドラッグ&ドロップで配置できたら楽しそうだけど、今は不要

将来対応したいこと

  • 肥料・農薬の複数混合使用(2〜3種類を混ぜて使う)
  • 複数ハウスにまたがる畝選択(使用頻度は低いけど、あると便利かも)
目次