fix(admin-ui): audit — компактнее панель фильтров + popover календаря

1. DateRangePicker popover. PopoverContent имел default w-72 (288px),
   а DayPicker с numberOfMonths={2} требует ~560px. Из-за этого второй
   месяц переносился под первый, popover вылазил поверх таблицы аудита
   и кнопок «Сбросить/Применить». Override на p-3 w-auto max-w-[min(95vw,640px)]
   — два месяца теперь side-by-side, popover floats как должно.

2. Панель фильтров /audit. Было: 3-col grid с 4 input'ами + DateRange
   span'ит 2 cols на отдельной строке. Стало: на xl (≥1280px) все 5
   полей inline в одну строку (lg → 3 cols, sm → 2, mobile → 1).
   Padding p-4 space-y-3 → p-3 space-y-2. Убран border-t на action row
   — визуальный шум.

Изменения только на /audit (date-range-picker — shared компонент,
но используется только в audit-фильтрах сейчас; рендер других мест
не страдает — popover просто шире, w-auto не ломает узкий контент).
This commit is contained in:
Zimin A.N.
2026-05-25 15:36:32 +03:00
parent 712866a178
commit 2548f24aff
2 changed files with 31 additions and 27 deletions
+26 -26
View File
@@ -373,8 +373,11 @@ function FilterPanel({
} }
return ( return (
<div className="bg-surface border border-line rounded-lg p-4 space-y-3"> <div className="bg-surface border border-line rounded-lg p-3 space-y-2">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3"> {/* На xl+ (≥1280px) все 5 фильтров inline в одну строку; lg → 3 cols,
sm → 2 cols, mobile → 1. Раньше DateRangePicker занимал md:col-span-2
→ шла на отдельную строку даже на широких экранах. */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-2">
<SingleSelect <SingleSelect
label={t('audit.filter.dictionary')} label={t('audit.filter.dictionary')}
options={dictionaryOptions} options={dictionaryOptions}
@@ -399,31 +402,28 @@ function FilterPanel({
onChange={(e) => set('businessKey', e.target.value || undefined)} onChange={(e) => set('businessKey', e.target.value || undefined)}
onKeyDown={handleEnter} onKeyDown={handleEnter}
/> />
{/* DateRangePicker заменяет пару отдельных DatePicker'ов — один {/* DateRangePicker — один popover с двумя месяцами, выбор start/end
* popover с двумя месяцами, выбор start/end в одном UI. ISO sтроки в одном UI. ISO строки с local TZ offset формируются здесь чтобы
* c local TZ offset формируются здесь, чтобы backend получал backend получал правильный bounds (T00:00:00 для from, T23:59:59 для to). */}
* правильный bounds (T00:00:00 для from, T23:59:59 для to). */} <DateRangePicker
<div className="md:col-span-2"> label={t('audit.filter.range', { defaultValue: 'Период' })}
<DateRangePicker value={{ from: fromDate ?? undefined, to: toDate ?? undefined }}
label={t('audit.filter.range', { defaultValue: 'Период' })} onChange={({ from, to }) => {
value={{ from: fromDate ?? undefined, to: toDate ?? undefined }} const fromIso = from
onChange={({ from, to }) => { ? `${from.getFullYear()}-${String(from.getMonth() + 1).padStart(2, '0')}-${String(
const fromIso = from from.getDate(),
? `${from.getFullYear()}-${String(from.getMonth() + 1).padStart(2, '0')}-${String( ).padStart(2, '0')}T00:00:00${localTzOffset(from)}`
from.getDate(), : undefined
).padStart(2, '0')}T00:00:00${localTzOffset(from)}` const toIso = to
: undefined ? `${to.getFullYear()}-${String(to.getMonth() + 1).padStart(2, '0')}-${String(
const toIso = to to.getDate(),
? `${to.getFullYear()}-${String(to.getMonth() + 1).padStart(2, '0')}-${String( ).padStart(2, '0')}T23:59:59${localTzOffset(to)}`
to.getDate(), : undefined
).padStart(2, '0')}T23:59:59${localTzOffset(to)}` onChange({ ...filters, from: fromIso, to: toIso })
: undefined }}
onChange({ ...filters, from: fromIso, to: toIso }) />
}}
/>
</div>
</div> </div>
<div className="flex justify-end gap-2 pt-2 border-t border-line"> <div className="flex justify-end gap-2">
<Button type="button" variant="secondary" onClick={onReset}> <Button type="button" variant="secondary" onClick={onReset}>
{t('audit.filter.reset')} {t('audit.filter.reset')}
</Button> </Button>
@@ -161,7 +161,11 @@ export function DateRangePicker({
)} )}
</button> </button>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent align="start" className="p-3"> {/* PopoverContent default w-72 (288px) — DayPicker mode=range
numberOfMonths={2} требует ~560px, иначе второй месяц переносится
под первый и popover вылазит поверх контента ниже. На <620px
(mobile/narrow) — один месяц чтобы помещалось в w-72-aware viewport. */}
<PopoverContent align="start" className="p-3 w-auto max-w-[min(95vw,640px)]">
<PopoverPrimitive.Close className="hidden" /> <PopoverPrimitive.Close className="hidden" />
<React.Suspense <React.Suspense
fallback={ fallback={