Files
router-lists-ui/frontend/UX_IMPROVEMENTS.md
T

15 KiB
Raw Blame History

🎨 UX/UI Улучшения - Документация

Этот документ описывает все внедренные UX/UI улучшения в проект S3 Lists Manager.

📋 Содержание

  1. CSS Улучшения
  2. Новые Компоненты
  3. Улучшенные Компоненты
  4. Примеры Использования

CSS Улучшения

Semantic Colors

Добавлена система семантических цветов для статусов и акцентов:

--status-online: #2fb344;
--status-offline: #d63939;
--status-warning: #f59f00;
--status-unknown: #868e96;

--accent-primary: #206bc4;
--accent-danger: #d63939;
--accent-success: #2fb344;

Улучшенная Типографика

  • Увеличенный размер заголовков с лучшей читаемостью
  • Оптимизированная межстрочная высота
  • Letter-spacing для заголовков

Enhanced Spacing

  • Единая система отступов через CSS переменные
  • Увеличенные padding в таблицах для лучшей читаемости
  • Consistent spacing между карточками

Интерактивные Элементы

  • Hover states: кнопки поднимаются на 1px с тенью
  • Active states: визуальный feedback при нажатии
  • Focus states: улучшенные для accessibility
  • Touch targets: минимум 44x44px на мобильных

Новые Компоненты

1. Tooltip

Компонент для отображения подсказок с поддержкой keyboard shortcuts.

Использование:

import Tooltip from './components/Tooltip.jsx'

<Tooltip content="Сохранить изменения" shortcut="Ctrl+S" position="top">
  <button className="btn btn-primary">
    <IconDeviceFloppy /> Сохранить
  </button>
</Tooltip>

Props:

  • content (string) - текст подсказки
  • shortcut (string) - keyboard shortcut для отображения
  • position (string) - позиция: 'top', 'bottom', 'left', 'right'
  • delay (number) - задержка перед показом (ms)

2. TrendIndicator

Компонент для отображения изменения метрик со стрелками вверх/вниз.

Использование:

import TrendIndicator from './components/TrendIndicator.jsx'

<TrendIndicator 
  value={1250} 
  previousValue={1180}
  format="number" // или "percent"
  inverse={false} // true если рост = плохо
/>

Props:

  • value (number) - текущее значение
  • previousValue (number) - предыдущее значение
  • format ('number' | 'percent') - формат отображения
  • inverse (boolean) - инвертировать цвета (рост = красный)

3. LastSaved

Компонент для отображения времени последнего сохранения с автообновлением.

Использование:

import LastSaved from './components/LastSaved.jsx'

<LastSaved 
  timestamp="2025-01-15T10:30:00Z" 
  variant="compact" // 'default', 'badge', 'compact'
/>

Варианты:

  • default - полный вид с иконкой
  • badge - зеленый badge "Сохранено X мин назад"
  • compact - компактный вид для toolbar

4. ContextMenu

Компонент контекстного меню для действий с поддержкой правого клика.

Использование:

import ContextMenu from './components/ContextMenu.jsx'
import { IconEdit, IconTrash, IconCopy } from '@tabler/icons-react'

<ContextMenu 
  items={[
    { label: 'Редактировать', icon: IconEdit, onClick: handleEdit },
    { label: 'Копировать', icon: IconCopy, onClick: handleCopy },
    { divider: true },
    { label: 'Удалить', icon: IconTrash, onClick: handleDelete, variant: 'danger' }
  ]}
  align="right"
>
  {/* Trigger element или оставить пустым для кнопки с тремя точками */}
</ContextMenu>

5. ValidatedInput

Input с real-time валидацией и визуальным feedback.

Использование:

import ValidatedInput from './components/ValidatedInput.jsx'

const validateDomain = (value) => {
  const valid = /^([a-z0-9-]+\.)+[a-z]{2,}$/i.test(value)
  return {
    valid,
    message: valid ? 'Домен валиден' : 'Введите корректный домен (example.com)'
  }
}

<ValidatedInput
  value={domain}
  onChange={(e) => setDomain(e.target.value)}
  validate={validateDomain}
  label="Домен"
  placeholder="example.com"
  hint="Введите доменное имя без http://"
  required
  debounce={300}
