diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f7c9545..04e14cc 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -19,6 +19,7 @@ import PostNewPage from '@/pages/posts/PostNewPage' import PostSearchPage from '@/pages/posts/PostSearchPage' import ServiceUnavailable from '@/pages/ServiceUnavailable' import SettingPage from '@/pages/users/SettingPage' +import TagListPage from '@/pages/tags/TagListPage' import WikiDetailPage from '@/pages/wiki/WikiDetailPage' import WikiDiffPage from '@/pages/wiki/WikiDiffPage' import WikiEditPage from '@/pages/wiki/WikiEditPage' @@ -46,6 +47,7 @@ const RouteTransitionWrapper = ({ user, setUser }: { }/> }/> }/> + }/> }/> }/> }/> diff --git a/frontend/src/components/TagDetailSidebar.tsx b/frontend/src/components/TagDetailSidebar.tsx index b4be734..1b995a1 100644 --- a/frontend/src/components/TagDetailSidebar.tsx +++ b/frontend/src/components/TagDetailSidebar.tsx @@ -17,7 +17,7 @@ import SectionTitle from '@/components/common/SectionTitle' import SubsectionTitle from '@/components/common/SubsectionTitle' import SidebarComponent from '@/components/layout/SidebarComponent' import { toast } from '@/components/ui/use-toast' -import { CATEGORIES } from '@/consts' +import { CATEGORIES, CATEGORY_NAMES } from '@/consts' import { apiDelete, apiGet, apiPatch, apiPost } from '@/lib/api' import { dateString, originalCreatedAtString } from '@/lib/utils' @@ -255,15 +255,6 @@ export default (({ post }: Props) => { } } - const categoryNames: Record = { - deerjikist: 'ニジラー', - meme: '原作・ネタ元・ミーム等', - character: 'キャラクター', - general: '一般', - material: '素材', - meta: 'メタタグ', - nico: 'ニコニコタグ' } - useEffect (() => { if (!(post)) return @@ -317,7 +308,7 @@ export default (({ post }: Props) => { {CATEGORIES.map ((cat: Category) => ((tags[cat] ?? []).length > 0 || dragging) && ( - {categoryNames[cat]} + {CATEGORY_NAMES[cat]} diff --git a/frontend/src/components/TopNav.tsx b/frontend/src/components/TopNav.tsx index 06c30cf..cd7b633 100644 --- a/frontend/src/components/TopNav.tsx +++ b/frontend/src/components/TopNav.tsx @@ -74,7 +74,7 @@ export default (({ user }: Props) => { { name: '履歴', to: '/posts/changes' }, { name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] }, { name: 'タグ', to: '/tags', subMenu: [ - { name: 'タグ一覧', to: '/tags', visible: false }, + { name: 'タグ一覧', to: '/tags', visible: true }, { name: '別名タグ', to: '/tags/aliases', visible: false }, { name: '上位タグ', to: '/tags/implications', visible: false }, { name: 'ニコニコ連携', to: '/tags/nico' }, diff --git a/frontend/src/consts.ts b/frontend/src/consts.ts index 13968d2..368af3a 100644 --- a/frontend/src/consts.ts +++ b/frontend/src/consts.ts @@ -13,6 +13,16 @@ export const CATEGORIES = [ 'nico', ] as const +export const CATEGORY_NAMES: Record = { + deerjikist: 'ニジラー', + meme: '原作・ネタ元・ミーム等', + character: 'キャラクター', + general: '一般', + material: '素材', + meta: 'メタタグ', + nico: 'ニコニコタグ', + } as const + export const FETCH_POSTS_ORDER_FIELDS = [ 'title', 'url', diff --git a/frontend/src/lib/posts.ts b/frontend/src/lib/posts.ts index 04ec1ba..3570abb 100644 --- a/frontend/src/lib/posts.ts +++ b/frontend/src/lib/posts.ts @@ -5,7 +5,7 @@ import type { FetchPostsParams, Post, PostTagChange } from '@/types' export const fetchPosts = async ( { url, title, tags, match, createdFrom, createdTo, updatedFrom, updatedTo, - originalCreatedFrom, originalCreatedTo, page, limit, order }: FetchPostsParams + originalCreatedFrom, originalCreatedTo, page, limit, order }: FetchPostsParams, ): Promise<{ posts: Post[] count: number }> => diff --git a/frontend/src/lib/queryKeys.ts b/frontend/src/lib/queryKeys.ts index d8cbeef..614810f 100644 --- a/frontend/src/lib/queryKeys.ts +++ b/frontend/src/lib/queryKeys.ts @@ -10,6 +10,7 @@ export const postsKeys = { export const tagsKeys = { root: ['tags'] as const, + index: (p) => ['tags', 'index', p] as const, show: (name: string) => ['tags', name] as const } export const wikiKeys = { diff --git a/frontend/src/lib/tags.ts b/frontend/src/lib/tags.ts index 3d25684..de0e10b 100644 --- a/frontend/src/lib/tags.ts +++ b/frontend/src/lib/tags.ts @@ -3,6 +3,22 @@ import { apiGet } from '@/lib/api' import type { Tag } from '@/types' +export const fetchTags = async ( + { name, category, postCountGTE, postCountLTE, createdFrom, createdTo, + updatedFrom, updatedTo }, +): Promise<{ tags: Tag[] + count: number }> => + await apiGet ('/tags', { params: { + ...(name && { name }), + ...(category && { category }), + ...(postCountGTE && { post_count_gte: postCountGTE }), + ...(postCountLTE && { post_count_lte: postCountLTE }), + ...(createdFrom && { created_from: createdFrom }), + ...(createdTo && { created_to: createdTo }), + ...(updatedFrom && { updated_from: updatedFrom }), + ...(updatedTo && { updated_to: updatedTo }) } }) + + export const fetchTagByName = async (name: string): Promise => { try { diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index 0207b5f..aac3785 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -47,16 +47,16 @@ export const originalCreatedAtString = ( if (from.getHours () === 0 && before.getHours () === 0) { - if (Math.abs (diff - 86_400_000) < 60_000) + if (Math.abs (diff - 86_400_000 /* 1 日 */) < 60_000) return dateString (from, 'hour') + ' (時刻不詳)' if (from.getDate () === 1 && before.getDate () === 1) { - if (2_419_200_000 /* 28 日 */ <= diff && diff < 2_764_800_000 /* 32 日 */) + if (2_332_800_000 /* 27 日 */ < diff && diff < 2_764_800_000 /* 32 日 */) return dateString (from, 'day') + ' (日不詳)' if (from.getMonth () === 0 && before.getMonth () === 0 - && (31_536_000_000 /* 365 日 */ <= diff + && (31_449_600_000 /* 364 日 */ <= diff && diff < 31_708_800_000 /* 367 日 */)) return dateString (from, 'month') + ' (月日不詳)' } @@ -65,9 +65,9 @@ export const originalCreatedAtString = ( } const rtn = ([from ? `${ dateString (from, 'second') }` : '', - '~', + '〜', before ? `${ dateString (new Date (before.getTime () - 60_000), 'second') }` : ''] .filter (Boolean) .join (' ')) - return rtn === '~' ? '年月日不詳' : rtn + return rtn === '〜' ? '年月日不詳' : rtn } diff --git a/frontend/src/pages/posts/PostSearchPage.tsx b/frontend/src/pages/posts/PostSearchPage.tsx index 727dced..057aaf2 100644 --- a/frontend/src/pages/posts/PostSearchPage.tsx +++ b/frontend/src/pages/posts/PostSearchPage.tsx @@ -358,7 +358,7 @@ export default (() => { {results.map (row => ( - + { + const location = useLocation () + const query = useMemo (() => new URLSearchParams (location.search), [location.search]) + + const page = Number (query.get ('page') ?? 1) + const limit = Number (query.get ('limit') ?? 20) + + const qName = query.get ('name') ?? '' + const qCategory = (query.get ('category') || null) as Category | null + const qPostCountGTE = Number (query.get ('post_count_gte') ?? 1) + const qPostCountLTE = Number (query.get ('post_count_lte') ?? (-1)) + const qCreatedFrom = query.get ('created_from') ?? '' + const qCreatedTo = query.get ('created_to') ?? '' + const qUpdatedFrom = query.get ('updated_from') ?? '' + const qUpdatedTo = query.get ('updated_to') ?? '' + const order = (query.get ('order') || 'post_count:desc') as FetchTagsOrder + + const [name, setName] = useState ('') + const [category, setCategory] = useState (null) + const [postCountGTE, setPostCountGTE] = useState (1) + const [postCountLTE, setPostCountLTE] = useState (-1) + const [createdFrom, setCreatedFrom] = useState (null) + const [createdTo, setCreatedTo] = useState (null) + const [updatedFrom, setUpdatedFrom] = useState (null) + const [updatedTo, setUpdatedTo] = useState (null) + + const keys = { name: qName, category: qCategory } + const { data, isLoading: loading } = useQuery ({ + queryKey: tagsKeys.index (keys), + queryFn: () => fetchTags (keys) }) + const results = data?.tags ?? [] + + useEffect (() => { + setName (qName) + setCategory (qCategory) + setPostCountGTE (qPostCountGTE) + setPostCountLTE (qPostCountLTE) + setCreatedFrom (qCreatedFrom) + setCreatedTo (qCreatedTo) + setUpdatedFrom (qUpdatedFrom) + setUpdatedTo (qUpdatedTo) + }, []) + + const handleSearch = () => { + ; + } + + return ( + + + タグ | {SITE_TITLE} + + +
+ タグ + +
+ {/* 名前 */} +
+ + setName (e.target.value)} + className="w-full border p-2 rounded"/> +
+ + {/* カテゴリ */} +
+ + +
+ + {/* 広場の投稿数 */} +
+ + setPostCountGTE (Number (e.target.value || (-1)))} + className="border rounded p-2"/> + + setPostCountLTE (Number (e.target.value || (-1)))} + className="border rounded p-2"/> +
+ + {/* はじめて記載された日時 */} +
+ + + + +
+ + {/* 定義の更新日時 */} +
+ + + + +
+ +
+ +
+
+
+ + {loading ? 'Loading...' : (results.length > 0 ? ( +
+
+ + + + + + + + + + + + + + + + + + + + + {results.map (row => ( + + + + + + + ))} + +
+ + + + + + + + + +
+ + {CATEGORY_NAMES[row.category]}{dateString (row.postCount)}{dateString (row.createdAt)}{dateString (row.updatedAt)}
+
+
) : '結果ないよ(笑)')} +
) +}) satisfies FC diff --git a/frontend/src/types.ts b/frontend/src/types.ts index f40f533..da7eb04 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -26,6 +26,15 @@ export type FetchPostsParams = { limit: number order: FetchPostsOrder } +export type FetchTagsOrder = `${ FetchTagsOrderField }:${ 'asc' | 'desc' }` + +export type FetchTagsOrderField = + | 'name' + | 'category' + | 'post_count' + | 'create_at' + | 'updated_at' + export type Menu = MenuItem[] export type MenuItem = {