このコミットが含まれているのは:
2026-10-26 12:44:22 +09:00
コミット f91f9d0eb5
23個のファイルの変更、637行の追加、316行の削除
+50 -10
ファイルの表示
@@ -7,6 +7,7 @@ import CountsControls from '@/components/counts/CountsControls'
import CountsSummaryResults from '@/components/counts/CountsSummaryResults'
import CountsResults from '@/components/counts/CountsResults'
import CountsTableLayout from '@/components/counts/CountsTableLayout'
import AnimatedCollapse from '@/components/common/AnimatedCollapse'
import PageHead from '@/components/common/PageHead'
import PageTitle from '@/components/common/PageTitle'
import Pagination from '@/components/common/Pagination'
@@ -16,10 +17,12 @@ import { Button } from '@/components/ui/button'
import { countsRequestParams, exportCounts, fieldsForCounts, fetchCounts,
isCountsState, normaliseCountsParams, selectedCountColumns } from '@/lib/counts'
import { countsExportLabels } from '@/lib/countsDisplay'
import { countsAxisFields, countsResources, countsFieldLabel, readCountsJSON }
import { CountsDefinitionContext, countsAxisFields, countsResources, countsFieldLabel,
fetchCountsDefinition, readCountsJSON, useCountsDefinition }
from '@/lib/countsDefinitions'
import { countsKeys } from '@/lib/queryKeys'
import { cn, inputClass } from '@/lib/utils'
import { useClientMotion } from '@/lib/useClientMotion'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
@@ -28,7 +31,8 @@ import type { CountsAxis } from '@/lib/countsDefinitions'
import type { CountsResource } from '@/lib/counts'
const CountsResourcePage = ({ resource }: { resource: CountsResource }) => {
const CountsResourceContent = ({ resource }: { resource: CountsResource }) => {
const definition = useCountsDefinition ()
const [source, setParams] = useSearchParams ()
const params = normaliseCountsParams (resource, source)
const canonical = params.toString ()
@@ -38,6 +42,11 @@ const CountsResourcePage = ({ resource }: { resource: CountsResource }) => {
const request = countsRequestParams (params)
const [open, setOpen] = useState (false)
const [sidebarOpen, setSidebarOpen] = useState (false)
const [expanded, setExpanded] = useState (() => new Set<string> ([resource]))
const motion = useClientMotion ()
useEffect (() => {
setExpanded (previous => new Set ([...previous, resource]))
}, [resource])
const locale = useLocale ()
const { label, message } = useUITranslation ('counts')
const mode = params.get ('mode') ?? 'summary'
@@ -57,7 +66,8 @@ const CountsResourcePage = ({ resource }: { resource: CountsResource }) => {
&& previousParams.get ('mode') === mode
&& previousParams.get ('rows') === params.get ('rows')
&& previousParams.get ('cols') === params.get ('cols')
&& previousParams.get ('definition') === cacheParams.get ('definition')) ? previous : undefined
&& previousParams.get ('definition') === cacheParams.get ('definition'))
? previous : undefined
} })
useEffect (() => {
@@ -99,13 +109,14 @@ const CountsResourcePage = ({ resource }: { resource: CountsResource }) => {
...readCountsJSON<CountsAxis[]> (params, 'cols', [])].map (axis => ({
key: axis.field, label: countsFieldLabel (axis.field) })),
{ key: 'count', label: 'count' }]
: selectedCountColumns (resource, params).map (column =>
fieldsForCounts (resource).find (field => field.key === column)!)
: selectedCountColumns (resource, params, definition).map (column =>
fieldsForCounts (resource, definition).find (field => field.key === column)!)
await exportCounts (resource, params, locale, format,
fields.map (field => field.key), fields.map (field => message (field.label).text),
{ ...countsExportLabels (resource, locale),
...Object.fromEntries (countsAxisFields (resource, !state).map (field => [
field.key, message (field.label).text])), count: message ('count').text })
{ ...countsExportLabels (resource, locale, definition),
...Object.fromEntries (countsAxisFields (definition, !state).map (field => [
field.key, message (field.label).text])), count: message ('count').text },
() => setExportError (true))
}
catch
@@ -133,11 +144,26 @@ const CountsResourcePage = ({ resource }: { resource: CountsResource }) => {
<h2 className="mb-2 font-semibold">{label ('target')}</h2>
<ul className="space-y-1">
{countsResources.map (target => <li key={target}>
<span>{label (`resources.${target}`)}</span>
<button type="button" className="flex w-full items-center gap-1 py-1 text-left
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500
dark:focus-visible:ring-blue-400"
aria-expanded={expanded.has (target)} aria-controls={`counts-definition-${target}`}
onClick={() => setExpanded (previous => {
const next = new Set (previous)
if (next.has (target)) next.delete (target)
else next.add (target)
return next
})}>
<span aria-hidden className="inline-block" style={{
transform: expanded.has (target) ? 'rotate(90deg)' : 'rotate(0deg)',
transition: `transform ${motion.duration ('collapse')}ms ${motion.easing}` }}>▸</span>
{label (`resources.${target}`)}
</button>
<AnimatedCollapse open={expanded.has (target)} id={`counts-definition-${target}`}>
<ul className="ml-3">
{['latest', 'history'].map (definition => {
const next = new URLSearchParams ({ definition, mode })
for (const key of ['timezone', 'at', 'execution_at'])
for (const key of ['timezone', 'at'])
if (params.get (key)) next.set (key, params.get (key)!)
const active = target === resource
&& params.get ('definition') === definition
@@ -148,6 +174,7 @@ const CountsResourcePage = ({ resource }: { resource: CountsResource }) => {
{label (definition)}</PrefetchLink></li>
})}
</ul>
</AnimatedCollapse>
</li>)}
</ul>
</nav>
@@ -197,4 +224,17 @@ const CountsResourcePage = ({ resource }: { resource: CountsResource }) => {
}
const CountsResourcePage = ({ resource }: { resource: CountsResource }) => {
const { label } = useUITranslation ('counts')
const { data, isError } = useQuery ({
queryKey: countsKeys.definition (resource),
queryFn: () => fetchCountsDefinition (resource), staleTime: Infinity })
if (data == null) return <p>{label (
isError ? 'common:fetchError' : 'shared:messages.loadingEllipsis')}</p>
return <CountsDefinitionContext.Provider value={data}>
<CountsResourceContent resource={resource}/>
</CountsDefinitionContext.Provider>
}
export default CountsResourcePage