|
|
@@ -0,0 +1,165 @@ |
|
|
|
|
|
import { useState } from 'react' |
|
|
|
|
|
import { Helmet } from 'react-helmet-async' |
|
|
|
|
|
import { useLocation } from 'react-router-dom' |
|
|
|
|
|
|
|
|
|
|
|
import DateTimeField from '@/components/common/DateTimeField' |
|
|
|
|
|
import Label from '@/components/common/Label' |
|
|
|
|
|
import SectionTitle from '@/components/common/SectionTitle' |
|
|
|
|
|
import MainArea from '@/components/layout/MainArea' |
|
|
|
|
|
import { SITE_TITLE } from '@/config' |
|
|
|
|
|
import { apiGet } from '@/lib/api' |
|
|
|
|
|
|
|
|
|
|
|
import type { FC, FormEvent } from 'react' |
|
|
|
|
|
|
|
|
|
|
|
import type { Post } from '@/types' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default (() => { |
|
|
|
|
|
const [createdFrom, setCreatedFrom] = useState<string | null> (null) |
|
|
|
|
|
const [createdTo, setCreatedTo] = useState<string | null> (null) |
|
|
|
|
|
const [matchType, setMatchType] = useState<'all' | 'any'> ('all') |
|
|
|
|
|
const [originalCreatedFrom, setOriginalCreatedFrom] = useState<string | null> (null) |
|
|
|
|
|
const [originalCreatedTo, setOriginalCreatedTo] = useState<string | null> (null) |
|
|
|
|
|
const [tagsStr, setTagsStr] = useState ('') |
|
|
|
|
|
const [title, setTitle] = useState ('') |
|
|
|
|
|
const [updatedFrom, setUpdatedFrom] = useState<string | null> (null) |
|
|
|
|
|
const [updatedTo, setUpdatedTo] = useState<string | null> (null) |
|
|
|
|
|
const [url, setURL] = useState ('') |
|
|
|
|
|
const [results, setResults] = useState<Post[]> ([]) |
|
|
|
|
|
|
|
|
|
|
|
const location = useLocation () |
|
|
|
|
|
const query = new URLSearchParams (location.search) |
|
|
|
|
|
const page = Number (query.get ('page') ?? 1) |
|
|
|
|
|
const limit = Number (query.get ('limit') ?? 20) |
|
|
|
|
|
|
|
|
|
|
|
const search = async () => { |
|
|
|
|
|
const tags = tagsStr.split (' ').filter (e => e !== '') |
|
|
|
|
|
setResults (await apiGet ('/posts', { params: { |
|
|
|
|
|
url, title, tags: tags.join (' '), match: matchType, |
|
|
|
|
|
created_from: createdFrom, created_to: createdTo, |
|
|
|
|
|
updated_from: updatedFrom, updated_to: updatedTo, |
|
|
|
|
|
original_created_from: originalCreatedFrom, |
|
|
|
|
|
original_created_to: originalCreatedTo, |
|
|
|
|
|
page, limit } })) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const handleSearch = (e: FormEvent) => { |
|
|
|
|
|
e.preventDefault () |
|
|
|
|
|
search () |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
<MainArea> |
|
|
|
|
|
<Helmet> |
|
|
|
|
|
<title>広場検索 | {SITE_TITLE}</title> |
|
|
|
|
|
</Helmet> |
|
|
|
|
|
|
|
|
|
|
|
<div className="max-w-xl"> |
|
|
|
|
|
<SectionTitle>広場検索</SectionTitle> |
|
|
|
|
|
<form onSubmit={handleSearch} className="space-y-2"> |
|
|
|
|
|
{/* URL */} |
|
|
|
|
|
<div> |
|
|
|
|
|
<Label>URL</Label> |
|
|
|
|
|
<input |
|
|
|
|
|
type="url" |
|
|
|
|
|
value={url} |
|
|
|
|
|
onChange={e => setURL (e.target.value)} |
|
|
|
|
|
className="w-full border p-2 rounded"/> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{/* タイトル */} |
|
|
|
|
|
<div> |
|
|
|
|
|
<Label>タイトル</Label> |
|
|
|
|
|
<input |
|
|
|
|
|
type="text" |
|
|
|
|
|
value={title} |
|
|
|
|
|
onChange={e => setTitle (e.target.value)} |
|
|
|
|
|
className="w-full border p-2 rounded"/> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{/* タグ */} |
|
|
|
|
|
<div> |
|
|
|
|
|
<Label>タグ</Label> |
|
|
|
|
|
<input |
|
|
|
|
|
type="text" |
|
|
|
|
|
value={tagsStr} |
|
|
|
|
|
onChange={e => setTagsStr (e.target.value)} |
|
|
|
|
|
className="w-full border p-2 rounded"/> |
|
|
|
|
|
<fieldset className="w-full my-2"> |
|
|
|
|
|
<label>検索区分:</label> |
|
|
|
|
|
<label className="mx-2"> |
|
|
|
|
|
<input |
|
|
|
|
|
type="radio" |
|
|
|
|
|
name="match-type" |
|
|
|
|
|
checked={matchType === 'all'} |
|
|
|
|
|
onChange={() => setMatchType ('all')}/> |
|
|
|
|
|
AND |
|
|
|
|
|
</label> |
|
|
|
|
|
<label className="mx-2"> |
|
|
|
|
|
<input |
|
|
|
|
|
type="radio" |
|
|
|
|
|
name="match-type" |
|
|
|
|
|
checked={matchType === 'any'} |
|
|
|
|
|
onChange={() => setMatchType ('any')}/> |
|
|
|
|
|
OR |
|
|
|
|
|
</label> |
|
|
|
|
|
</fieldset> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{/* 投稿日時 */} |
|
|
|
|
|
<div> |
|
|
|
|
|
<Label>投稿日時</Label> |
|
|
|
|
|
<DateTimeField |
|
|
|
|
|
value={createdFrom ?? undefined} |
|
|
|
|
|
onChange={setCreatedFrom}/> |
|
|
|
|
|
<span className="mx-1">〜</span> |
|
|
|
|
|
<DateTimeField |
|
|
|
|
|
value={createdTo ?? undefined} |
|
|
|
|
|
onChange={setCreatedTo}/> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{/* 更新日時 */} |
|
|
|
|
|
<div> |
|
|
|
|
|
<Label>更新日時</Label> |
|
|
|
|
|
<DateTimeField |
|
|
|
|
|
value={updatedFrom ?? undefined} |
|
|
|
|
|
onChange={setUpdatedFrom}/> |
|
|
|
|
|
<span className="mx-1">〜</span> |
|
|
|
|
|
<DateTimeField |
|
|
|
|
|
value={updatedTo ?? undefined} |
|
|
|
|
|
onChange={setUpdatedTo}/> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{/* オリジナルの投稿日時 */} |
|
|
|
|
|
<div> |
|
|
|
|
|
<Label>オリジナルの投稿日時</Label> |
|
|
|
|
|
<DateTimeField |
|
|
|
|
|
value={originalCreatedFrom ?? undefined} |
|
|
|
|
|
onChange={setOriginalCreatedFrom}/> |
|
|
|
|
|
<span className="mx-1">〜</span> |
|
|
|
|
|
<DateTimeField |
|
|
|
|
|
value={originalCreatedTo ?? undefined} |
|
|
|
|
|
onChange={setOriginalCreatedTo}/> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{/* 検索 */} |
|
|
|
|
|
<div className="py-3"> |
|
|
|
|
|
<button |
|
|
|
|
|
type="submit" |
|
|
|
|
|
className="bg-blue-500 text-white px-4 py-2 rounded"> |
|
|
|
|
|
検索 |
|
|
|
|
|
</button> |
|
|
|
|
|
</div> |
|
|
|
|
|
</form> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div className="mt-4"> |
|
|
|
|
|
<table className="table-auto w-full border-collapse"> |
|
|
|
|
|
<thead> |
|
|
|
|
|
<tr> |
|
|
|
|
|
</tr> |
|
|
|
|
|
</thead> |
|
|
|
|
|
</table> |
|
|
|
|
|
</div> |
|
|
|
|
|
</MainArea>) |
|
|
|
|
|
}) satisfies FC |