This commit is contained in:
@@ -16,6 +16,7 @@ import PostDetailPage from '@/pages/posts/PostDetailPage'
|
|||||||
import PostHistoryPage from '@/pages/posts/PostHistoryPage'
|
import PostHistoryPage from '@/pages/posts/PostHistoryPage'
|
||||||
import PostListPage from '@/pages/posts/PostListPage'
|
import PostListPage from '@/pages/posts/PostListPage'
|
||||||
import PostNewPage from '@/pages/posts/PostNewPage'
|
import PostNewPage from '@/pages/posts/PostNewPage'
|
||||||
|
import PostSearchPage from '@/pages/posts/PostSearchPage'
|
||||||
import ServiceUnavailable from '@/pages/ServiceUnavailable'
|
import ServiceUnavailable from '@/pages/ServiceUnavailable'
|
||||||
import SettingPage from '@/pages/users/SettingPage'
|
import SettingPage from '@/pages/users/SettingPage'
|
||||||
import WikiDetailPage from '@/pages/wiki/WikiDetailPage'
|
import WikiDetailPage from '@/pages/wiki/WikiDetailPage'
|
||||||
@@ -42,6 +43,7 @@ const RouteTransitionWrapper = ({ user, setUser }: {
|
|||||||
<Route path="/" element={<Navigate to="/posts" replace/>}/>
|
<Route path="/" element={<Navigate to="/posts" replace/>}/>
|
||||||
<Route path="/posts" element={<PostListPage/>}/>
|
<Route path="/posts" element={<PostListPage/>}/>
|
||||||
<Route path="/posts/new" element={<PostNewPage user={user}/>}/>
|
<Route path="/posts/new" element={<PostNewPage user={user}/>}/>
|
||||||
|
<Route path="/posts/search" element={<PostSearchPage/>}/>
|
||||||
<Route path="/posts/:id" element={<PostDetailRoute user={user}/>}/>
|
<Route path="/posts/:id" element={<PostDetailRoute user={user}/>}/>
|
||||||
<Route path="/posts/changes" element={<PostHistoryPage/>}/>
|
<Route path="/posts/changes" element={<PostHistoryPage/>}/>
|
||||||
<Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/>
|
<Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/>
|
||||||
|
|||||||
@@ -69,8 +69,9 @@ export default (({ user }: Props) => {
|
|||||||
const menu: Menu = [
|
const menu: Menu = [
|
||||||
{ name: '広場', to: '/posts', subMenu: [
|
{ name: '広場', to: '/posts', subMenu: [
|
||||||
{ name: '一覧', to: '/posts' },
|
{ name: '一覧', to: '/posts' },
|
||||||
|
{ name: '検索', to: '/posts/search' },
|
||||||
{ name: '投稿追加', to: '/posts/new' },
|
{ name: '投稿追加', to: '/posts/new' },
|
||||||
{ name: '耕作履歴', to: '/posts/changes' },
|
{ name: '履歴', to: '/posts/changes' },
|
||||||
{ name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] },
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] },
|
||||||
{ name: 'タグ', to: '/tags', subMenu: [
|
{ name: 'タグ', to: '/tags', subMenu: [
|
||||||
{ name: 'タグ一覧', to: '/tags', visible: false },
|
{ name: 'タグ一覧', to: '/tags', visible: false },
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const prefetchPostChanges: Prefetcher = async (qc, url) => {
|
|||||||
|
|
||||||
export const routePrefetchers: { test: (u: URL) => boolean; run: Prefetcher }[] = [
|
export const routePrefetchers: { test: (u: URL) => boolean; run: Prefetcher }[] = [
|
||||||
{ test: u => u.pathname === '/' || u.pathname === '/posts', run: prefetchPostsIndex },
|
{ test: u => u.pathname === '/' || u.pathname === '/posts', run: prefetchPostsIndex },
|
||||||
{ test: u => (!(['/posts/new', '/posts/changes'].includes (u.pathname))
|
{ test: u => (!(['/posts/new', '/posts/changes', '/posts/search'].includes (u.pathname))
|
||||||
&& Boolean (mPost (u.pathname))),
|
&& Boolean (mPost (u.pathname))),
|
||||||
run: prefetchPostShow },
|
run: prefetchPostShow },
|
||||||
{ test: u => u.pathname === '/posts/changes', run: prefetchPostChanges },
|
{ test: u => u.pathname === '/posts/changes', run: prefetchPostChanges },
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user