このコミットが含まれているのは:
2026-10-27 01:02:53 +09:00
コミット 53ea010f82
7個のファイルの変更、160行の追加、94行の削除
+21 -27
ファイルの表示
@@ -1,4 +1,4 @@
import { useQuery } from '@tanstack/react-query'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { useEffect, useRef, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
@@ -38,15 +38,10 @@ import type { CountsResource } from '@/lib/counts'
const CountsResourceContent = ({ resource }: { resource: CountsResource }) => {
const definition = useCountsDefinition ()
const queryClient = useQueryClient ()
const [source, setParams] = useSearchParams ()
const params = normaliseCountsParams (resource, source, definition)
const canonical = params.toString ()
const controlParams = new URLSearchParams (params)
controlParams.delete ('nested_json')
controlParams.delete ('order')
controlParams.delete ('row_numbers')
controlParams.set ('columns', selectedCountColumns (resource, params, definition)
.filter (column => column !== 'thumbnail').sort ().join (' '))
const request = countsRequestParams (params)
const [open, setOpen] = useState (false)
const [sidebarOpen, setSidebarOpen] = useState (false)
@@ -65,8 +60,10 @@ const CountsResourceContent = ({ resource }: { resource: CountsResource }) => {
const [exporting, setExporting] = useState (false)
const exportInFlight = useRef (false)
const [exportError, setExportError] = useState (false)
const { data, isPending, isError, isPlaceholderData, refetch } = useQuery ({
const { data, dataUpdatedAt, isFetching, isPending, isError,
isPlaceholderData, refetch } = useQuery ({
enabled: params.has ('timezone'),
staleTime: params.has ('execution_at') ? 5 * 60 * 1000 : 0,
queryKey: countsKeys.index (resource, cacheParams.toString (), locale),
queryFn: () => fetchCounts (resource, params, locale), retry: false,
placeholderData: (previous, query) => {
@@ -80,25 +77,22 @@ const CountsResourceContent = ({ resource }: { resource: CountsResource }) => {
} })
useEffect (() => {
if (source.toString () !== canonical)
setParams (canonical, { replace: true })
}, [canonical, source, setParams])
useEffect (() => {
const timezone = Intl.DateTimeFormat ().resolvedOptions ().timeZone
if (params.get ('timezone') !== timezone)
const next = new URLSearchParams (canonical)
if (!next.has ('timezone'))
next.set ('timezone', Intl.DateTimeFormat ().resolvedOptions ().timeZone)
if (data?.executionAt != null && !isPlaceholderData && !isFetching && !isError
&& params.has ('timezone') && !params.has ('execution_at'))
{
const next = new URLSearchParams (canonical)
next.set ('timezone', timezone)
setParams (next, { replace: true })
}
else if (data?.executionAt != null && !isPlaceholderData && !params.has ('execution_at'))
{
const next = new URLSearchParams (canonical)
next.set ('execution_at', data.executionAt)
setParams (next, { replace: true })
// Pin the response under its server-confirmed time before changing the URL.
queryClient.setQueryData (
countsKeys.index (resource, countsRequestParams (next).toString (), locale),
data, { updatedAt: dataUpdatedAt })
}
}, [canonical, data, isPlaceholderData, params, setParams])
if (source.toString () !== next.toString ())
setParams (next, { replace: true })
}, [canonical, data, dataUpdatedAt, isError, isFetching, isPlaceholderData,
locale, params, queryClient, resource, setParams, source])
const update = (key: string, value: string, extra?: Record<string, string>) => {
@@ -166,8 +160,8 @@ const CountsResourceContent = ({ resource }: { resource: CountsResource }) => {
<Button type="button" variant="ghost" size="sm" className="shrink-0 md:hidden"
aria-expanded={sidebarOpen} aria-controls="counts-targets"
onClick={() => setSidebarOpen (!sidebarOpen)}>{label ('target')}</Button>
<SidebarComponent sidebarKey="counts" maxWidth={224}
className={cn ('md:block md:max-w-56', !sidebarOpen && 'hidden')}>
<SidebarComponent sidebarKey="counts" maxWidthRatio={.5}
className={cn ('md:block', !sidebarOpen && 'hidden')}>
<nav id="counts-targets" aria-label={message ('target').text}
lang={message ('target').language}>
<h2 className="mb-2 font-semibold">{label ('target')}</h2>
@@ -212,7 +206,7 @@ const CountsResourceContent = ({ resource }: { resource: CountsResource }) => {
<MainArea className="flex min-h-0 min-w-0 flex-col p-2">
<PageTitle className="mb-1 text-base">{label (`titles.${resource}`)}</PageTitle>
<div className="shrink-0">
<CountsControls key={`${resource}:${controlParams}`}
<CountsControls
resource={resource} applied={params}
open={open} setOpen={setOpen}
reorder={value => update ('order', value)}