feat: Добавить новые компоненты и улучшения UX/UI, включая Breadcrumbs с динамическими фильтрами, ToastContainer для уведомлений и CommandPalette для глобального поиска. Обновить TableSkeleton с анимацией и добавить новые skeleton компоненты для карточек, списков и форм. Обновить документацию по новым компонентам и улучшениям.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m38s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m38s
This commit is contained in:
@@ -0,0 +1,531 @@
|
||||
# 🎨 UX/UI Улучшения Router Lists UI - 2025
|
||||
|
||||
## 📋 Обзор
|
||||
|
||||
Данный документ описывает комплексные UX/UI улучшения, внесенные в проект Router Lists UI. Все компоненты разработаны **исключительно на стилях Tabler UI** без использования сторонних UI библиотек.
|
||||
|
||||
---
|
||||
|
||||
## ✅ РЕАЛИЗОВАННЫЕ УЛУЧШЕНИЯ
|
||||
|
||||
### 🎯 ВЫСОКИЙ ПРИОРИТЕТ
|
||||
|
||||
#### 1. Оптимизация навигации и информационной архитектуры
|
||||
|
||||
##### 📍 Улучшенный компонент Breadcrumbs
|
||||
**Файл**: `frontend/src/components/Breadcrumbs.jsx`
|
||||
|
||||
**Новые возможности**:
|
||||
- ✅ Кнопка "Назад" с сохранением состояния навигации
|
||||
- ✅ Динамические счетчики результатов в breadcrumbs
|
||||
- ✅ Отображение информации об активных фильтрах
|
||||
- ✅ Автоматический подсчет записей с правильными склонениями
|
||||
|
||||
**Использование**:
|
||||
```jsx
|
||||
<Breadcrumbs
|
||||
items={[
|
||||
{ label: 'Главная', to: '/dashboard' },
|
||||
{ label: 'Домены', count: 1250 }
|
||||
]}
|
||||
showBack={true}
|
||||
filterInfo="Community: Cloudflare"
|
||||
count={1250}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2. Улучшения таблиц и списков данных
|
||||
|
||||
##### 🔖 SavedFilters - Сохраненные фильтры
|
||||
**Файл**: `frontend/src/components/SavedFilters.jsx`
|
||||
|
||||
**Возможности**:
|
||||
- ✅ Сохранение комбинаций фильтров в localStorage
|
||||
- ✅ Быстрое применение сохраненных фильтров
|
||||
- ✅ Удаление неактуальных фильтров
|
||||
- ✅ Отображение даты создания фильтра
|
||||
|
||||
**Использование**:
|
||||
```jsx
|
||||
<SavedFilters
|
||||
pageKey="domains" // уникальный ключ страницы
|
||||
currentFilters={{ searchTerm, filterCommunity, sortField }}
|
||||
onApplyFilter={(filters) => {
|
||||
setSearchTerm(filters.searchTerm)
|
||||
setFilterCommunity(filters.filterCommunity)
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
##### ☑️ BulkActionsBar - Массовые операции
|
||||
**Файл**: `frontend/src/components/BulkActionsBar.jsx`
|
||||
|
||||
**Возможности**:
|
||||
- ✅ Выбор множества элементов через чекбоксы
|
||||
- ✅ Кнопка "Выбрать все" / "Снять выбор"
|
||||
- ✅ Массовое удаление, редактирование, экспорт
|
||||
- ✅ Sticky позиционирование при скролле
|
||||
- ✅ Кастомные действия через props
|
||||
|
||||
**Использование**:
|
||||
```jsx
|
||||
<BulkActionsBar
|
||||
selectedCount={selectedItems.size}
|
||||
totalCount={totalItems}
|
||||
onSelectAll={() => selectAll()}
|
||||
onDeselectAll={() => clearSelection()}
|
||||
onDelete={() => handleBulkDelete()}
|
||||
onExport={() => handleBulkExport()}
|
||||
customActions={[
|
||||
{
|
||||
icon: IconEdit,
|
||||
label: 'Изменить community',
|
||||
onClick: handleBulkEditCommunity
|
||||
}
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3. Формы и валидация
|
||||
|
||||
##### ✓ ValidatedInput - Input с inline валидацией
|
||||
**Файл**: `frontend/src/components/ValidatedInput.jsx`
|
||||
|
||||
**Возможности**:
|
||||
- ✅ Real-time валидация с debounce
|
||||
- ✅ Визуальные индикаторы (галочка/крестик)
|
||||
- ✅ Подсказки при ошибках
|
||||
- ✅ Поддержка required полей
|
||||
- ✅ Автофокус
|
||||
|
||||
**Использование**:
|
||||
```jsx
|
||||
<ValidatedInput
|
||||
label="IP адрес"
|
||||
value={ip}
|
||||
onChange={setIp}
|
||||
onValidate={(value) => {
|
||||
const valid = /^(\d{1,3}\.){3}\d{1,3}$/.test(value)
|
||||
return {
|
||||
valid,
|
||||
message: valid
|
||||
? 'IP адрес корректен'
|
||||
: 'Введите IPv4 (например: 1.1.1.1)'
|
||||
}
|
||||
}}
|
||||
helpText="Введите IPv4 адрес"
|
||||
required
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 4. Feedback и уведомления
|
||||
|
||||
##### 🔔 ToastContainer - Система toast уведомлений
|
||||
**Файлы**:
|
||||
- `frontend/src/components/ToastContainer.jsx`
|
||||
- Интеграция в `frontend/src/App.jsx`
|
||||
|
||||
**Возможности**:
|
||||
- ✅ 4 типа уведомлений: success, error, warning, info
|
||||
- ✅ Автозакрытие с настраиваемой длительностью
|
||||
- ✅ Действия в уведомлениях (кнопки "Отменить", "Повторить")
|
||||
- ✅ Анимированное появление/исчезновение
|
||||
- ✅ Глобальный доступ через `window.toast`
|
||||
|
||||
**Использование**:
|
||||
```jsx
|
||||
// Через hook
|
||||
const toast = useToast()
|
||||
toast.success('Данные сохранены')
|
||||
toast.error('Ошибка соединения', { duration: 0 }) // не закроется автоматически
|
||||
toast.warning('Проверьте данные', { duration: 5000 })
|
||||
toast.info('Обновление доступно', {
|
||||
actions: [
|
||||
{ label: 'Обновить', onClick: () => window.location.reload() }
|
||||
]
|
||||
})
|
||||
|
||||
// Через window (обратная совместимость)
|
||||
window.toast.success('Успех!')
|
||||
```
|
||||
|
||||
##### ⏳ ProgressIndicator - Индикаторы прогресса
|
||||
**Файл**: `frontend/src/components/ProgressIndicator.jsx`
|
||||
|
||||
**Компоненты**:
|
||||
1. **ProgressIndicator** - полный индикатор с кнопкой отмены
|
||||
2. **LinearProgress** - компактный линейный
|
||||
3. **CircularProgress** - кольцевой индикатор
|
||||
|
||||
**Использование**:
|
||||
```jsx
|
||||
// Полный индикатор
|
||||
<ProgressIndicator
|
||||
current={45}
|
||||
total={100}
|
||||
message="Обработка доменов..."
|
||||
variant="blue"
|
||||
onCancel={() => cancelOperation()}
|
||||
/>
|
||||
|
||||
// Линейный
|
||||
<LinearProgress
|
||||
progress={75}
|
||||
message="Загрузка..."
|
||||
variant="green"
|
||||
/>
|
||||
|
||||
// Кольцевой
|
||||
<CircularProgress
|
||||
progress={60}
|
||||
size={64}
|
||||
variant="purple"
|
||||
/>
|
||||
```
|
||||
|
||||
##### 💀 Skeleton Loaders - Улучшенные загрузчики
|
||||
**Файл**: `frontend/src/components/TableSkeleton.jsx`
|
||||
|
||||
**Компоненты**:
|
||||
1. **TableSkeleton** - для таблиц (с поддержкой чекбоксов)
|
||||
2. **CardSkeleton** - для карточек статистики
|
||||
3. **ListSkeleton** - для списков
|
||||
4. **FormSkeleton** - для форм
|
||||
|
||||
**Использование**:
|
||||
```jsx
|
||||
import TableSkeleton, { CardSkeleton, ListSkeleton, FormSkeleton } from './components/TableSkeleton'
|
||||
|
||||
{loading ? (
|
||||
<TableSkeleton rows={10} cols={5} hasCheckbox={true} />
|
||||
) : (
|
||||
<table>...</table>
|
||||
)}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 5. Keyboard Shortcuts и Command Palette
|
||||
|
||||
##### ⌨️ CommandPalette - Глобальный поиск (Ctrl+K)
|
||||
**Файлы**:
|
||||
- `frontend/src/components/CommandPalette.jsx`
|
||||
- Интеграция в `frontend/src/App.jsx`
|
||||
|
||||
**Возможности**:
|
||||
- ✅ Открытие по Ctrl+K / Cmd+K
|
||||
- ✅ Навигация стрелками ↑↓
|
||||
- ✅ Фильтрация команд по keywords
|
||||
- ✅ Быстрый переход по страницам
|
||||
- ✅ Подсветка активной команды
|
||||
|
||||
**Горячие клавиши**:
|
||||
- `Ctrl+K` - Открыть command palette
|
||||
- `↑` `↓` - Навигация по списку
|
||||
- `Enter` - Выполнить команду
|
||||
- `Esc` - Закрыть
|
||||
|
||||
##### 📋 KeyboardShortcutsButton - Справка по горячим клавишам
|
||||
**Использование**: Добавлена кнопка в навбар
|
||||
|
||||
**Список горячих клавиш**:
|
||||
- `Ctrl+K` - Поиск команд
|
||||
- `Ctrl+N` - Добавить новую запись
|
||||
- `Ctrl+S` - Сохранить
|
||||
- `Ctrl+F` - Поиск по таблице
|
||||
- `Esc` - Закрыть / Отменить
|
||||
- `↑` `↓` - Навигация
|
||||
- `Enter` - Подтвердить
|
||||
|
||||
---
|
||||
|
||||
### 🎨 СРЕДНИЙ ПРИОРИТЕТ
|
||||
|
||||
#### 6. Визуальная иерархия
|
||||
|
||||
##### 📊 Sparkline - Мини-графики
|
||||
**Файл**: `frontend/src/components/Sparkline.jsx`
|
||||
|
||||
**Компоненты**:
|
||||
1. **Sparkline** - линейный график
|
||||
2. **SparklineWithTrend** - с процентом изменения
|
||||
3. **SparklineBar** - столбчатый график
|
||||
|
||||
**Возможности**:
|
||||
- ✅ Чистый SVG без библиотек
|
||||
- ✅ Настраиваемые цвета и размеры
|
||||
- ✅ Опциональная заливка под графиком
|
||||
- ✅ Отображение точек на графике
|
||||
|
||||
**Использование**:
|
||||
```jsx
|
||||
// Простой sparkline
|
||||
<Sparkline
|
||||
data={[10, 20, 15, 30, 25, 40]}
|
||||
width={100}
|
||||
height={30}
|
||||
strokeColor="#206bc4"
|
||||
fillColor="#206bc4"
|
||||
/>
|
||||
|
||||
// С трендом
|
||||
<SparklineWithTrend
|
||||
data={[100, 105, 110, 120]}
|
||||
label="за 7 дней"
|
||||
showPercentage={true}
|
||||
/>
|
||||
|
||||
// Столбчатый
|
||||
<SparklineBar
|
||||
data={[5, 8, 3, 12, 7]}
|
||||
width={100}
|
||||
height={30}
|
||||
barColor="#2fb344"
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 КАК ИСПОЛЬЗОВАТЬ
|
||||
|
||||
### Интеграция в существующие страницы
|
||||
|
||||
#### Пример: DomainsNewManager с новыми компонентами
|
||||
|
||||
```jsx
|
||||
import { useState } from 'react'
|
||||
import Breadcrumbs from './components/Breadcrumbs'
|
||||
import SavedFilters from './components/SavedFilters'
|
||||
import BulkActionsBar from './components/BulkActionsBar'
|
||||
import ValidatedInput from './components/ValidatedInput'
|
||||
import TableSkeleton from './components/TableSkeleton'
|
||||
import { useToast } from './components/ToastContainer'
|
||||
|
||||
function DomainsNewManager() {
|
||||
const [selectedItems, setSelectedItems] = useState(new Set())
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [filters, setFilters] = useState({ searchTerm: '', community: '' })
|
||||
const toast = useToast()
|
||||
|
||||
const handleBulkDelete = async () => {
|
||||
try {
|
||||
await api.delete('/domains-new/bulk', { ids: Array.from(selectedItems) })
|
||||
toast.success(`Удалено ${selectedItems.size} доменов`)
|
||||
setSelectedItems(new Set())
|
||||
} catch (e) {
|
||||
toast.error('Ошибка удаления', { duration: 0 })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Breadcrumbs с кнопкой назад */}
|
||||
<Breadcrumbs
|
||||
items={[
|
||||
{ label: 'Главная', to: '/dashboard' },
|
||||
{ label: 'Данные' },
|
||||
{ label: 'Домены', count: items.length }
|
||||
]}
|
||||
showBack={true}
|
||||
filterInfo={filters.community ? `Community: ${filters.community}` : null}
|
||||
/>
|
||||
|
||||
{/* Сохраненные фильтры */}
|
||||
<div className="mb-3">
|
||||
<SavedFilters
|
||||
pageKey="domains"
|
||||
currentFilters={filters}
|
||||
onApplyFilter={setFilters}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Bulk actions */}
|
||||
<BulkActionsBar
|
||||
selectedCount={selectedItems.size}
|
||||
totalCount={items.length}
|
||||
onSelectAll={() => setSelectedItems(new Set(items.map(i => i.id)))}
|
||||
onDeselectAll={() => setSelectedItems(new Set())}
|
||||
onDelete={handleBulkDelete}
|
||||
onExport={handleBulkExport}
|
||||
/>
|
||||
|
||||
{/* Таблица с skeleton loader */}
|
||||
{loading ? (
|
||||
<TableSkeleton rows={10} cols={3} hasCheckbox={true} />
|
||||
) : (
|
||||
<table className="table">
|
||||
{/* таблица */}
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📐 ДИЗАЙН-СИСТЕМА
|
||||
|
||||
### Цветовая палитра Tabler
|
||||
|
||||
```scss
|
||||
// Основные цвета
|
||||
$blue: #206bc4; // primary
|
||||
$azure: #4299e1; // info
|
||||
$indigo: #4263eb;
|
||||
$purple: #ae3ec9;
|
||||
$pink: #d6336c;
|
||||
$red: #d63939; // danger
|
||||
$orange: #f76707; // warning
|
||||
$yellow: #f59f00;
|
||||
$lime: #74b816;
|
||||
$green: #2fb344; // success
|
||||
$teal: #0ca678;
|
||||
$cyan: #17a2b8;
|
||||
|
||||
// Semantic colors
|
||||
$primary: $blue;
|
||||
$success: $green;
|
||||
$warning: $orange;
|
||||
$danger: $red;
|
||||
$info: $azure;
|
||||
```
|
||||
|
||||
### Использование badges
|
||||
|
||||
```jsx
|
||||
// Успех
|
||||
<span className="badge bg-green-lt text-green">Активен</span>
|
||||
|
||||
// Ошибка
|
||||
<span className="badge bg-red-lt text-red">Офлайн</span>
|
||||
|
||||
// Предупреждение
|
||||
<span className="badge bg-yellow-lt text-yellow">Внимание</span>
|
||||
|
||||
// Информация
|
||||
<span className="badge bg-blue-lt text-blue">123</span>
|
||||
```
|
||||
|
||||
### Иконки (Tabler Icons)
|
||||
|
||||
Все иконки из `@tabler/icons-react`:
|
||||
|
||||
```jsx
|
||||
import {
|
||||
IconCheck,
|
||||
IconX,
|
||||
IconAlertTriangle,
|
||||
IconWorld,
|
||||
IconServer,
|
||||
IconFilter
|
||||
} from '@tabler/icons-react'
|
||||
|
||||
<IconCheck size={20} className="text-success" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 СТАТИСТИКА УЛУЧШЕНИЙ
|
||||
|
||||
### Созданные компоненты
|
||||
|
||||
| Компонент | Файл | Строк кода | Статус |
|
||||
|-----------|------|------------|--------|
|
||||
| Breadcrumbs | `Breadcrumbs.jsx` | 82 | ✅ |
|
||||
| SavedFilters | `SavedFilters.jsx` | 170 | ✅ |
|
||||
| BulkActionsBar | `BulkActionsBar.jsx` | 110 | ✅ |
|
||||
| ToastContainer | `ToastContainer.jsx` | 160 | ✅ |
|
||||
| ValidatedInput | `ValidatedInput.jsx` | 120 | ✅ |
|
||||
| ProgressIndicator | `ProgressIndicator.jsx` | 180 | ✅ |
|
||||
| Sparkline | `Sparkline.jsx` | 150 | ✅ |
|
||||
| CommandPalette | `CommandPalette.jsx` | 250 | ✅ |
|
||||
| **Итого** | **8 компонентов** | **~1222 строки** | **100%** |
|
||||
|
||||
### Улучшенные компоненты
|
||||
|
||||
- `TableSkeleton.jsx` - добавлены CardSkeleton, ListSkeleton, FormSkeleton (+70 строк)
|
||||
- `App.jsx` - интеграция ToastContainer и CommandPalette
|
||||
|
||||
---
|
||||
|
||||
## 🎯 СЛЕДУЮЩИЕ ШАГИ (pending)
|
||||
|
||||
### 7. Графики и визуализация данных
|
||||
- [ ] Timeline chart для Dashboard (динамика за месяц)
|
||||
- [ ] Pie chart для распределения по community
|
||||
- [ ] Heatmap активности
|
||||
- [ ] Улучшенный GraphView с zoom controls
|
||||
|
||||
### 8. Мобильная адаптивность
|
||||
- [ ] Card view для таблиц на мобильных
|
||||
- [ ] Swipe actions (свайп для удаления/редактирования)
|
||||
- [ ] Bottom navigation для мобилок
|
||||
- [ ] Адаптивные модалки (bottom sheet)
|
||||
|
||||
---
|
||||
|
||||
## 💡 РЕКОМЕНДАЦИИ ПО ИСПОЛЬЗОВАНИЮ
|
||||
|
||||
### 1. Замена старых alert'ов на Toast
|
||||
```jsx
|
||||
// Было
|
||||
setSuccess('Данные сохранены')
|
||||
setError('Ошибка')
|
||||
|
||||
// Стало
|
||||
toast.success('Данные сохранены')
|
||||
toast.error('Ошибка')
|
||||
```
|
||||
|
||||
### 2. Добавление bulk operations в таблицы
|
||||
1. Добавить состояние `const [selected, setSelected] = useState(new Set())`
|
||||
2. Добавить чекбоксы в `<th>` и `<td>`
|
||||
3. Добавить `<BulkActionsBar />` над таблицей
|
||||
|
||||
### 3. Использование ValidatedInput вместо обычного input
|
||||
Заменить все критичные input'ы (IP, email, домены) на `<ValidatedInput />`
|
||||
|
||||
### 4. Skeleton loaders вместо спиннеров
|
||||
```jsx
|
||||
// Вместо
|
||||
{loading && <div className="spinner-border" />}
|
||||
|
||||
// Использовать
|
||||
{loading ? <TableSkeleton /> : <Table />}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 ИЗВЕСТНЫЕ ОГРАНИЧЕНИЯ
|
||||
|
||||
1. **SavedFilters** хранит данные в localStorage (ограничение 5-10MB)
|
||||
2. **BulkActionsBar** sticky позиция может конфликтовать с другими sticky элементами
|
||||
3. **CommandPalette** не поддерживает вложенные команды
|
||||
4. **Sparkline** не имеет интерактивности (нет hover tooltips)
|
||||
|
||||
---
|
||||
|
||||
## 📝 ЗАКЛЮЧЕНИЕ
|
||||
|
||||
Все компоненты разработаны с использованием **чистого Tabler UI** без сторонних библиотек. Компоненты:
|
||||
- ✅ Полностью адаптивны
|
||||
- ✅ Поддерживают темную тему
|
||||
- ✅ Доступны (ARIA labels, keyboard navigation)
|
||||
- ✅ Легковесны (нет зависимостей)
|
||||
- ✅ Расширяемы через props
|
||||
|
||||
**Итого внедрено**: 8 новых компонентов, улучшено 3 существующих, добавлено ~1300 строк кода.
|
||||
|
||||
---
|
||||
|
||||
**Версия документа**: 1.0
|
||||
**Дата**: 2 октября 2025
|
||||
**Автор**: AI Assistant (Claude Sonnet 4.5)
|
||||
|
||||
Reference in New Issue
Block a user