From f44a436e20514808dfccb47dd696e876beeb521a Mon Sep 17 00:00:00 2001 From: miteruzo Date: Mon, 26 Oct 2026 19:31:00 +0900 Subject: [PATCH] #434 --- frontend/src/App.tsx | 2 +- .../counts/CountsAdditionalConditions.tsx | 33 +++++++------ .../src/components/counts/CountsControls.tsx | 49 +++++++++++++++---- .../src/components/counts/CountsFieldTree.tsx | 4 +- .../src/components/counts/CountsResults.tsx | 9 +++- .../components/counts/CountsSortHeader.tsx | 2 +- .../components/dialogues/DialogueProvider.tsx | 5 +- frontend/src/entry-server.tsx | 4 +- frontend/src/lib/dialogues/useDialogue.ts | 1 + frontend/src/lib/locales.ts | 17 +++---- frontend/src/locales/ja/counts.json | 3 +- frontend/src/pages/CountsResourcePage.tsx | 7 +++ 12 files changed, 95 insertions(+), 41 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 1bc73e8..1b0deab 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -409,7 +409,7 @@ const LocaleRoute: FC = () => { void i18n.changeLanguage (activeLocale) }, [activeLocale, i18n]) - const redirect = legacyLocalePath (location) + const redirect = legacyLocalePath (location, pathname => resolvePageRoute (pathname) != null) if (!(isLocaleCode (locale)) && redirect == null) return if (isError && data == null) diff --git a/frontend/src/components/counts/CountsAdditionalConditions.tsx b/frontend/src/components/counts/CountsAdditionalConditions.tsx index 61f2487..9f58347 100644 --- a/frontend/src/components/counts/CountsAdditionalConditions.tsx +++ b/frontend/src/components/counts/CountsAdditionalConditions.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' import DateTimeField from '@/components/common/DateTimeField' import { Button } from '@/components/ui/button' @@ -9,18 +9,25 @@ import useDialogue from '@/lib/dialogues/useDialogue' import { inputClass } from '@/lib/utils' import { useUITranslation } from '@/lib/useUITranslation' +import type { DialogueFormControls } from '@/lib/dialogues/useDialogue' import type { CountsCondition, CountsDefinition } from '@/lib/countsDefinitions' -const ConditionEditor = ({ definition, initial, save }: { - definition: CountsDefinition; initial?: CountsCondition - save: (condition: CountsCondition) => void }, +const ConditionEditor = ( + { definition, initial, controls, save }: { + definition: CountsDefinition; initial?: CountsCondition + controls: DialogueFormControls + save: (condition: CountsCondition) => void }, ) => { const { label, message } = useUITranslation ('counts') const fields = countsFilterFields (definition) const [field, setField] = useState (initial?.field ?? 'resource_id') const [operator, setOperator] = useState (initial?.operator ?? 'eq') const operators = definition.fields.find (item => item.key === field)?.operators ?? [] + useEffect (() => { + controls.setActions ([{ label: label (initial == null ? 'add' : 'shared:actions.edit'), + onSelect: () => save ({ ...initial, field, operator }) }]) + }, [controls, field, operator, initial, label, save]) return
-
} @@ -48,14 +53,14 @@ const CountsAdditionalConditions = ({ params, update }: { const conditions = readCountsJSON (params, 'conditions', []) const write = (next: CountsCondition[]) => update ('conditions', JSON.stringify (next)) const edit = (index?: number) => { - void dialogue.form ({ title: label ('additionalConditions'), body: controls => ( - { - write (index == null ? [...conditions, condition] - : conditions.map ((item, position) => position === index ? condition : item)) - controls.close () - }}/>) }) + const initial = index == null ? undefined : conditions[index] + const save = (condition: CountsCondition) => { + write (index == null ? [...conditions, condition] + : conditions.map ((item, position) => position === index ? condition : item)) + } + dialogue.form ({ title: label ('additionalConditions'), footerLayout: 'inline', + body: controls => }) } const patch = (index: number, change: Partial) => write ( conditions.map ((item, position) => position === index ? { ...item, ...change } : item)) diff --git a/frontend/src/components/counts/CountsControls.tsx b/frontend/src/components/counts/CountsControls.tsx index f9b95a7..b9bc956 100644 --- a/frontend/src/components/counts/CountsControls.tsx +++ b/frontend/src/components/counts/CountsControls.tsx @@ -9,7 +9,9 @@ import FormField from '@/components/common/FormField' import HelpPopover from '@/components/common/HelpPopover' import TagInput from '@/components/common/TagInput' import { Button } from '@/components/ui/button' -import { countsUseLatest, normaliseCountsParams } from '@/lib/counts' +import { countsUseLatest, fieldsForCounts, normaliseCountsParams, + selectedCountColumns } from '@/lib/counts' +import { useCountsDefinition } from '@/lib/countsDefinitions' import { useClientMotion } from '@/lib/useClientMotion' import { inputClass } from '@/lib/utils' import { useUITranslation } from '@/lib/useUITranslation' @@ -21,21 +23,36 @@ import type { CountsResource } from '@/lib/counts' const CountsControls = ( { resource, applied, open, setOpen, execute, displayJSON, displayRowNumbers, - reorder, actions }: { + displayThumbnail, reorder, actions }: { resource: CountsResource; applied: URLSearchParams; open: boolean setOpen: (open: boolean) => void execute: (params: URLSearchParams) => void displayJSON: (enabled: boolean) => void displayRowNumbers: (enabled: boolean) => void + displayThumbnail: (enabled: boolean) => void reorder: (value: string) => void; actions?: ReactNode }, ) => { const { label, message } = useUITranslation ('counts') + const definition = useCountsDefinition () const id = useId () const motion = useClientMotion () const [draft, setDraft] = useState (() => new URLSearchParams (applied)) const order = applied.get ('order') const nestedJSON = applied.get ('nested_json') const rowNumbers = applied.get ('row_numbers') + const thumbnail = selectedCountColumns (resource, applied, definition).includes ('thumbnail') + const details = applied.get ('mode') === 'details' + const hasNested = details && fieldsForCounts (resource, definition) + .some (field => field.children != null) + useEffect (() => { + setDraft (previous => { + const next = new URLSearchParams (previous) + const columns = selectedCountColumns (resource, previous, definition) + .filter (column => column !== 'thumbnail') + next.set ('columns', (thumbnail ? ['thumbnail', ...columns] : columns).join (',')) + return next + }) + }, [thumbnail, resource, definition]) useEffect (() => { setDraft (previous => { const next = new URLSearchParams (previous) @@ -95,14 +112,32 @@ const CountsControls = ( {['summary', 'details'].map (value => )} + {actions} + +
+ {label ('displaySettings')} {actions} - + + {details && resource === 'posts' && } + {hasNested && } +
@@ -111,12 +146,6 @@ const CountsControls = ( {mode === 'summary' ? : <> {label ('output')} - { update (key, value) if (key === 'order') reorder (value) diff --git a/frontend/src/components/counts/CountsFieldTree.tsx b/frontend/src/components/counts/CountsFieldTree.tsx index f8fcd26..90151d6 100644 --- a/frontend/src/components/counts/CountsFieldTree.tsx +++ b/frontend/src/components/counts/CountsFieldTree.tsx @@ -109,9 +109,11 @@ const CountsFieldTree = ( update ('children', countChildPaths (fields).filter ( key => key.includes ('.') && selected.has (key)).join (',')) } + const visibleFields = parent === '' + ? fields.filter (field => field.key !== 'thumbnail') : fields return ( <> - ) } diff --git a/frontend/src/components/counts/CountsResults.tsx b/frontend/src/components/counts/CountsResults.tsx index c1ed84c..c60112b 100644 --- a/frontend/src/components/counts/CountsResults.tsx +++ b/frontend/src/components/counts/CountsResults.tsx @@ -50,14 +50,19 @@ const CountsResults = ( switch (column) { case 'resource_id': - if (resource === 'nico_tags') return - {row.resourceId} + if (resource === 'nico_tags') return row.name ? ( + + {row.resourceId}) : row.resourceId if (resource === 'tag_groups') return {row.resourceId} return {row.resourceId} + case 'name': + return resource === 'nico_tags' && row.name ? ( + + {value}) : value case 'thumbnail': { const url = countsImageURL (row.thumbnailBase) diff --git a/frontend/src/components/counts/CountsSortHeader.tsx b/frontend/src/components/counts/CountsSortHeader.tsx index d14b5e8..f19a7bf 100644 --- a/frontend/src/components/counts/CountsSortHeader.tsx +++ b/frontend/src/components/counts/CountsSortHeader.tsx @@ -87,7 +87,7 @@ const CountsSortHeader = ( }}> {label.text} {priority >= 0 && - {direction === 'asc' ? '↑' : '↓'}{sorts.length > 1 && priority + 1}} + {direction === 'asc' ? '▲' : '▼'}{sorts.length > 1 && priority + 1}} ) diff --git a/frontend/src/components/dialogues/DialogueProvider.tsx b/frontend/src/components/dialogues/DialogueProvider.tsx index d9185a5..e277f41 100644 --- a/frontend/src/components/dialogues/DialogueProvider.tsx +++ b/frontend/src/components/dialogues/DialogueProvider.tsx @@ -237,7 +237,10 @@ const DialogueProvider: FC = ({ children }) => { ))}
-
+
button]:w-auto' : 'flex-col')}>