214 行
6.5 KiB
TypeScript
214 行
6.5 KiB
TypeScript
import { useQuery } from '@tanstack/react-query'
|
||
import { useEffect, useState } from 'react'
|
||
import { Helmet } from 'react-helmet-async'
|
||
import { useLocation, useNavigate } from 'react-router-dom'
|
||
|
||
import PrefetchLink from '@/components/PrefetchLink'
|
||
import FormField from '@/components/common/FormField'
|
||
import PageTitle from '@/components/common/PageTitle'
|
||
import Pagination from '@/components/common/Pagination'
|
||
import MainArea from '@/components/layout/MainArea'
|
||
import { SITE_TITLE } from '@/config'
|
||
import { fetchMaterialChanges } from '@/lib/materials'
|
||
import { materialsKeys } from '@/lib/queryKeys'
|
||
import { dateString, inputClass } from '@/lib/utils'
|
||
|
||
import type { FC, FormEvent } from 'react'
|
||
|
||
|
||
const MaterialHistoryPage: FC = () => {
|
||
const location = useLocation ()
|
||
const navigate = useNavigate ()
|
||
const query = new URLSearchParams (location.search)
|
||
|
||
const materialId = query.get ('material_id') ?? ''
|
||
const tag = query.get ('tag') ?? ''
|
||
const eventType = query.get ('event_type') ?? ''
|
||
const page = Number (query.get ('page') ?? 1)
|
||
const limit = Number (query.get ('limit') ?? 20)
|
||
|
||
const [materialIdInput, setMaterialIdInput] = useState (materialId)
|
||
const [tagInput, setTagInput] = useState (tag)
|
||
const [eventTypeInput, setEventTypeInput] = useState (eventType)
|
||
|
||
useEffect (() => {
|
||
setMaterialIdInput (materialId)
|
||
setTagInput (tag)
|
||
setEventTypeInput (eventType)
|
||
}, [eventType, materialId, tag])
|
||
|
||
const { data, isLoading, isError } = useQuery ({
|
||
queryKey: materialsKeys.changes ({
|
||
...(materialId && { materialId }),
|
||
...(tag && { tag }),
|
||
...(eventType && { eventType }),
|
||
page,
|
||
limit }),
|
||
queryFn: () => fetchMaterialChanges ({
|
||
...(materialId && { materialId }),
|
||
...(tag && { tag }),
|
||
...(eventType && { eventType }),
|
||
page,
|
||
limit }) })
|
||
|
||
const versions = data?.versions ?? []
|
||
const totalPages = data ? Math.ceil (data.count / limit) : 0
|
||
|
||
useEffect (() => {
|
||
document.querySelector ('table')?.scrollIntoView ({ behavior: 'smooth' })
|
||
}, [location.search])
|
||
|
||
const handleSearch = (event: FormEvent) => {
|
||
event.preventDefault ()
|
||
|
||
const qs = new URLSearchParams ()
|
||
if (materialIdInput.trim ())
|
||
qs.set ('material_id', materialIdInput.trim ())
|
||
if (tagInput.trim ())
|
||
qs.set ('tag', tagInput.trim ())
|
||
if (eventTypeInput.trim ())
|
||
qs.set ('event_type', eventTypeInput.trim ())
|
||
qs.set ('page', '1')
|
||
qs.set ('limit', String (limit))
|
||
navigate (`/materials/changes?${ qs.toString () }`)
|
||
}
|
||
|
||
return (
|
||
<MainArea>
|
||
<Helmet>
|
||
<title>{`素材履歴 | ${ SITE_TITLE }`}</title>
|
||
</Helmet>
|
||
|
||
<div className="space-y-5">
|
||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||
<PageTitle>素材履歴</PageTitle>
|
||
<PrefetchLink
|
||
to="/materials"
|
||
className="text-sm text-sky-700 underline underline-offset-2 dark:text-sky-300">
|
||
素材一覧へ戻る
|
||
</PrefetchLink>
|
||
</div>
|
||
|
||
<form
|
||
onSubmit={handleSearch}
|
||
className="max-w-3xl rounded-lg border border-stone-200 bg-white p-4
|
||
text-stone-900 dark:border-stone-700 dark:bg-stone-900
|
||
dark:text-stone-100">
|
||
<div className="space-y-3">
|
||
<FormField label="素材 ID">
|
||
{({ invalid }) => (
|
||
<input
|
||
type="text"
|
||
value={materialIdInput}
|
||
onChange={e => setMaterialIdInput (e.target.value)}
|
||
className={inputClass (invalid)}/>)}
|
||
</FormField>
|
||
|
||
<FormField label="タグ名">
|
||
{({ invalid }) => (
|
||
<input
|
||
type="text"
|
||
value={tagInput}
|
||
onChange={e => setTagInput (e.target.value)}
|
||
className={inputClass (invalid)}/>)}
|
||
</FormField>
|
||
|
||
<FormField label="イベント">
|
||
{({ invalid }) => (
|
||
<select
|
||
value={eventTypeInput}
|
||
onChange={e => setEventTypeInput (e.target.value)}
|
||
className={inputClass (invalid)}>
|
||
<option value="">すべて</option>
|
||
<option value="create">create</option>
|
||
<option value="update">update</option>
|
||
<option value="discard">discard</option>
|
||
<option value="restore">restore</option>
|
||
</select>)}
|
||
</FormField>
|
||
</div>
|
||
|
||
<button
|
||
type="submit"
|
||
className="mt-4 rounded-full border border-stone-300 bg-white px-4 py-2
|
||
text-sm text-stone-900 hover:bg-stone-100 dark:border-stone-700
|
||
dark:bg-stone-900 dark:text-stone-100 dark:hover:bg-stone-800">
|
||
絞り込む
|
||
</button>
|
||
</form>
|
||
|
||
{isLoading
|
||
? 'Loading...'
|
||
: isError
|
||
? (
|
||
<p className="text-red-600 dark:text-red-300">
|
||
素材履歴の取得に失敗しました.
|
||
</p>)
|
||
: (
|
||
<>
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full min-w-[1200px] table-fixed border-collapse">
|
||
<colgroup>
|
||
<col className="w-48"/>
|
||
<col className="w-32"/>
|
||
<col className="w-28"/>
|
||
<col className="w-24"/>
|
||
<col className="w-64"/>
|
||
<col className="w-64"/>
|
||
<col className="w-80"/>
|
||
<col className="w-80"/>
|
||
<col className="w-48"/>
|
||
</colgroup>
|
||
|
||
<thead className="border-b-2 border-black dark:border-white">
|
||
<tr>
|
||
<th className="p-2 text-left">日時</th>
|
||
<th className="p-2 text-left">event_type</th>
|
||
<th className="p-2 text-left">素材 ID</th>
|
||
<th className="p-2 text-left">版</th>
|
||
<th className="p-2 text-left">タグ名</th>
|
||
<th className="p-2 text-left">ファイル名</th>
|
||
<th className="p-2 text-left">URL</th>
|
||
<th className="p-2 text-left">export_path</th>
|
||
<th className="p-2 text-left">操作者</th>
|
||
</tr>
|
||
</thead>
|
||
|
||
<tbody>
|
||
{versions.map (version => (
|
||
<tr
|
||
key={version.id}
|
||
className="even:bg-gray-100 dark:even:bg-gray-700">
|
||
<td className="p-2">{dateString (version.createdAt)}</td>
|
||
<td className="p-2">{version.eventType}</td>
|
||
<td className="p-2">
|
||
<PrefetchLink to={`/materials/${ version.materialId }`}>
|
||
#{version.materialId}
|
||
</PrefetchLink>
|
||
</td>
|
||
<td className="p-2">{version.versionNo}</td>
|
||
<td className="p-2 break-all">{version.tagName}</td>
|
||
<td className="p-2 break-all">{version.fileFilename}</td>
|
||
<td className="p-2 break-all">{version.url}</td>
|
||
<td className="p-2 break-all">
|
||
{version.exportPathsJson.legacy_drive}
|
||
</td>
|
||
<td className="p-2">
|
||
{version.createdByUser
|
||
? version.createdByUser.name || `#${ version.createdByUser.id }`
|
||
: 'bot 操作'}
|
||
</td>
|
||
</tr>))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<Pagination page={page} totalPages={totalPages}/>
|
||
</>)}
|
||
</div>
|
||
</MainArea>)
|
||
}
|
||
|
||
|
||
export default MaterialHistoryPage
|