/>

Props:

  • validate (function) - функция валидации
  • debounce (number) - задержка перед валидацией
  • showValidIcon (boolean) - показывать иконку валидации
  • validateOnChange (boolean) - валидировать при вводе или только при blur

6. ProgressBar & MultiStepProgress

Компоненты для отображения прогресса операций.

ProgressBar:

import ProgressBar from './components/ProgressBar.jsx'

<ProgressBar 
  progress={65} 
  status="Загрузка данных..."
  estimatedTime={45} // секунды
  variant="primary"
  striped
  animated
/>

MultiStepProgress:

import { MultiStepProgress } from './components/ProgressBar.jsx'

<MultiStepProgress 
  steps={['Валидация', 'Обработка', 'Сохранение', 'Завершение']}
  currentStep={1} // текущий шаг (0-indexed)
  variant="success"
/>

7. MobileCardView

Компонент для отображения таблиц в виде карточек на мобильных с swipe gestures.

Использование:

import MobileCardView from './components/MobileCardView.jsx'

<MobileCardView 
  items={domains}
  onItemClick={(item) => console.log('Clicked:', item)}
  renderContent={(item) => (
    <div>
      <div className="fw-bold">{item.domain}</div>
      <div className="text-muted small">{item.community}</div>
    </div>
  )}
  actions={[
    { label: 'Редактировать', icon: IconEdit, onClick: handleEdit },
    { label: 'Удалить', icon: IconTrash, onClick: handleDelete, variant: 'danger' }
  ]}
/>

8. KeyboardShortcutHint

Компонент для отображения горячих клавиш.

Использование:

import KeyboardShortcutHint, { ShortcutsList } from './components/KeyboardShortcutHint.jsx'

// Внутри кнопки:
<button className="btn btn-primary">
  Сохранить
  <KeyboardShortcutHint shortcut="Ctrl + S" />
</button>

// Список горячих клавиш:
<ShortcutsList 
  shortcuts={[
    { description: 'Сохранить изменения', keys: 'Ctrl + S' },
    { description: 'Отменить', keys: 'Ctrl + Z' },
    { description: 'Поиск', keys: 'Ctrl + F' }
  ]}
/>

Улучшенные Компоненты

ErrorAlert

Добавлены новые возможности:

  • Кнопка "Копировать ошибку" для bug reports
  • Кнопка "Повторить" для retry операций
  • Раскрывающиеся детали ошибки
  • Отображение кода ошибки

Новое использование:

<ErrorAlert 
  message="Не удалось загрузить данные"
  error={{ code: 'E_NETWORK', details: errorDetails }}
  onRetry={fetchData}
  onClose={() => setError(null)}
  showDetails={true}
/>

EmptyState

Добавлены:

  • Поддержка иллюстраций
  • Различные размеры (small, default, large)
  • Варианты цветов (success, info, warning)
  • Анимация иллюстраций

Новое использование:

<EmptyState
  icon={IconDatabase}
  title="Нет доменов"
  description="Начните с добавления первого домена"
  size="large"
  variant="info"
  illustration="/illustrations/empty-state.svg"
  action={<button className="btn btn-primary">Добавить домен</button>}
/>

Pagination

Улучшения:

  • Jump to Page для быстрого перехода (показывается при > 10 страниц)
  • Иконки вместо текста для навигации
  • Улучшенная accessibility
  • Адаптивность для мобильных

Автоматически используется везде, где был старый Pagination


Dashboard

Улучшения:

  • StatCard теперь поддерживает trends
  • Добавлен LastSaved в header
  • Hover эффекты на карточках (card-hover)
  • Tooltips на кнопках

Изменения в коде:

<StatCard
  icon={IconWorld}
  value={stats.domainsCount}
  title="Доменов"
  to="/domains"
  trend={true}
  previousValue={previousStats?.domainsCount}
/>

Примеры Использования

Пример 1: Форма с валидацией

