見出し画像

ユーザー管理をさらに作っていく

現状

こんな感じになっている。ここで「最終ログイン」のカラムがあるので、これをクリックしてsortしたくなるだろう、普通。なのでこの機能を作っていく。

最終ログインのソート

clickableな見た目にする

とりあえず「ヘッダ列を押せそうな見た目」に変更する。
現状は

<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
  {t('Last Logined At')}
</th>

これを

<th scope="col" className="px-6 py-3 text-left text-xs font-medium
// text-gray-500 uppercase tracking-wider
   text-blue-600 uppercase underline hover:text-blue-800 cursor-pointer hover:bg-gray-100

みたいに変更していく。日本語になってるならuppercaseは必要ないかもね。

さらにDirectionを表すアイコンを持ってくる。ここではVscArrowUpVscArrowDownを使う

import {
  VscVerifiedFilled,
  VscUnverified,
  VscChromeClose,
  VscArrowUp,
  VscArrowDown,
} from 'react-icons/vsc';

ID列から

最終ログインのソートもあるんだけど、まずdefaultではIDソートがかかっているので、これを最初に処理していこう。まあ今ID列が無いので、適当に作る。

<th scope="col"
className="px-6 py-3 text-left text-xs font-medium text-blue-600 underline hover:text-blue-800 cursor-pointer hover:bg-gray-100" >
    ID <span className="inline-block ml-2"><VscArrowDown/></span>
</th>
// ...
                <tbody className="bg-white divide-y divide-gray-200">
                  {users.map((user) => (
                    <tr key={user.id}>
                      <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{user.id}</td>


ID列default降順みたいなノリになった

クリックしてID(他)のdirectionをトグルできるように

<th scope="col"
  className="px-6 py-3 text-left text-xs font-medium text-blue-600 underline hover:text-blue-800 cursor-pointer hover:bg-gray-100"
  onClick={() => handleSort('id')}
>
    ID <span className="inline-block ml-2"><VscArrowDown/></span>
</th>

onClickにてhandleSort を指定したので、空の関数を書いておく

  const handleSort = (key) => {}

これを実装していくぞい

とりあえずアイコンの見た目だけ変更してみる

この手のトグル系はuseStateするのが鉄板である。

import { useState } from 'react';

(inertia.jsの場合Reactはimportしなくてもok)

ではこれを利用して、setなんとかを書いていこう。ここではsortConfigという名前にしてみる

const [sortConfig, setSortConfig] = useState({key: "id", direction: "desc"});

本来useState()のdefaultは、とりあえずidのdescにベタ打ちしている。これでconsoleをみながらclickするとちゃんと切り替わっているはずだ。

  const handleSort = (key) => {
    const direction = sortConfig.key === key && sortConfig.direction === 'desc' ? 'asc' : 'desc';
    setSortConfig({ key, direction });
    console.log(sortConfig);
  };

さて、この状況に基づき、アイコンを入れ替えていく。これはそんなしんどい話でもない。

<th scope="col"
  className="px-6 py-3 text-left text-xs font-medium text-blue-600 underline hover:text-blue-800 cursor-pointer hover:bg-gray-100"
  onClick={() => handleSort('id')} >
    ID <span className="inline-block ml-2">
      {sortConfig.key === 'id' && (sortConfig.direction === 'desc' ? <VscArrowDown /> : <VscArrowUp />)}
    </span>
</th>

これは、keyを変更すれば他のフィールドにも適用できるので、last_loginにも適用しておこう。

IDと最終ログインのアイコンが反転するようになった

実際のsort処理

ここまでUIの面ではうまくいってるので、これを実際に裏にぶん投げてlaravelでソートして返却させる。この場合のInertiaはManual Visitのrouterを使う。ここまでやったなら既にimportしているとは思うけど一応こんな感じ

import { Head, useForm, router } from '@inertiajs/react';

そしてconsole.log()を削除して実際のリクエストに置き換える

  const handleSort = (key) => {
    const direction = sortConfig.key === key && sortConfig.direction === 'desc' ? 'asc' : 'desc';
    setSortConfig({ key, direction });
    router.get(route('users.index', { sort: key, order: direction }));
  };

backendの処理

    public function index(Request $request): Response
    {
        $query         = $request->query('q', '');
        $sortKey       = $request->query('sort', 'id');
        $sortDirection = $request->query('order', 'asc');

        if (!empty($query)) {
            $users = User::search($query)->get();
        } else {
            $users = User::all();
        }

        return Inertia::render('Users/Index', [
            'users' => $users,
            'query' => $query ?? '',
            'sort' => [
                'key' => $sortKey,
                'direction' => $sortDirection
            ],
        ]);
    }

まだ実際にソート処理を行っていないが、defaultのキーとdirectionを取得し、inertiaのパラメーターに渡しているということはdefaultをfrontendで指定する必要はなくなった

export default function UserIndex({ auth, users, query, sort }) {
  const { t, currentLocale } = useLaravelReactI18n();
  const {
    data, setData, get, processing, errors, reset,
  } = useForm({
    q: query,
  });
  // const [sortConfig, setSortConfig] = useState({key: "id", direction: "desc"});
  const [sortConfig, setSortConfig] = useState(sort);

のようにsortもパラメーターに渡し、useStateの初期値をbackendから受け取るようにしている。

実際にsortを書く

まあ、あとはこれに基いてDBのパラメーターを入れ替えるだけであるね。

    public function index(Request $request): Response
    {
        $query         = $request->query('q', '');
        $sortKey       = $request->query('sort', 'id');
        $sortDirection = $request->query('order', 'asc');

        $user = new User();
        if ($query) {
            $users = $user->search($query)->get();
        } else {
            $users = $user->orderBy($sortKey, $sortDirection)->get();
        }

        return Inertia::render('Users/Index', [
            'users' => $users,
            'query' => $query ?? '',
            'sort' => [
                'key' => $sortKey,
                'direction' => $sortDirection
            ],
        ]);
    }

検索とのかねあい

たとえば


adminとかで検索して今1人しかいないのであれなんだけど、その場合においても最終ログインはqueryが受け継がれていないので、押すとクリアーされる。これは単純にqも付けとけば解決するとは思う

  const handleSort = (key) => {
    const direction = sortConfig.key === key && sortConfig.direction === 'desc' ? 'asc' : 'desc';
    setSortConfig({ key, direction });
    // router.get(route('admin.users.index', { sort: key, order: direction }));
    router.get(route('admin.users.index', { q: data.q, sort: key, order: direction }));
  };

meilisearchと組合せる

今はまだsearchしたときのorderByを意図的に書いてないが

        $user = new User();
        if ($query) {
            $users = $user->search($query)->get();
        } else {
            $users = $user->orderBy($sortKey, $sortDirection)->get();
        }

これを付けた場合

とかなる場合がある。これはmeilisearchの制限でconfig/scout.php に足しておかないといけない

    'meilisearch' => [
        'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
        'key' => env('MEILISEARCH_KEY'),
        'index-settings' => [
            'users' => [
                'filterableAttributes'=> ['id', 'name', 'email'], 
                 // ↓これ
                 'sortableAttributes'=> ['id', 'name', 'email', 'last_login_at'],
            ],
        ],
    ],

そして

artisan scout:sync-index-settings
Settings for the [users] index synced successfully.

とすれば動く、はず

pager


まあいずれ書けたら書くかな

この記事が気に入ったらサポートをしてみませんか?