Merge branch 'fix/day-picker-ordinis-styles' into 'main'

fix(admin-ui): /audit — стили DayPicker + UserPicker fallback на TextInput

See merge request 2-6/2-6-4/terravault/ordinis!264
This commit is contained in:
Александр Зимин
2026-05-25 14:51:12 +00:00
3 changed files with 134 additions and 4 deletions
@@ -269,7 +269,7 @@ export function PeriodFilter({ value, onChange, label, disabled }: PeriodFilterP
mode="range" mode="range"
selected={dpRange} selected={dpRange}
onSelect={handleSelect} onSelect={handleSelect}
numberOfMonths={2} numberOfMonths={1}
showOutsideDays showOutsideDays
weekStartsOn={1} weekStartsOn={1}
className="ord-day-picker" className="ord-day-picker"
@@ -2,7 +2,7 @@ import * as React from 'react'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { CaretDownIcon, MagnifyingGlassIcon, UserIcon, XIcon } from '@phosphor-icons/react' import { CaretDownIcon, MagnifyingGlassIcon, UserIcon, XIcon } from '@phosphor-icons/react'
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from 'cmdk' import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from 'cmdk'
import { Popover, PopoverTrigger, PopoverContent, FieldLabel } from '@/ui' import { Popover, PopoverTrigger, PopoverContent, FieldLabel, TextInput } from '@/ui'
import { useAllUsers, type UserListItem } from '@/api/queries' import { useAllUsers, type UserListItem } from '@/api/queries'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
@@ -38,11 +38,32 @@ export function UserPicker({ value, onChange, label, placeholder, disabled }: Us
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const usersQ = useAllUsers() const usersQ = useAllUsers()
// Fallback на TextInput если cache пустой или endpoint вернул 403/404 (юзер
// без INTERNAL scope, либо writer был только-что рестартован и cache не
// прогрелся). Без этого user-фильтр становится недоступен — раньше тут был
// TextInput, не лишаем юзера возможности вписать UUID руками.
const usersList = usersQ.data ?? []
const fallbackToTextInput =
!usersQ.isLoading && !usersQ.isError && usersList.length === 0
const usersBySub = useMemo(() => { const usersBySub = useMemo(() => {
const m = new Map<string, UserListItem>() const m = new Map<string, UserListItem>()
for (const u of usersQ.data ?? []) m.set(u.sub, u) for (const u of usersList) m.set(u.sub, u)
return m return m
}, [usersQ.data]) }, [usersList])
if (fallbackToTextInput) {
return (
<TextInput
label={label}
value={value ?? ''}
onChange={(e) => onChange(e.target.value || undefined)}
placeholder="UUID или username"
hint="Список юзеров пуст — введите вручную"
disabled={disabled}
/>
)
}
const selected = value ? usersBySub.get(value) : undefined const selected = value ? usersBySub.get(value) : undefined
const triggerLabel = useMemo(() => { const triggerLabel = useMemo(() => {
+109
View File
@@ -443,3 +443,112 @@ body {
animation: none; animation: none;
} }
} }
/* === react-day-picker — кастомизация под ordinis design system.
* Без этих overrides DayPicker рендерится дефолтными rdp-* стилями
* (большие квадратные ячейки, нет hover/selected состояний под наш
* accent). Класс `.ord-day-picker` ставится на корень DayPicker'а
* в DateRangePicker и PeriodFilter. === */
.ord-day-picker {
--rdp-day-width: 2rem;
--rdp-day-height: 2rem;
--rdp-day_button-width: 2rem;
--rdp-day_button-height: 2rem;
--rdp-day_button-border-radius: 6px;
--rdp-months-gap: 1rem;
--rdp-nav_button-height: 1.75rem;
--rdp-nav_button-width: 1.75rem;
--rdp-weekday-text-transform: lowercase;
--rdp-weekday-padding: 0.25rem 0;
--rdp-month_caption-font: 600 0.875rem/1.25 'Inter', sans-serif;
--rdp-day-font: 400 0.8125rem/1 'JetBrains Mono', monospace;
--rdp-weekday-font: 500 0.7rem/1 'Inter', sans-serif;
--rdp-accent-color: var(--color-accent);
--rdp-accent-background-color: var(--color-accent-bg);
font-family: 'Inter', sans-serif;
color: var(--color-ink);
}
/* Multi-month layout (если когда-то нужно): 2 столбца side-by-side
* вместо wrap'а вниз. */
.ord-day-picker .rdp-months {
flex-wrap: nowrap;
}
.ord-day-picker .rdp-month_caption {
padding: 0 0 0.5rem;
color: var(--color-ink);
}
.ord-day-picker .rdp-weekday {
color: var(--color-mute);
text-transform: lowercase;
font-weight: 500;
}
.ord-day-picker .rdp-day_button {
border-radius: 6px;
transition: background-color 120ms, color 120ms;
}
.ord-day-picker .rdp-day_button:hover:not(:disabled):not([aria-disabled='true']) {
background-color: var(--color-surface-2);
color: var(--color-ink);
}
.ord-day-picker .rdp-day.rdp-outside .rdp-day_button {
color: var(--color-mute);
opacity: 0.55;
}
.ord-day-picker .rdp-day.rdp-today .rdp-day_button {
font-weight: 600;
outline: 1px solid var(--color-line-2, var(--color-line));
outline-offset: -2px;
}
/* Selected range */
.ord-day-picker .rdp-day.rdp-selected .rdp-day_button {
background-color: var(--color-accent);
color: var(--color-on-accent, white);
font-weight: 500;
}
.ord-day-picker .rdp-day.rdp-range_middle .rdp-day_button {
background-color: var(--color-accent-bg);
color: var(--color-accent);
border-radius: 0;
}
.ord-day-picker .rdp-day.rdp-range_start .rdp-day_button {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.ord-day-picker .rdp-day.rdp-range_end .rdp-day_button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.ord-day-picker .rdp-day.rdp-range_start.rdp-range_end .rdp-day_button {
border-radius: 6px;
}
/* Nav buttons */
.ord-day-picker .rdp-button_previous,
.ord-day-picker .rdp-button_next {
border-radius: 6px;
color: var(--color-accent);
transition: background-color 120ms;
}
.ord-day-picker .rdp-button_previous:hover,
.ord-day-picker .rdp-button_next:hover {
background-color: var(--color-surface-2);
}
.ord-day-picker .rdp-button_previous:disabled,
.ord-day-picker .rdp-button_next:disabled {
opacity: 0.35;
cursor: not-allowed;
}