アイヌ語追加 (#428) (#433)

Reviewed-on: #433
Co-authored-by: miteruzo <miteruzo@naver.com>
このコミットはプルリクエスト #433 でマージされました。
このコミットが含まれているのは:
2026-10-20 20:44:14 +09:00
committed by みてるぞ
コミット 084a090cab
294個のファイルの変更、13978行の追加、3461行の削除
+16 -13
ファイルの表示
@@ -8,7 +8,7 @@ import App from '@/App'
import { helpFixture } from '@/test/helpFixtures'
vi.hoisted (() => {
window.history.replaceState (null, '', '/help/changes')
window.history.replaceState (null, '', '/ja/help/changes')
})
vi.mock ('@/lib/help', async () => (await import ('@/test/helpFixtures')).helpFixture.api)
@@ -21,7 +21,10 @@ vi.mock ('@/lib/useClientBehaviourSettings', () => ({
describe ('Help global edition history', () => {
beforeEach (() => helpFixture.reset ())
beforeEach (() => {
helpFixture.reset ()
vi.mocked (apiGet).mockResolvedValue ({ locales: [{ code: 'ja', name: '日本語' }] })
})
it ('routes /help/changes to published editions in date and numeric order', async () => {
const client = new QueryClient ({ defaultOptions: { queries: { retry: false } } })
@@ -31,12 +34,12 @@ describe ('Help global edition history', () => {
</QueryClientProvider>)
const links = await screen.findAllByRole ('link')
const editions = links.map (link => link.getAttribute ('href'))
.filter (href => href?.startsWith ('/help/') && href.includes ('edition='))
.filter (href => href?.startsWith ('/ja/help/') && href.includes ('edition='))
expect (editions).toEqual ([
`/help/${encodeURIComponent ('設定 手順')}?edition=2026-10-03`,
'/help/guide?edition=2026-10-02.10', '/help/guide?edition=2026-10-02.3',
'/help/guide?edition=2026-10-02.2', '/help/guide?edition=2026-10-02',
'/help/guide?edition=2026-10-01'])
`/ja/help/${encodeURIComponent ('設定 手順')}?edition=2026-10-03`,
'/ja/help/guide?edition=2026-10-02.10', '/ja/help/guide?edition=2026-10-02.3',
'/ja/help/guide?edition=2026-10-02.2', '/ja/help/guide?edition=2026-10-02',
'/ja/help/guide?edition=2026-10-01'])
expect (container).toHaveTextContent ('旧ホーム')
expect (screen.getAllByRole ('columnheader').map (cell => cell.textContent))
.toEqual (['差分', 'タイトル', '有効開始年月日'])
@@ -45,11 +48,11 @@ describe ('Help global edition history', () => {
expect (container.textContent).not.toMatch (/\d{1,2}:\d{2}/)
expect (screen.getAllByRole ('link', { name: '差分' }).map (link =>
link.getAttribute ('href'))).toEqual ([
'/help/guide/diff?from=2026-10-02.3&to=2026-10-02.10',
'/help/guide/diff?from=2026-10-02.2&to=2026-10-02.3',
'/help/guide/diff?from=2026-10-02&to=2026-10-02.2',
'/help/guide/diff?from=2026-10-01&to=2026-10-02'])
expect (apiGet).not.toHaveBeenCalled ()
expect (window.location.pathname).toBe ('/help/changes')
'/ja/help/guide/diff?from=2026-10-02.3&to=2026-10-02.10',
'/ja/help/guide/diff?from=2026-10-02.2&to=2026-10-02.3',
'/ja/help/guide/diff?from=2026-10-02&to=2026-10-02.2',
'/ja/help/guide/diff?from=2026-10-01&to=2026-10-02'])
expect (apiGet).toHaveBeenCalledWith ('/locales')
expect (window.location.pathname).toBe ('/ja/help/changes')
})
})
+21 -17
ファイルの表示
@@ -1,22 +1,27 @@
import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { Helmet } from 'react-helmet-async'
import { useSearchParams } from 'react-router-dom'
import PrefetchLink from '@/components/PrefetchLink'
import PageTitle from '@/components/common/PageTitle'
import Pagination from '@/components/common/Pagination'
import MainArea from '@/components/layout/MainArea'
import { SITE_TITLE } from '@/config'
import PageHead from '@/components/common/PageHead'
import { getHelpChanges, helpEditionDate } from '@/lib/help'
import { localeLanguage } from '@/lib/locales'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { helpKeys } from '@/lib/queryKeys'
import { LIST_LIMIT_OPTIONS } from '@/lib/settings'
import { dateString } from '@/lib/utils'
import { localisedDateString } from '@/lib/utils'
import type { FC } from 'react'
const HelpChangesPage: FC = () => {
const { message, label } = useUITranslation ('help')
const locale = useLocale ()
const language = localeLanguage (locale)
const [searchParams, setSearchParams] = useSearchParams ()
const identifier = searchParams.get ('identifier') ?? ''
const pageValue = searchParams.get ('page') ?? '1'
@@ -27,8 +32,9 @@ const HelpChangesPage: FC = () => {
const limit = LIST_LIMIT_OPTIONS.find (
value => String (value) === searchParams.get ('limit')) ?? 20
const { data: changes = [], isLoading, error } = useQuery ({
queryKey: helpKeys.changes ('ja'),
queryFn: () => getHelpChanges ('ja'),
queryKey: helpKeys.changes (language),
queryFn: () => getHelpChanges (language),
select: entries => entries.filter (entry => entry.language === language),
staleTime: Infinity })
const results = useMemo (() => identifier
@@ -47,13 +53,11 @@ const HelpChangesPage: FC = () => {
return (
<MainArea className="min-w-0 max-w-full">
<Helmet>
<title>{`ヘルプ改定履歴 | ${ SITE_TITLE }`}</title>
</Helmet>
<PageHead title={message ('history')}/>
<div className="flex flex-wrap items-center justify-between gap-3">
<PageTitle>ヘルプ改定履歴</PageTitle>
<PageTitle>{label ('history')}</PageTitle>
<label className="flex items-center gap-2 text-xs text-muted-foreground">
<span>件数</span>
<span>{label ('common:countLabel')}</span>
<select value={limit} onChange={ev => handleLimit (ev.target.value)}
className="rounded border border-border bg-background px-2 py-1 text-sm
text-foreground">
@@ -64,9 +68,9 @@ const HelpChangesPage: FC = () => {
</div>
{isLoading
? <p className="mt-4">読み込み中...</p>
? <p className="mt-4">{label ('shared:messages.loading')}</p>
: (error
? <p className="mt-4 text-red-600" role="alert">{error.message}</p>
? <p className="mt-4 text-red-600" role="alert">{label ('common:fetchError')}</p>
: (
<div className="mt-4">
<div className="min-w-0 max-w-full overflow-x-auto">
@@ -74,9 +78,9 @@ const HelpChangesPage: FC = () => {
border-collapse">
<thead className="border-b-2 border-black dark:border-white">
<tr>
<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">{label ('shared:actions.diff')}</th>
<th className="p-2 text-left">{label ('shared:labels.title')}</th>
<th className="p-2 text-left">{label ('effectiveSince')}</th>
</tr>
</thead>
<tbody>
@@ -94,7 +98,7 @@ const HelpChangesPage: FC = () => {
<td className="p-2">
{entry.pred != null && (
<PrefetchLink to={diffTo}>
差分
{label ('shared:actions.diff')}
</PrefetchLink>)}
</td>
<td className="p-2">
@@ -103,7 +107,7 @@ const HelpChangesPage: FC = () => {
</PrefetchLink>
</td>
<td className="p-2">
{updatedAt != null && dateString (updatedAt, 'hour')}
{updatedAt != null && localisedDateString (updatedAt, locale, 'hour')}
</td>
</tr>)
})}
+13 -13
ファイルの表示
@@ -17,19 +17,19 @@ const Navigation = () => {
return (
<>
<output data-testid="location">{location.pathname}{location.search}</output>
<button onClick={() => navigate ('/help/設定%20手順')}>switch article</button>
<button onClick={() => navigate ('/help/guide?edition=2026-10-02.3')}>
<button onClick={() => navigate ('/ja/help/設定%20手順')}>switch article</button>
<button onClick={() => navigate ('/ja/help/guide?edition=2026-10-02.3')}>
switch edition
</button>
</>)
}
const renderDetail = (route = '/help/guide') => renderWithProviders (
const renderDetail = (route = '/ja/help/guide') => renderWithProviders (
<>
<Navigation/>
<Routes>
<Route path="/help/:identifier" element={<HelpDetailPage/>}/>
<Route path="/ja/help/:identifier" element={<HelpDetailPage/>}/>
</Routes>
</>, { route })
@@ -39,7 +39,7 @@ describe ('Help details', () => {
it.each (['2026-10-02', '2026-10-02.2', '2026-10-02.3', '2026-10-02.10']) (
'displays a date without inventing a time for edition %s', async edition => {
const { container } = renderDetail (`/help/guide?edition=${ edition }`)
const { container } = renderDetail (`/ja/help/guide?edition=${ edition }`)
await screen.findByRole ('heading')
expect (container).toHaveTextContent ('10月2日')
expect (container).not.toHaveTextContent ('Invalid Date')
@@ -61,31 +61,31 @@ describe ('Help details', () => {
})
it ('uses the historical H1 in both page and document titles', async () => {
renderDetail ('/help/guide?edition=2026-10-01')
renderDetail ('/ja/help/guide?edition=2026-10-01')
expect (await screen.findByRole ('heading', { name: '旧ホーム' })).toBeInTheDocument ()
expect (screen.getByText ('過去限定本文')).toBeInTheDocument ()
await waitFor (() => expect (document.title).toContain ('旧ホーム ヘルプ'))
expect (screen.getByText ('(最古)')).toBeInTheDocument ()
expect (screen.getByRole ('link', { name: '新 >' }))
.toHaveAttribute ('href', '/help/guide?edition=2026-10-02')
.toHaveAttribute ('href', '/ja/help/guide?edition=2026-10-02')
})
it ('navigates .2, .3 and .10 in numeric edition order', async () => {
renderDetail ('/help/guide?edition=2026-10-02.3')
renderDetail ('/ja/help/guide?edition=2026-10-02.3')
await screen.findByRole ('heading', { name: 'ホーム第三版' })
expect (screen.getByRole ('link', { name: '< 古' }))
.toHaveAttribute ('href', '/help/guide?edition=2026-10-02.2')
.toHaveAttribute ('href', '/ja/help/guide?edition=2026-10-02.2')
const next = screen.getByRole ('link', { name: '新 >' })
expect (next).toHaveAttribute ('href', '/help/guide?edition=2026-10-02.10')
expect (next).toHaveAttribute ('href', '/ja/help/guide?edition=2026-10-02.10')
fireEvent.click (next)
await screen.findByRole ('heading', { name: 'ホーム第十版' })
expect (screen.getByText ('(最新)')).toBeInTheDocument ()
expect (screen.queryByText ('第三版本文')).not.toBeInTheDocument ()
})
it.each (['/help/missing', '/help/guide?edition=missing',
'/help/guide?edition=2026-02-30', '/help/guide?edition=2026-10-02.2.extra',
'/help/guide?edition=2026-10-02.11']) (
it.each (['/ja/help/missing', '/ja/help/guide?edition=missing',
'/ja/help/guide?edition=2026-02-30', '/ja/help/guide?edition=2026-10-02.2.extra',
'/ja/help/guide?edition=2026-10-02.11']) (
'shows NotFound without redirecting %s', async route => {
renderDetail (route)
expect (await screen.findByText ('404')).toBeInTheDocument ()
+39 -19
ファイルの表示
@@ -1,37 +1,44 @@
import { useQuery } from '@tanstack/react-query'
import { Helmet } from 'react-helmet-async'
import { useLocation, useParams } from 'react-router-dom'
import PrefetchLink from '@/components/PrefetchLink'
import MainArea from '@/components/layout/MainArea'
import { SITE_TITLE } from '@/config'
import PageHead from '@/components/common/PageHead'
import { getHelpArticle,
getHelpEditionContent,
helpEditionDate,
parseHelpEdition } from '@/lib/help'
parseHelpEdition,
resolveHelpLanguage } from '@/lib/help'
import { localeLanguage } from '@/lib/locales'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { dateString } from '@/lib/utils'
import { helpKeys } from '@/lib/queryKeys'
import { currentRenderContext } from '@/lib/renderContext'
import { LocalisedDocument } from '@/mdx-components'
import NotFound from '@/pages/NotFound'
import type { FC } from 'react'
const HelpDetailPage: FC = () => {
const { message, label } = useUITranslation ('help')
const locale = useLocale ()
const { identifier = '' } = useParams ()
const location = useLocation ()
// Locale 対応が入るまでは ja 固定
const language = 'ja'
const { data: article, isLoading: articleLoading } = useQuery ({
queryKey: helpKeys.article (identifier),
queryFn: () => getHelpArticle (identifier),
staleTime: Infinity })
const requestedLanguage = localeLanguage (locale)
const language = resolveHelpLanguage (article, requestedLanguage)
const query = new URLSearchParams (location.search)
const requestedEdition = query.get ('edition')
const languageMeta = article?.languages[language]
const languageMeta = language == null ? null : article?.languages[language]
const edition = requestedEdition ?? languageMeta?.currentEdition
const editionInfo = parseHelpEdition (edition ?? '')
@@ -48,8 +55,8 @@ const HelpDetailPage: FC = () => {
const { data: current, isSuccess, isError } = useQuery ({
enabled: edition != null && editionInfo != null && editions.includes (edition),
queryKey: helpKeys.edition (identifier, language, edition ?? ''),
queryFn: () => getHelpEditionContent (identifier, language, edition ?? ''),
queryKey: helpKeys.edition (identifier, language ?? requestedLanguage, edition ?? ''),
queryFn: () => getHelpEditionContent (identifier, language ?? requestedLanguage, edition ?? ''),
staleTime: Infinity })
if (!articleLoading
@@ -64,12 +71,19 @@ const HelpDetailPage: FC = () => {
return <NotFound/>
const Content = current?.content
const origin = currentRenderContext ()?.origin
const canonical = origin == null ? undefined : new URL (
`/${locale}/help/${encodeURIComponent (identifier)}`, origin).href
const description = current?.body.replace (/!?(\[([^\]]*)\])\([^)]*\)/g, '$2')
.replace (/[#*`<>]/g, '')
.replace (/\s+/g, language === 'ain' ? '\u3000' : ' ').trim ().slice (0, 180)
return (
<MainArea>
<Helmet>
<title>{current?.title || identifier} ヘルプ | {SITE_TITLE}</title>
</Helmet>
<MainArea className="min-w-0">
<PageHead title={message ('articleTitle', { title: current?.title || identifier })}
canonical={canonical}
description={description == null ? undefined : {
text: description, language: language === 'ain' ? 'ain-Kana' : language ?? 'ja' }}/>
{requestedEdition && (
<div className="text-sm flex gap-3 items-center justify-center
@@ -78,9 +92,9 @@ const HelpDetailPage: FC = () => {
? (
<PrefetchLink
to={`/help/${ encodeURIComponent (identifier) }?edition=${ pred }`}>
&lt; 古
{label ('shared:actions.old')}
</PrefetchLink>)
: '(最古)'}
: label ('shared:actions.oldest')}
<span>
{editionDate != null && dateString (editionDate, 'hour')}
@@ -91,13 +105,19 @@ const HelpDetailPage: FC = () => {
? (
<PrefetchLink
to={`/help/${ encodeURIComponent (identifier) }?edition=${ succ }`}>
新 &gt;
{label ('shared:actions.new')}
</PrefetchLink>)
: '(最新)'}
: label ('shared:actions.latest')}
</div>)}
<article className="prose dark:prose-invert mx-auto p-4">
{Content ? <Content/> : <div>Loading...</div>}
<article lang={language ?? requestedLanguage}
className="prose dark:prose-invert mx-auto min-w-0 p-4">
{Content
? (
<LocalisedDocument language={language ?? requestedLanguage}>
<Content/>
</LocalisedDocument>)
: <div>{label ('common:loading')}</div>}
</article>
</MainArea>)
}
+21 -12
ファイルの表示
@@ -1,12 +1,14 @@
import { useQuery } from '@tanstack/react-query'
import { Helmet } from 'react-helmet-async'
import { useParams, useSearchParams } from 'react-router-dom'
import PrefetchLink from '@/components/PrefetchLink'
import PageTitle from '@/components/common/PageTitle'
import MainArea from '@/components/layout/MainArea'
import { SITE_TITLE } from '@/config'
import { getHelpDiff } from '@/lib/help'
import PageHead from '@/components/common/PageHead'
import { getHelpArticle, getHelpDiff, resolveHelpLanguage } from '@/lib/help'
import { localeLanguage } from '@/lib/locales'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { helpKeys } from '@/lib/queryKeys'
import { cn } from '@/lib/utils'
import NotFound from '@/pages/NotFound'
@@ -17,20 +19,29 @@ const markers = { added: '+ ', removed: '- ', unchanged: ' ' }
const HelpDiffPage: FC = () => {
const { message, label } = useUITranslation ('help')
const locale = useLocale ()
const { identifier = '' } = useParams ()
const [searchParams] = useSearchParams ()
const from = searchParams.get ('from') ?? ''
const to = searchParams.get ('to') ?? ''
const { data: article } = useQuery ({
queryKey: helpKeys.article (identifier),
queryFn: () => getHelpArticle (identifier),
staleTime: Infinity })
const requestedLanguage = localeLanguage (locale)
const language = resolveHelpLanguage (article, requestedLanguage)
const validParams = Boolean (identifier && from && to)
&& searchParams.getAll ('from').length === 1
&& searchParams.getAll ('to').length === 1
const { data: diff, isLoading, isError } = useQuery ({
enabled: validParams,
queryKey: helpKeys.diff (identifier, 'ja', from, to),
queryFn: () => getHelpDiff (identifier, 'ja', from, to),
enabled: validParams && language != null,
queryKey: helpKeys.diff (identifier, requestedLanguage, from, to),
queryFn: () => getHelpDiff (identifier, requestedLanguage, from, to),
staleTime: Infinity })
if (!(validParams) || isError || (!(isLoading) && diff == null))
if (!(validParams) || (article != null && language == null)
|| isError || (!(isLoading) && diff == null))
return <NotFound/>
const title = `${ diff?.to.title ?? identifier } (${ from } → ${ to })`
@@ -38,9 +49,7 @@ const HelpDiffPage: FC = () => {
return (
<MainArea className="min-w-0 max-w-full">
<Helmet>
<title>{`ヘルプ 差分: ${ title } | ${ SITE_TITLE }`}</title>
</Helmet>
<PageHead title={message ('diffTitle', { title })}/>
<PageTitle>{title}</PageTitle>
{diff
@@ -55,7 +64,7 @@ const HelpDiffPage: FC = () => {
{diff.to.title} ({to})
</PrefetchLink>
</div>
<pre className="mt-4 overflow-x-auto whitespace-pre">
<pre lang={requestedLanguage} className="mt-4 overflow-x-auto whitespace-pre">
<code>
{diff.lines.map ((line, index) => {
const colour = cn (
@@ -73,7 +82,7 @@ const HelpDiffPage: FC = () => {
</code>
</pre>
</>)
: <div>Loading...</div>}
: <div>{label ('common:loading')}</div>}
</MainArea>)
}
+6 -6
ファイルの表示
@@ -14,9 +14,9 @@ describe ('Help search', () => {
it ('initially lists each current article once with encoded detail links', async () => {
renderWithProviders (<HelpListPage/>, { route: '/help' })
expect (await screen.findByRole ('link', { name: 'ホーム第十版' }))
.toHaveAttribute ('href', '/help/guide')
.toHaveAttribute ('href', '/ja/help/guide')
expect (screen.getByRole ('link', { name: '設定' }))
.toHaveAttribute ('href', `/help/${encodeURIComponent ('設定 手順')}`)
.toHaveAttribute ('href', `/ja/help/${encodeURIComponent ('設定 手順')}`)
expect (screen.getByText ('2 件')).toBeInTheDocument ()
expect (screen.queryByText ('旧ホーム')).not.toBeInTheDocument ()
expect (screen.queryByText ('未公開ホーム')).not.toBeInTheDocument ()
@@ -82,7 +82,7 @@ describe ('Help search', () => {
renderWithProviders (<HelpListPage/>, { route: `/help?${ params }` })
await screen.findByText (`${ found ? 1 : 0 } 件`)
if (found)
expect (screen.getByRole ('link', { name: title })).toHaveAttribute ('href', '/help/guide')
expect (screen.getByRole ('link', { name: title })).toHaveAttribute ('href', '/ja/help/guide')
else
expect (screen.queryByRole ('link', { name: title })).not.toBeInTheDocument ()
expect (entry).toEqual ({ identifier: 'guide', edition: '2026-10-02.10',
@@ -99,7 +99,7 @@ describe ('Help search', () => {
route: `/help?order=title:${ direction }` })
await screen.findByText ('2 件')
const expected = direction === 'asc'
? ['/help/beta', '/help/alpha'] : ['/help/alpha', '/help/beta']
? ['/ja/help/beta', '/ja/help/alpha'] : ['/ja/help/alpha', '/ja/help/beta']
expect (Array.from (container.querySelectorAll ('tbody a'), link =>
link.getAttribute ('href'))).toEqual (expected)
})
@@ -113,7 +113,7 @@ describe ('Help search', () => {
route: `/help?order=title:${ direction }` })
await screen.findByText ('2 件')
expect (Array.from (container.querySelectorAll ('tbody a'), link =>
link.getAttribute ('href'))).toEqual (['/help/alpha', '/help/beta'])
link.getAttribute ('href'))).toEqual (['/ja/help/alpha', '/ja/help/beta'])
})
it.each (['asc', 'desc']) ('ignores title case before identifier ties in order %s',
@@ -125,6 +125,6 @@ describe ('Help search', () => {
route: `/help?order=title:${ direction }` })
await screen.findByText ('2 件')
expect (Array.from (container.querySelectorAll ('tbody a'), link =>
link.getAttribute ('href'))).toEqual (['/help/alpha', '/help/beta'])
link.getAttribute ('href'))).toEqual (['/ja/help/alpha', '/ja/help/beta'])
})
})
+24 -19
ファイルの表示
@@ -1,6 +1,5 @@
import { useQuery } from '@tanstack/react-query'
import { useEffect, useMemo, useState } from 'react'
import { Helmet } from 'react-helmet-async'
import { useSearchParams } from 'react-router-dom'
import PrefetchLink from '@/components/PrefetchLink'
@@ -9,12 +8,15 @@ 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 PageHead from '@/components/common/PageHead'
import { compareHelpEditions,
compareHelpTitles,
getHelpSearchEntries,
helpEditionDate,
normaliseHelpSearchText } from '@/lib/help'
import { localeLanguage } from '@/lib/locales'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
import { helpKeys } from '@/lib/queryKeys'
import { LIST_LIMIT_OPTIONS } from '@/lib/settings'
import { dateString, inputClass } from '@/lib/utils'
@@ -30,6 +32,8 @@ const defaultDirection: Record<HelpOrderField, 'asc' | 'desc'> = {
const HelpSearchPage: FC = () => {
const { message, label } = useUITranslation ('help')
const language = localeLanguage (useLocale ())
const [searchParams, setSearchParams] = useSearchParams ()
const pageValue = searchParams.get ('page') ?? '1'
const pageNumber = Number (pageValue)
@@ -49,8 +53,9 @@ const HelpSearchPage: FC = () => {
const [title, setTitle] = useState (conditions.title)
const [text, setText] = useState (conditions.text)
const { data: entries = [], isLoading: loading, error } = useQuery ({
queryKey: helpKeys.index ('ja'),
queryFn: () => getHelpSearchEntries ('ja'),
queryKey: helpKeys.index (language),
queryFn: () => getHelpSearchEntries (language),
select: entries => entries.filter (entry => entry.language === language),
staleTime: Infinity })
useEffect (() => {
@@ -107,15 +112,13 @@ const HelpSearchPage: FC = () => {
return (
<MainArea className="min-w-0 max-w-full">
<Helmet>
<title>{`ヘルプ | ${ SITE_TITLE }`}</title>
</Helmet>
<PageHead title={message ('title')}/>
<div className="max-w-xl">
<div className="flex flex-wrap items-center justify-between gap-3">
<PageTitle>ヘルプ検索</PageTitle>
<PageTitle>{label ('search')}</PageTitle>
<label className="flex items-center gap-2 text-xs text-muted-foreground">
<span>件数</span>
<span>{label ('common:countLabel')}</span>
<select value={limit} onChange={ev => handleLimit (ev.target.value)}
className="rounded border border-border bg-background px-2 py-1 text-sm
text-foreground">
@@ -125,19 +128,19 @@ const HelpSearchPage: FC = () => {
</label>
</div>
<form onSubmit={handleSearch} className="space-y-2">
<FormField label="タイトル">
<FormField label={label ('shared:labels.title')}>
{({ invalid }) => (
<input type="text"
aria-label="タイトル"
aria-label={message ('shared:labels.title').text}
value={title}
onChange={e => setTitle (e.target.value)}
className={inputClass (invalid)}/>)}
</FormField>
<FormField label="内容">
<FormField label={label ('shared:labels.content')}>
{({ invalid }) => (
<input type="text"
aria-label="内容"
aria-label={message ('shared:labels.content').text}
value={text}
onChange={e => setText (e.target.value)}
className={inputClass (invalid)}/>)}
@@ -146,19 +149,21 @@ const HelpSearchPage: FC = () => {
<div className="py-3">
<button type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded">
検索
{label ('common:search')}
</button>
</div>
</form>
</div>
{loading
? <p className="mt-4">読み込み中...</p>
? <p className="mt-4">{label ('shared:messages.loading')}</p>
: (error
? <p className="mt-4 text-red-600" role="alert">{error.message}</p>
: (
<div className="mt-4">
<p className="mb-2 text-sm" aria-live="polite">{results.length} 件</p>
<p className="mb-2 text-sm" aria-live="polite">
{label ('shared:labels.resultsCount', { count: results.length })}
</p>
<div className="min-w-0 max-w-full overflow-x-auto">
<table className="table-auto w-full min-w-[640px] whitespace-nowrap
border-collapse text-gray-900 dark:text-gray-100">
@@ -167,14 +172,14 @@ const HelpSearchPage: FC = () => {
<th className="p-2 text-left">
<SortHeader<HelpOrderField>
by="title"
label="タイトル"
label={label ('shared:labels.title')}
currentOrder={order}
defaultDirection={defaultDirection}/>
</th>
<th className="p-2 text-left">
<SortHeader<HelpOrderField>
by="updated_at"
label="有効開始年月日"
label={label ('effectiveSince')}
currentOrder={order}
defaultDirection={defaultDirection}/>
</th>
@@ -203,7 +208,7 @@ const HelpSearchPage: FC = () => {
</table>
</div>
{totalPages > 0 && <Pagination page={page} totalPages={totalPages}/>}
{results.length === 0 && <p className="mt-4">該当する記事がありません.</p>}
{results.length === 0 && <p className="mt-4">{label ('empty')}</p>}
</div>))}
</MainArea>)
}