このコミットが含まれているのは:
2026-10-26 19:16:19 +09:00
コミット c61d9d76e3
12個のファイルの変更、138行の追加、54行の削除
+19 -19
ファイルの表示
@@ -17,11 +17,12 @@ import { Button } from '@/components/ui/button'
import { countsRequestParams, defaultCountsMode, exportCounts, fieldsForCounts, fetchCounts,
isCountsState, normaliseCountsParams, selectedCountColumns } from '@/lib/counts'
import { countsExportLabels } from '@/lib/countsDisplay'
import { CountsDefinitionContext, countsAxisFields, countsResources, countsFieldLabel,
import { CountsDefinitionContext, countsAxisFields, countsAxisLabel,
countsResources, countsFieldLabel,
fetchCountsDefinition, readCountsJSON, useCountsDefinition }
from '@/lib/countsDefinitions'
import { countsKeys } from '@/lib/queryKeys'
import { cn, inputClass } from '@/lib/utils'
import { cn } from '@/lib/utils'
import { useClientMotion } from '@/lib/useClientMotion'
import { useLocale } from '@/lib/useLocale'
import { useUITranslation } from '@/lib/useUITranslation'
@@ -54,7 +55,6 @@ const CountsResourceContent = ({ resource }: { resource: CountsResource }) => {
const state = isCountsState (params)
const cacheParams = new URLSearchParams (request)
cacheParams.set ('definition', state ? 'latest' : 'history')
const [format, setFormat] = useState<'json' | 'csv'> ('json')
const [exporting, setExporting] = useState (false)
const [exportError, setExportError] = useState (false)
const { data, isPending, isError, isPlaceholderData, refetch } = useQuery ({
@@ -100,20 +100,25 @@ const CountsResourceContent = ({ resource }: { resource: CountsResource }) => {
if (key === 'sort') next.delete ('page')
setParams (next)
}
const download = async () => {
const download = async (format: 'csv' | 'json') => {
setExporting (true)
setExportError (false)
try
{
const axes = [...readCountsJSON<CountsAxis[]> (params, 'rows', []),
...readCountsJSON<CountsAxis[]> (params, 'cols', [])]
const fields = mode === 'summary' ? [
...[...readCountsJSON<CountsAxis[]> (params, 'rows', []),
...readCountsJSON<CountsAxis[]> (params, 'cols', [])].map (axis => ({
...axes.map (axis => ({
key: axis.field, label: countsFieldLabel (axis.field) })),
{ key: 'count', label: 'count' }]
: selectedCountColumns (resource, params, definition).map (column =>
fieldsForCounts (resource, definition).find (field => field.key === column)!)
const headers = mode === 'summary'
? [...axes.map (axis => countsAxisLabel (axis, definition, locale).text),
message ('count').text]
: fields.map (field => message (field.label).text)
await exportCounts (resource, params, locale, format,
fields.map (field => field.key), fields.map (field => message (field.label).text),
fields.map (field => field.key), headers,
{ ...countsExportLabels (resource, locale, definition),
...Object.fromEntries (countsAxisFields (definition, !state).map (field => [
field.key, message (field.label).text])), count: message ('count').text },
@@ -193,20 +198,15 @@ const CountsResourceContent = ({ resource }: { resource: CountsResource }) => {
execute={next => {
if (next.toString () === canonical) void refetch ()
else setParams (next)
}} actions={<>
<select value={format} aria-label={message ('exportFormat').text}
lang={message ('exportFormat').language}
className={inputClass (false, 'w-auto py-1')}
onChange={event => setFormat (event.target.value as 'json' | 'csv')}>
{(['json', 'csv'] as const).map (value => (
<option key={value} value={value}>
{message (`exportFormats.${value}`).text}</option>))}
</select>
<Button type="button" variant="outline" size="sm"
}} actions={<div className="ml-auto flex shrink-0 items-center gap-2">
{(['csv', 'json'] as const).map (format => <Button key={format}
type="button" variant="outline" size="sm"
disabled={data == null || isError || isPlaceholderData
|| exporting || !params.get ('execution_at')}
onClick={() => void download ()}>{label ('export')}</Button>
</>}/>
onClick={() => void download (format)}>
{label (`exportFormats.${format}`)}</Button>)}
</div>}/>
{exportError && <p role="alert">{label ('common:fetchError')}</p>}
</div>
{isPending && <p>{label ('shared:messages.loadingEllipsis')}</p>}