このコミットが含まれているのは:
2026-11-01 18:18:54 +09:00
コミット 22e57564a5
18個のファイルの変更、313行の追加、121行の削除
+25 -16
ファイルの表示
@@ -1,4 +1,5 @@
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { LoaderCircle } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
@@ -15,7 +16,8 @@ import Pagination from '@/components/common/Pagination'
import MainArea from '@/components/layout/MainArea'
import SidebarComponent from '@/components/layout/SidebarComponent'
import { Button } from '@/components/ui/button'
import { countsRequestParams, countsDefinitionParams, countsExportPayload, fieldsForCounts, fetchCounts,
import { countsResourcePath, countsRequestParams, countsDefinitionParams,
countsExportPayload, fieldsForCounts, fetchCounts,
isCountsState, normaliseCountsParams, selectedCountColumns } from '@/lib/counts'
import { countsErrorMessage, countsExportLabels } from '@/lib/countsDisplay'
import { isApiError } from '@/lib/api'
@@ -52,10 +54,10 @@ const tooManyColumns = (failure: unknown): number | null => {
const CountsResourceContent = (
{ resource, definition, definitionError }: {
{ resource, definition, definitionError, definitionFailure }: {
resource: CountsResource
definition?: CountsDefinition
definitionError: boolean },
definitionError: boolean; definitionFailure?: unknown },
) => {
const queryClient = useQueryClient ()
const [mounted, setMounted] = useState (false)
@@ -106,7 +108,7 @@ const CountsResourceContent = (
const requestKey = request.toString ()
const [exporting, setExporting] = useState (false)
const exportInFlight = useRef (false)
const [exportError, setExportError] = useState (false)
const [exportError, setExportError] = useState<{ failure: unknown } | null> (null)
const { data: cachedData, error: queryError, dataUpdatedAt, isFetching: fetching,
isPending: pending, isError: errorFlg,
isPlaceholderData: placeholder } = useQuery ({
@@ -236,7 +238,7 @@ const CountsResourceContent = (
const waiting = running || (!definitionError && (definition == null
|| pending || fetching || placeholder
|| (!params.has ('execution_at') && !errorFlg)))
const failure = runError ? runFailure : queryError
const failure = definitionError ? definitionFailure : (runError ? runFailure : queryError)
const failureMessage = countsErrorMessage (failure, locale)
const columnLimit = runColumnLimit ?? tooManyColumns (queryError)
@@ -263,7 +265,7 @@ const CountsResourceContent = (
exportInFlight.current = true
setExporting (true)
setExportError (false)
setExportError (null)
try
{
const axes = [...readCountState<CountsAxis[]> (params, 'rows', []),
@@ -291,9 +293,9 @@ const CountsResourceContent = (
handle={handle} controls={controls}/> })
}
catch
catch (failure)
{
setExportError (true)
setExportError ({ failure })
}
finally
{
@@ -344,7 +346,7 @@ const CountsResourceContent = (
return (
<li key={definition}>
<PrefetchLink
to={`/counts/${target}?${next}`}
to={`${countsResourcePath (target)}?${next}`}
aria-current={active ? 'page' : undefined}
className={cn ('block py-1', active && 'font-bold underline')}>
{label (definition)}
@@ -385,16 +387,20 @@ const CountsResourceContent = (
{label (`exportFormats.${format}`)}</Button>)}
</div>}/>}
{exportError && <p role="alert">{label ('common:fetchError')}</p>}
{exportError != null && <p role="alert">
{label ('errors.exportStart')}<br/>
{countsErrorMessage (exportError.failure, locale).text}</p>}
</div>
{columnLimit != null ? <div role="alert">
<p>{label ('columnLimit.title')}</p>
<p>{label ('columnLimit.message', { max: columnLimit })}</p>
</div> : ((definitionError || errorFlg || runError)
&& <p role="alert" lang={failureMessage.language}>{failureMessage.text}</p>)}
&& <p role="alert" lang={failureMessage.language}
className="whitespace-pre-line">{failureMessage.text}</p>)}
<div className="relative flex min-h-0 flex-1 flex-col overflow-hidden"
aria-busy={waiting}>
<div inert={waiting} className="flex min-h-0 flex-1 flex-col">
<div inert={waiting} aria-hidden={running || undefined}
className={cn ('flex min-h-0 flex-1 flex-col', running && 'invisible')}>
{mounted && definition != null && data != null && <>
<p className="my-1 flex flex-wrap items-baseline gap-x-2 text-sm">
<span>{label (resultMode === 'summary' ? 'outputRows' : 'outputTotal', { value: data.outputCount ?? '—' })}</span>
@@ -413,10 +419,12 @@ const CountsResourceContent = (
</>}
</div>
{waiting && <div role="status" aria-live="polite"
className="pointer-events-none absolute right-2 top-2">
className="pointer-events-none absolute inset-0 flex items-center justify-center">
<span className="rounded border border-slate-300 bg-white px-2 py-1
text-sm text-slate-900 dark:border-slate-600
inline-flex items-center gap-2 text-sm text-slate-900 dark:border-slate-600
dark:bg-slate-950 dark:text-slate-100">
<LoaderCircle aria-hidden className={cn (
'h-5 w-5', !motion.reduced && 'animate-spin')}/>
{label ('shared:messages.loadingEllipsis')}</span>
</div>}
</div>
@@ -428,11 +436,12 @@ const CountsResourceContent = (
const CountsResourcePage = ({ resource }: { resource: CountsResource }) => {
const locale = useLocale ()
const { data, isError: errorFlg } = useQuery ({
const { data, error, isError: errorFlg } = useQuery ({
queryKey: countsKeys.definition (resource, locale),
queryFn: () => fetchCountsDefinition (resource, locale), staleTime: Infinity })
return <CountsDefinitionContext.Provider value={data ?? null}>
<CountsResourceContent resource={resource} definition={data} definitionError={errorFlg}/>
<CountsResourceContent resource={resource} definition={data}
definitionError={errorFlg} definitionFailure={error}/>
</CountsDefinitionContext.Provider>
}