【麹帳】管理画面作成

【麹帳】管理画面作成

投稿日: 2025年02月22日

学習振り返り
要約
  • 麹帳に管理画面を作成し、adminユーザーが調味料の公開申請を承認できるようにした。
  • react-tableを使用し、セル内での操作をコンポーネントに分けることでステート管理を簡素化した。
  • テーブル操作の経験を積むために繰り返し実装に取り組んでおり、次はユーザー向け機能の実装を考えている。

はじめに

最近余計なことばかりしていて遅くなっていますが、麹帳に管理画面を作成しました。
多分ごくごく一般的な管理画面だと思います。

以前やらせていただいた実務案件で管理画面を6ページ作ったのですが、まさに同じような実装をしました。
その案件には検索や日付絞込み、ページネーションもありましたが今回は不要なのでなしです。
内容と技術的な部分も少し書いてみます。

作成した画面

目的はadminユーザーが麹調味料の公開申請を承認するための画面です。
supabaseの画面でも可能ですが、 技術力向上を目的としているので今回はあえて作成しました!
また、今回はロールがメインなら親麴調味料はnull、サブなら親麹調味料必須なので、そこはプログラム上でしか制御できないかなと思いました。
作りながら気づいただけで、だから作ったわけではないですw

見た目

【麹帳】管理画面作成|ShiftBブログ

ライブラリ

私的に定番なreact-table使用しました。
セルの中で色んな操作が必要だったので、コンポーネント設計やステートをどこで管理するか、PUTリクエストをどうやって行うか悩みどころは多々ありました。

列定義

基本react-tableではcreateColumnHelperを使って定義する列データの定義ですべて済みました。列の定義を明確にできるおかげで、コンポーネントの分離がしやすくなると感じています。

例えば、親麹調味料なら

columnHelper.accessor("mainMaltArticle.id", {
      header: "親麹調味料",
      cell: info => {
        const role = info.row.original.maltRole;
        const articleId = info.row.original.id;
        if (role === MaltRole.MAIN) {
          return <div className="text-center">-</div>;
        }
        return <SelectMainMalt value={info.getValue()} articleId={articleId} />;
      },
    }),

こんな感じで、列の値の管理がコンポーネントに切り出すことでシンプルになりました。
というかそうしないとかなりステート管理が難しくすごくわかりにくくになると思います。

SelectMainMaltの中で更新処理も行いました。

import { useState } from "react";
import { useCategories } from "@/app/recipes/_hooks/useCategories";
import Select, { SingleValue } from "react-select";
import { api } from "@/app/_utils/api";
import { PutRequest } from "@/app/_types/Admin/malts/Parent/PutRequest";
import { useAdminMalts } from "../_hooks/useAdminMalts";
interface Props {
  value: string | null;
  articleId: string;
}
interface Option {
  value: string;
  label: string;
}

export const SelectMainMalt: React.FC<Props> = ({ value, articleId }) => {
  const [malt, setMalt] = useState(value);
  const { data, error } = useCategories();
  const { mutate } = useAdminMalts();
  const handleChange = async (selectedOption: SingleValue<Option>) => {
    if (!selectedOption) return;
    setMalt(selectedOption.value);
    try {
      await api.put<PutRequest, { message: string }>(
        `/api/admin/malts/${articleId}/parent`,
        { parentId: selectedOption.value }
      );
      mutate();
    } catch (e) {
      console.error(e);
      alert(e);
    }
  };
  if (!data) return <div>取得中...</div>;
  if (error) return <div>{error.message}</div>;
  const options: Option[] = data.maltArticles.map(article => ({
    value: article.id,
    label: article.title,
  }));

  const selectedOption = options.find(option => option.value === malt);
  return (
    <Select
      options={options}
      value={selectedOption}
      onChange={handleChange}
      classNames={{
        control: () => "!border-dark_brown !outline-none !text-dark_brown",
        placeholder: () => "!text-dark_brown",
        dropdownIndicator: () => "!text-dark_brown",
        indicatorSeparator: () => "!bg-dark_brown",
        singleValue: () => "!text-dark_brown",
      }}
    />
  );
};

おわりに

テーブル操作結構難しいので、もうある程度書けるもののパターン経験したくて何度もしつこく書いて習得しようと頑張っています。
私は頭あんまり良くないし一度じゃ無理なのでとにかく繰り返しです。

次またユーザー向け機能の実装したいと思います。

シェア!

Threads
user
吉本茜
山口在住/二児の母/育休中
Loading...
記事一覧に戻る
Threads
0