function AddDomainForm() {
  const [domain, setDomain] = useState('')
  const [community, setCommunity] = useState('')

  const validateDomain = (value) => ({
    valid: /^([a-z0-9-]+\.)+[a-z]{2,}$/i.test(value),
    message: 'Введите корректный домен'
  })

  const validateCommunity = (value) => ({
    valid: /^\d+$/.test(value),
    message: 'Введите числовой community'
  })

  return (
    <form>
      <ValidatedInput
        value={domain}
        onChange={(e) => setDomain(e.target.value)}
        validate={validateDomain}
        label="Домен"
        placeholder="example.com"
        required
      />
      
      <ValidatedInput
        value={community}
        onChange={(e) => setCommunity(e.target.value)}
        validate={validateCommunity}
        label="Community"
        placeholder="65000"
        required
      />
      
      <button type="submit" className="btn btn-primary">
        Добавить
        <KeyboardShortcutHint shortcut="Ctrl + Enter" />
      </button>
    </form>
  )
}

Пример 2: Таблица с ContextMenu и MobileCardView

function DomainsTable({ domains, onEdit, onDelete }) {
  return (
    <>
      {/* Desktop view */}
      <div className="table-responsive d-none d-md-block">
        <table className="table">
          <tbody>
            {domains.map(domain => (
              <tr key={domain.id}>
                <td>{domain.name}</td>
                <td>{domain.community}</td>
                <td>
                  <ContextMenu
                    items={[
                      { label: 'Редактировать', icon: IconEdit, onClick: () => onEdit(domain) },
                      { label: 'Удалить', icon: IconTrash, onClick: () => onDelete(domain), variant: 'danger' }
                    ]}
                  />
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      
      {/* Mobile view */}
      <MobileCardView
        items={domains}
        renderContent={(domain) => (
          <div>
            <div className="fw-bold">{domain.name}</div>
            <div className="text-muted small">{domain.community}</div>
          </div>
        )}
        actions={[
          { label: 'Редактировать', icon: IconEdit, onClick: onEdit },
          { label: 'Удалить', icon: IconTrash, onClick: onDelete, variant: 'danger' }
        ]}
      />
    </>
  )
}

Пример 3: Dashboard с трендами

function Dashboard() {
  const [stats, setStats] = useState({})
  const [previousStats, setPreviousStats] = useState(null)
  const [lastFetchTime, setLastFetchTime] = useState(null)

  return (
    <div>
      <PageHeader
        title="Панель"
        actions={
          <div className="d-flex gap-3">
            <LastSaved timestamp={lastFetchTime} variant="compact" />
            <Tooltip content="Обновить данные" shortcut="F5">
              <button className="btn btn-outline-primary">
                <IconRefresh /> Обновить
              </button>
            </Tooltip>
          </div>
        }
      />
      
      <div className="row g-3">
        <div className="col-md-3">
          <StatCard
            icon={IconWorld}
            value={stats.domains}
            title="Доменов"
            trend={true}
            previousValue={previousStats?.domains}
          />
        </div>
      </div>
    </div>
  )
}

Best Practices

1. Accessibility

  • Всегда добавляйте aria-label для иконочных кнопок
  • Используйте semantic HTML
  • Обеспечьте минимум 44x44px для touch targets на мобильных
  • Добавляйте keyboard shortcuts для частых действий

2. Performance

  • Используйте useMemo для тяжелых вычислений
  • Debounce для валидации форм
  • Lazy loading для больших списков
  • Virtualization для таблиц > 1000 записей

3. User Experience

  • Показывайте loading states для всех async операций
  • Предоставляйте clear error messages с действиями
  • Используйте optimistic updates где возможно
  • Сохраняйте состояние форм при навигации

4. Mobile First

  • Используйте MobileCardView для таблиц
  • Убедитесь что все элементы кликабельны на touch screens
  • Тестируйте swipe gestures
  • Адаптируйте модальные окна для мобильных

Roadmap

Планируемые улучшения:

  • Виртуальный скроллинг для больших таблиц
  • Drag & Drop для изменения порядка
  • Сохранение пользовательских настроек
  • Dark mode improvements
  • Анимированные transitions между страницами
  • PWA support
  • Offline mode

Вклад

При добавлении новых компонентов:

  1. Следуйте существующим паттернам
  2. Добавляйте JSDoc комментарии
  3. Обеспечьте accessibility
  4. Тестируйте на мобильных
  5. Обновляйте эту документацию

Версия: 1.0.0
Последнее обновление: 2025-01-15