This commit is contained in:
@@ -220,8 +220,14 @@ class TagsController < ApplicationController
|
|||||||
category = params[:category].to_s.strip
|
category = params[:category].to_s.strip
|
||||||
return head :unprocessable_entity if name.blank? || category.blank?
|
return head :unprocessable_entity if name.blank? || category.blank?
|
||||||
|
|
||||||
if tag.nico? != (category == 'nico')
|
if name != tag.name &&
|
||||||
return render json: { error: 'ニコタグのカテゴリ変更はできません.' },
|
tag.in?([Tag.tagme, Tag.bot, Tag.no_deerjikist, Tag.video, Tag.niconico])
|
||||||
|
return render json: { error: 'システム・タグの名称は変更できません.' },
|
||||||
|
status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
if tag.nico? || category == 'nico'
|
||||||
|
return render json: { error: 'ニコタグは変更できません.' },
|
||||||
status: :unprocessable_entity
|
status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -258,8 +264,8 @@ class TagsController < ApplicationController
|
|||||||
|
|
||||||
tag = Tag.find(params[:id])
|
tag = Tag.find(params[:id])
|
||||||
|
|
||||||
if category.present? && tag.nico? != (category == 'nico')
|
if tag.nico? || (category.present? && category == 'nico')
|
||||||
return render json: { error: 'ニコタグのカテゴリ変更はできません.' },
|
return render json: { error: 'ニコタグは変更できません.' },
|
||||||
status: :unprocessable_entity
|
status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const RouteTransitionWrapper = ({ user, setUser }: {
|
|||||||
<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" element={<TagListPage/>}/>
|
<Route path="/tags" element={<TagListPage/>}/>
|
||||||
<Route path="/tags/:name" element={<TagDetailPage/>}/>
|
<Route path="/tags/:id" element={<TagDetailPage/>}/>
|
||||||
<Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/>
|
<Route path="/tags/nico" element={<NicoTagListPage user={user}/>}/>
|
||||||
<Route path="/theatres/:id" element={<TheatreDetailPage/>}/>
|
<Route path="/theatres/:id" element={<TheatreDetailPage/>}/>
|
||||||
<Route path="/materials" element={<MaterialBasePage/>}>
|
<Route path="/materials" element={<MaterialBasePage/>}>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import PrefetchLink from '@/components/PrefetchLink'
|
|||||||
import TopNavUser from '@/components/TopNavUser'
|
import TopNavUser from '@/components/TopNavUser'
|
||||||
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
|
import { WikiIdBus } from '@/lib/eventBus/WikiIdBus'
|
||||||
import { tagsKeys, wikiKeys } from '@/lib/queryKeys'
|
import { tagsKeys, wikiKeys } from '@/lib/queryKeys'
|
||||||
import { fetchTagByName } from '@/lib/tags'
|
import { fetchTag, fetchTagByName } from '@/lib/tags'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { fetchWikiPage } from '@/lib/wiki'
|
import { fetchWikiPage } from '@/lib/wiki'
|
||||||
|
|
||||||
@@ -29,6 +29,8 @@ export const menuOutline = ({ tag, wikiId, user, pathName }: {
|
|||||||
const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^\/]+/.test (pathName) && wikiId)
|
const wikiPageFlg = Boolean (/^\/wiki\/(?!new|changes)[^\/]+/.test (pathName) && wikiId)
|
||||||
const wikiTitle = pathName.split ('/')[2] ?? ''
|
const wikiTitle = pathName.split ('/')[2] ?? ''
|
||||||
|
|
||||||
|
const tagFlg = /^\/tags\/\d+/.test (pathName)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{ name: '広場', to: '/posts', subMenu: [
|
{ name: '広場', to: '/posts', subMenu: [
|
||||||
{ name: '一覧', to: '/posts' },
|
{ name: '一覧', to: '/posts' },
|
||||||
@@ -38,10 +40,13 @@ export const menuOutline = ({ tag, wikiId, user, pathName }: {
|
|||||||
{ name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] },
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:広場' }] },
|
||||||
{ name: 'タグ', to: '/tags', subMenu: [
|
{ name: 'タグ', to: '/tags', subMenu: [
|
||||||
{ name: 'マスタ', to: '/tags' },
|
{ name: 'マスタ', to: '/tags' },
|
||||||
{ name: '別名タグ', to: '/tags/aliases', visible: false },
|
|
||||||
{ name: '上位タグ', to: '/tags/implications', visible: false },
|
|
||||||
{ name: 'ニコニコ連携', to: '/tags/nico' },
|
{ name: 'ニコニコ連携', to: '/tags/nico' },
|
||||||
{ name: 'ヘルプ', to: '/wiki/ヘルプ:タグ' }] },
|
{ name: 'ヘルプ', to: '/wiki/ヘルプ:タグ' },
|
||||||
|
{ component: <Separator/>, visible: tagFlg },
|
||||||
|
{ name: `広場 (${ postCount || 0 })`,
|
||||||
|
to: `/posts?tags=${ encodeURIComponent (tag?.name) }`,
|
||||||
|
visible: tagFlg },
|
||||||
|
{ name: '履歴', to: `/tags/changes?id=${ tag?.id }`, visible: false }] },
|
||||||
{ name: '素材', to: '/materials', visible: false, subMenu: [
|
{ name: '素材', to: '/materials', visible: false, subMenu: [
|
||||||
{ name: '一覧', to: '/materials' },
|
{ name: '一覧', to: '/materials' },
|
||||||
{ name: '検索', to: '/materials/search', visible: false },
|
{ name: '検索', to: '/materials/search', visible: false },
|
||||||
@@ -114,12 +119,14 @@ export default (({ user }: Props) => {
|
|||||||
queryKey: wikiKeys.show (wikiIdStr, { }),
|
queryKey: wikiKeys.show (wikiIdStr, { }),
|
||||||
queryFn: () => fetchWikiPage (wikiIdStr, { }) })
|
queryFn: () => fetchWikiPage (wikiIdStr, { }) })
|
||||||
|
|
||||||
const effectiveTitle = wikiPage?.title ?? ''
|
const tagFlg = /^\/tags\/\d+/.test (location.pathname)
|
||||||
|
const effectiveTitle = (tagFlg ? location.pathname.split ('/')[2] : wikiPage?.title) ?? ''
|
||||||
|
|
||||||
const { data: tag } = useQuery ({
|
const { data: tag } = useQuery ({
|
||||||
enabled: Boolean (effectiveTitle),
|
enabled: Boolean (effectiveTitle),
|
||||||
queryKey: tagsKeys.show (effectiveTitle),
|
queryKey: tagsKeys.show (effectiveTitle),
|
||||||
queryFn: () => fetchTagByName (effectiveTitle) })
|
queryFn: () => (tagFlg ? fetchTag : fetchTagByName) (effectiveTitle) })
|
||||||
|
|
||||||
|
|
||||||
const menu = menuOutline ({ tag, wikiId, user, pathName: location.pathname })
|
const menu = menuOutline ({ tag, wikiId, user, pathName: location.pathname })
|
||||||
const visibleMenu = menu.filter ((item): item is MenuVisibleItem => item.visible ?? true)
|
const visibleMenu = menu.filter ((item): item is MenuVisibleItem => item.visible ?? true)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type Prefetcher = (qc: QueryClient, url: URL) => Promise<void>
|
|||||||
|
|
||||||
const mPost = match<{ id: string }> ('/posts/:id')
|
const mPost = match<{ id: string }> ('/posts/:id')
|
||||||
const mWiki = match<{ title: string }> ('/wiki/:title')
|
const mWiki = match<{ title: string }> ('/wiki/:title')
|
||||||
|
const mTag = match<{ id: string }> ('/tags/:id')
|
||||||
|
|
||||||
|
|
||||||
const prefetchWikiPagesIndex: Prefetcher = async (qc, url) => {
|
const prefetchWikiPagesIndex: Prefetcher = async (qc, url) => {
|
||||||
@@ -169,6 +170,19 @@ const prefetchTagsIndex: Prefetcher = async (qc, url) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const prefetchTagShow: Prefetcher = async (qc, url) => {
|
||||||
|
const m = mTag (url.pathname)
|
||||||
|
if (!(m))
|
||||||
|
return
|
||||||
|
|
||||||
|
const { id } = m.params
|
||||||
|
|
||||||
|
await qc.prefetchQuery ({
|
||||||
|
queryKey: tagsKeys.show (id),
|
||||||
|
queryFn: () => fetchTag (id) })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const routePrefetchers: { test: (u: URL) => boolean; run: Prefetcher }[] = [
|
export const routePrefetchers: { test: (u: URL) => boolean; run: Prefetcher }[] = [
|
||||||
{ test: u => ['/', '/posts', '/posts/search'].includes (u.pathname),
|
{ test: u => ['/', '/posts', '/posts/search'].includes (u.pathname),
|
||||||
run: prefetchPostsIndex },
|
run: prefetchPostsIndex },
|
||||||
@@ -180,7 +194,9 @@ export const routePrefetchers: { test: (u: URL) => boolean; run: Prefetcher }[]
|
|||||||
{ test: u => (!(['/wiki/new', '/wiki/changes'].includes (u.pathname))
|
{ test: u => (!(['/wiki/new', '/wiki/changes'].includes (u.pathname))
|
||||||
&& Boolean (mWiki (u.pathname))),
|
&& Boolean (mWiki (u.pathname))),
|
||||||
run: prefetchWikiPageShow },
|
run: prefetchWikiPageShow },
|
||||||
{ test: u => u.pathname === '/tags', run: prefetchTagsIndex }]
|
{ test: u => u.pathname === '/tags', run: prefetchTagsIndex },
|
||||||
|
{ test: u => u.pathname !== '/tags/nico' && Boolean (mTag (u.pathname)),
|
||||||
|
run: prefetchTagShow }]
|
||||||
|
|
||||||
|
|
||||||
export const prefetchForURL = async (qc: QueryClient, urlLike: string): Promise<void> => {
|
export const prefetchForURL = async (qc: QueryClient, urlLike: string): Promise<void> => {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useParams } from 'react-router-dom'
|
import { useParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
import TagLink from '@/components/TagLink'
|
||||||
import Label from '@/components/common/Label'
|
import Label from '@/components/common/Label'
|
||||||
import PageTitle from '@/components/common/PageTitle'
|
import PageTitle from '@/components/common/PageTitle'
|
||||||
import MainArea from '@/components/layout/MainArea'
|
import MainArea from '@/components/layout/MainArea'
|
||||||
@@ -9,7 +10,8 @@ import { toast } from '@/components/ui/use-toast'
|
|||||||
import { CATEGORIES, CATEGORY_NAMES } from '@/consts'
|
import { CATEGORIES, CATEGORY_NAMES } from '@/consts'
|
||||||
import { apiPut } from '@/lib/api'
|
import { apiPut } from '@/lib/api'
|
||||||
import { postsKeys, tagsKeys } from '@/lib/queryKeys'
|
import { postsKeys, tagsKeys } from '@/lib/queryKeys'
|
||||||
import { fetchTagByName } from '@/lib/tags'
|
import { fetchTag } from '@/lib/tags'
|
||||||
|
import { cn } from '@/lib/utils'
|
||||||
|
|
||||||
import type { FC, FormEvent } from 'react'
|
import type { FC, FormEvent } from 'react'
|
||||||
|
|
||||||
@@ -17,18 +19,19 @@ import type { Category, Tag } from '@/types'
|
|||||||
|
|
||||||
|
|
||||||
export default (() => {
|
export default (() => {
|
||||||
const { name: nameRaw } = useParams ()
|
const { id } = useParams ()
|
||||||
const tagName = String (nameRaw ?? '')
|
const tagId = String (id ?? '')
|
||||||
const tagKey = tagsKeys.show (tagName)
|
const tagKey = tagsKeys.show (tagId)
|
||||||
|
|
||||||
const { data: tag, isLoading: loading } = useQuery ({
|
const { data: tag, isLoading: loading } = useQuery ({
|
||||||
queryKey: tagKey,
|
queryKey: tagKey,
|
||||||
queryFn: () => fetchTagByName (tagName) })
|
queryFn: () => fetchTag (tagId) })
|
||||||
|
|
||||||
const [name, setName] = useState ('')
|
const [name, setName] = useState ('')
|
||||||
const [category, setCategory] = useState<Category> ('general')
|
const [category, setCategory] = useState<Category> ('general')
|
||||||
const [aliases, setAliases] = useState ('')
|
const [aliases, setAliases] = useState ('')
|
||||||
const [parentTags, setParentTags] = useState ('')
|
const [parentTags, setParentTags] = useState ('')
|
||||||
|
const [disabled, setDisabled] = useState (true)
|
||||||
|
|
||||||
const qc = useQueryClient ()
|
const qc = useQueryClient ()
|
||||||
|
|
||||||
@@ -43,7 +46,8 @@ export default (() => {
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const data = await apiPut<Tag> (`/tags/${ tag?.id }`, formData)
|
const data = await apiPut<Tag> (`/tags/${ id }`, formData)
|
||||||
|
|
||||||
setName (data.name)
|
setName (data.name)
|
||||||
setCategory (data.category as Category)
|
setCategory (data.category as Category)
|
||||||
setAliases (data.aliases.join (' '))
|
setAliases (data.aliases.join (' '))
|
||||||
@@ -61,26 +65,37 @@ export default (() => {
|
|||||||
|
|
||||||
useEffect (() => {
|
useEffect (() => {
|
||||||
if (!(tag))
|
if (!(tag))
|
||||||
return
|
{
|
||||||
|
setDisabled (true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
setName (tag.name)
|
setName (tag.name)
|
||||||
setCategory (tag.category as Category)
|
setCategory (tag.category as Category)
|
||||||
setAliases (tag.aliases?.join (' '))
|
setAliases (tag.aliases?.join (' '))
|
||||||
setParentTags (tag.parents?.map (t => t.name).join (' '))
|
setParentTags (tag.parents?.map (t => t.name).join (' '))
|
||||||
|
setDisabled (tag.category === 'nico')
|
||||||
}, [tag])
|
}, [tag])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainArea>
|
<MainArea>
|
||||||
{(loading || !(tag)) ? 'Loading...' : (
|
{(loading || !(tag)) ? 'Loading...' : (
|
||||||
<div className="max-w-xl">
|
<div className="max-w-xl">
|
||||||
<PageTitle>{tag.name}</PageTitle>
|
<PageTitle>
|
||||||
|
<TagLink
|
||||||
|
tag={tag}
|
||||||
|
withWiki={false}
|
||||||
|
withCount={false}/>
|
||||||
|
</PageTitle>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="my-4 space-y-2">
|
<form onSubmit={handleSubmit} className="my-4 space-y-2">
|
||||||
{/* 名称 */}
|
{/* 名称 */}
|
||||||
<div>
|
<div>
|
||||||
<Label>名称</Label>
|
<Label>名称</Label>
|
||||||
|
{/* TODO: 補完に対応させる */}
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
disabled={disabled}
|
||||||
value={name}
|
value={name}
|
||||||
onChange={e => setName (e.target.value)}
|
onChange={e => setName (e.target.value)}
|
||||||
className="w-full border p-2 rounded"/>
|
className="w-full border p-2 rounded"/>
|
||||||
@@ -90,10 +105,12 @@ export default (() => {
|
|||||||
<div>
|
<div>
|
||||||
<Label>カテゴリ</Label>
|
<Label>カテゴリ</Label>
|
||||||
<select
|
<select
|
||||||
|
disabled={disabled}
|
||||||
value={category ?? ''}
|
value={category ?? ''}
|
||||||
onChange={e => setCategory(e.target.value as Category)}
|
onChange={e => setCategory(e.target.value as Category)}
|
||||||
className="w-full border p-2 rounded">
|
className="w-full border p-2 rounded">
|
||||||
{CATEGORIES.filter (cat => cat !== 'nico').map (cat => (
|
{CATEGORIES.filter (cat => tag.category === 'nico' || cat !== 'nico')
|
||||||
|
.map (cat => (
|
||||||
<option key={cat} value={cat}>
|
<option key={cat} value={cat}>
|
||||||
{CATEGORY_NAMES[cat]}
|
{CATEGORY_NAMES[cat]}
|
||||||
</option>))}
|
</option>))}
|
||||||
@@ -103,8 +120,10 @@ export default (() => {
|
|||||||
{/* 別名 */}
|
{/* 別名 */}
|
||||||
<div>
|
<div>
|
||||||
<Label>別名</Label>
|
<Label>別名</Label>
|
||||||
|
{/* TODO: 補完に対応させる */}
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
disabled={disabled}
|
||||||
value={aliases}
|
value={aliases}
|
||||||
onChange={e => setAliases (e.target.value)}
|
onChange={e => setAliases (e.target.value)}
|
||||||
className="w-full border p-2 rounded"/>
|
className="w-full border p-2 rounded"/>
|
||||||
@@ -113,8 +132,10 @@ export default (() => {
|
|||||||
{/* 上位タグ */}
|
{/* 上位タグ */}
|
||||||
<div>
|
<div>
|
||||||
<Label>上位タグ</Label>
|
<Label>上位タグ</Label>
|
||||||
|
{/* TODO: 補完に対応させる */}
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
disabled={disabled}
|
||||||
value={parentTags}
|
value={parentTags}
|
||||||
onChange={e => setParentTags (e.target.value)}
|
onChange={e => setParentTags (e.target.value)}
|
||||||
className="w-full border p-2 rounded"/>
|
className="w-full border p-2 rounded"/>
|
||||||
@@ -123,7 +144,11 @@ export default (() => {
|
|||||||
<div className="py-3">
|
<div className="py-3">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="bg-blue-500 text-white px-4 py-2 rounded">
|
disabled={disabled}
|
||||||
|
className={cn ('px-4 py-2 rounded',
|
||||||
|
(disabled
|
||||||
|
? 'text-gray-300 bg-gray-500'
|
||||||
|
: 'text-white bg-blue-500'))}>
|
||||||
更新
|
更新
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -205,13 +205,15 @@ export default (() => {
|
|||||||
{loading ? 'Loading...' : (results.length > 0 ? (
|
{loading ? 'Loading...' : (results.length > 0 ? (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="w-full min-w-[1200px] table-fixed border-collapse">
|
<table className="w-full min-w-[2000px] table-fixed border-collapse">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col className="w-72"/>
|
<col className="w-72"/>
|
||||||
<col className="w-48"/>
|
|
||||||
<col className="w-16"/>
|
<col className="w-16"/>
|
||||||
<col className="w-44"/>
|
<col className="w-48"/>
|
||||||
<col className="w-44"/>
|
<col className="w-72"/>
|
||||||
|
<col className="w-48"/>
|
||||||
|
<col className="w-56"/>
|
||||||
|
<col className="w-56"/>
|
||||||
<col className="w-16"/>
|
<col className="w-16"/>
|
||||||
</colgroup>
|
</colgroup>
|
||||||
|
|
||||||
@@ -224,13 +226,6 @@ export default (() => {
|
|||||||
currentOrder={order}
|
currentOrder={order}
|
||||||
defaultDirection={defaultDirection}/>
|
defaultDirection={defaultDirection}/>
|
||||||
</th>
|
</th>
|
||||||
<th className="p-2 text-left whitespace-nowrap">
|
|
||||||
<SortHeader<FetchTagsOrderField>
|
|
||||||
by="category"
|
|
||||||
label="カテゴリ"
|
|
||||||
currentOrder={order}
|
|
||||||
defaultDirection={defaultDirection}/>
|
|
||||||
</th>
|
|
||||||
<th className="p-2 text-left whitespace-nowrap">
|
<th className="p-2 text-left whitespace-nowrap">
|
||||||
<SortHeader<FetchTagsOrderField>
|
<SortHeader<FetchTagsOrderField>
|
||||||
by="post_count"
|
by="post_count"
|
||||||
@@ -238,6 +233,15 @@ export default (() => {
|
|||||||
currentOrder={order}
|
currentOrder={order}
|
||||||
defaultDirection={defaultDirection}/>
|
defaultDirection={defaultDirection}/>
|
||||||
</th>
|
</th>
|
||||||
|
<th className="p-2 text-left whitespace-nowrap">
|
||||||
|
<SortHeader<FetchTagsOrderField>
|
||||||
|
by="category"
|
||||||
|
label="カテゴリ"
|
||||||
|
currentOrder={order}
|
||||||
|
defaultDirection={defaultDirection}/>
|
||||||
|
</th>
|
||||||
|
<th className="p-2 text-left whitespace-nowrap">別名</th>
|
||||||
|
<th className="p-2 text-left whitespace-nowrap">上位タグ</th>
|
||||||
<th className="p-2 text-left whitespace-nowrap">
|
<th className="p-2 text-left whitespace-nowrap">
|
||||||
<SortHeader<FetchTagsOrderField>
|
<SortHeader<FetchTagsOrderField>
|
||||||
by="created_at"
|
by="created_at"
|
||||||
@@ -262,11 +266,21 @@ export default (() => {
|
|||||||
<td className="p-2">
|
<td className="p-2">
|
||||||
<TagLink
|
<TagLink
|
||||||
tag={row}
|
tag={row}
|
||||||
to={`/tags/${ encodeURIComponent (row.name) }`}
|
to={`/tags/${ encodeURIComponent (row.id) }`}
|
||||||
withCount={false}/>
|
withCount={false}/>
|
||||||
</td>
|
</td>
|
||||||
<td className="p-2">{CATEGORY_NAMES[row.category]}</td>
|
|
||||||
<td className="p-2 text-right">{row.postCount}</td>
|
<td className="p-2 text-right">{row.postCount}</td>
|
||||||
|
<td className="p-2">{CATEGORY_NAMES[row.category]}</td>
|
||||||
|
<td className="p-2">{row.aliases.join (' ')}</td>
|
||||||
|
<td className="p-2">
|
||||||
|
{row.parents.map (t => (
|
||||||
|
<span key={t.id} className="mr-2">
|
||||||
|
<TagLink
|
||||||
|
tag={t}
|
||||||
|
withWiki={false}
|
||||||
|
withCount={false}/>
|
||||||
|
</span>))}
|
||||||
|
</td>
|
||||||
<td className="p-2">{dateString (row.createdAt)}</td>
|
<td className="p-2">{dateString (row.createdAt)}</td>
|
||||||
<td className="p-2">{dateString (row.updatedAt)}</td>
|
<td className="p-2">{dateString (row.updatedAt)}</td>
|
||||||
<td className="p-2">
|
<td className="p-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user