feat(admin-ui): sonner Toast + TimeTravel polish (Stage 3.6+3.7)

This commit is contained in:
Александр Зимин
2026-05-11 09:11:21 +00:00
parent 057fd09589
commit e2f1c3527b
5 changed files with 74 additions and 1 deletions
+1
View File
@@ -43,6 +43,7 @@
"react-i18next": "^17.0.2", "react-i18next": "^17.0.2",
"react-leaflet": "^5.0.0", "react-leaflet": "^5.0.0",
"react-oidc-context": "^3.3.1", "react-oidc-context": "^3.3.1",
"sonner": "^2.0.7",
"tailwind-merge": "^3.6.0", "tailwind-merge": "^3.6.0",
"tw-animate-css": "^1.4.0" "tw-animate-css": "^1.4.0"
}, },
+14
View File
@@ -104,6 +104,9 @@ importers:
react-oidc-context: react-oidc-context:
specifier: ^3.3.1 specifier: ^3.3.1
version: 3.3.1(oidc-client-ts@3.5.0)(react@19.2.5) version: 3.3.1(oidc-client-ts@3.5.0)(react@19.2.5)
sonner:
specifier: ^2.0.7
version: 2.0.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
tailwind-merge: tailwind-merge:
specifier: ^3.6.0 specifier: ^3.6.0
version: 3.6.0 version: 3.6.0
@@ -2213,6 +2216,12 @@ packages:
siginfo@2.0.0: siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
sonner@2.0.7:
resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
peerDependencies:
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
source-map-js@1.2.1: source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -4300,6 +4309,11 @@ snapshots:
siginfo@2.0.0: {} siginfo@2.0.0: {}
sonner@2.0.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
dependencies:
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
source-map-js@1.2.1: {} source-map-js@1.2.1: {}
stackback@0.0.2: {} stackback@0.0.2: {}
+2 -1
View File
@@ -2,7 +2,7 @@ import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { RouterProvider, createRouter } from '@tanstack/react-router' import { RouterProvider, createRouter } from '@tanstack/react-router'
import { NStartUiProvider, DatePickerProvider, DATE_PICKER_LOCALE_RU } from '@/ui' import { NStartUiProvider, DatePickerProvider, DATE_PICKER_LOCALE_RU, Toaster } from '@/ui'
import { AuthProvider } from 'react-oidc-context' import { AuthProvider } from 'react-oidc-context'
import { routeTree } from './routeTree.gen' import { routeTree } from './routeTree.gen'
import { oidcAuthConfig } from '@/auth/oidcConfig' import { oidcAuthConfig } from '@/auth/oidcConfig'
@@ -36,6 +36,7 @@ createRoot(document.getElementById('root')!).render(
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<DatePickerProvider datePicker={DATE_PICKER_LOCALE_RU}> <DatePickerProvider datePicker={DATE_PICKER_LOCALE_RU}>
<RouterProvider router={router} /> <RouterProvider router={router} />
<Toaster />
</DatePickerProvider> </DatePickerProvider>
</QueryClientProvider> </QueryClientProvider>
</RequireAuth> </RequireAuth>
@@ -0,0 +1,52 @@
import { Toaster as SonnerToaster, toast } from 'sonner'
import { useTheme } from '@/stores/ThemeProvider'
/**
* Toast notification provider wraps sonner с our design tokens.
*
* Sonner = headless, кастомизируем через CSS variables. Параметризируется
* theme="light" | "dark" берётся из ThemeProvider, авто-switch на смену темы.
*
* Использование:
* <pre>
* import { toast } from '@/ui/components/toaster'
* toast.success('Запись сохранена')
* toast.error('Ошибка валидации', { description: '...' })
* toast.promise(mutateAsync(), {loading, success, error})
* </pre>
*
* Mount: <Toaster /> один раз в main.tsx внутри ThemeProvider.
*/
export function Toaster() {
const { resolved } = useTheme()
return (
<SonnerToaster
position="bottom-right"
richColors
closeButton
theme={resolved}
toastOptions={{
// Inline styles переопределяют sonner CSS — tokens из @theme.
// На light: surface bg + ink text + line border.
// На dark: re-uses tokens автомат (другие значения cssvar).
style: {
background: 'var(--color-surface)',
color: 'var(--color-ink)',
border: '1px solid var(--color-line)',
borderRadius: 'var(--radius-lg)',
fontFamily: 'var(--font-sans)',
fontSize: '13px',
boxShadow: '0 12px 30px -10px rgba(40,25,10,0.38)',
},
classNames: {
title: 'font-medium',
description: 'text-mute text-xs',
},
}}
/>
)
}
// Re-export для удобства: `import { toast } from '@/ui/components/toaster'`.
export { toast }
+5
View File
@@ -123,3 +123,8 @@ export {
DropdownMenuRadioGroup, DropdownMenuRadioGroup,
} from './components/dropdown-menu' } from './components/dropdown-menu'
export { Switch as ShadcnSwitch } from './components/switch' export { Switch as ShadcnSwitch } from './components/switch'
// Toast notifications через sonner (rich, accessible, themable).
// Mount <Toaster /> один раз в main.tsx; call toast.success/error/etc.
// в callbacks / effects.
export { Toaster, toast } from './components/toaster'