ユーザー管理をさらに作っていく
現状
こんな感じになっている。ここで「最終ログイン」のカラムがあるので、これをクリックして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を表すアイコンを持ってくる。ここではVscArrowUpとVscArrowDownを使う
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(他)の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にも適用しておこう。
実際の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
まあいずれ書けたら書くかな
この記事が気に入ったらサポートをしてみませんか?