docs: обновление правил UI Kit и блока поставщиков + новые glass компоненты
- Добавлены правила анализа UI Kit компонентов (interaction-integrity-rules.md) - Обновлены правила блока 1: убран заголовок, добавлена кнопка навигации (rules-complete.md) - Детальная спецификация плавающей кнопки "Назад" (seller-ui-rules.md) - Новые компоненты: GlassDatePicker и GlassSelect для UI Kit - Улучшены hover/focus эффекты в DatePicker 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -139,6 +139,52 @@ ignores: ['diagnostic-script.js', 'legacy-config.js'] // конкретные ф
|
||||
- Планирую создать новые файлы вместо редактирования существующих
|
||||
```
|
||||
|
||||
### 1.5 🎯 ПРАВИЛА АНАЛИЗА ИСХОДНЫХ ПРИМЕРОВ
|
||||
|
||||
**КРИТИЧЕСКИ ВАЖНО**: При работе с существующими примерами кода или UI Kit компонентами:
|
||||
|
||||
#### 🔍 **ОБЯЗАТЕЛЬНЫЙ ТРЕХУРОВНЕВЫЙ АНАЛИЗ:**
|
||||
|
||||
1. **📋 СОДЕРЖАТЕЛЬНЫЙ АНАЛИЗ** (что делает код):
|
||||
- Функциональность компонента
|
||||
- Логика работы
|
||||
- Данные и состояния
|
||||
|
||||
2. **🏗️ АРХИТЕКТУРНЫЙ АНАЛИЗ** (как организован код):
|
||||
- Структура компонентов и контейнеров
|
||||
- Взаимосвязи между элементами
|
||||
- Позиционирование и layout
|
||||
- Иерархия DOM-элементов
|
||||
|
||||
3. **🎨 СТИЛЕВОЙ АНАЛИЗ** (как выглядит код):
|
||||
- CSS классы и стили
|
||||
- Анимации и переходы
|
||||
- Цвета и размеры
|
||||
|
||||
#### ❌ **ТИПИЧНЫЕ ОШИБКИ АНАЛИЗА:**
|
||||
|
||||
- **Поверхностный анализ**: Копирование только стилей без понимания архитектуры
|
||||
- **Игнорирование контекста**: Непонимание места элемента в общей структуре
|
||||
- **Буквальное копирование**: Применение решения без адаптации к текущей задаче
|
||||
|
||||
#### ✅ **ПРАВИЛЬНЫЙ ПОДХОД:**
|
||||
|
||||
```
|
||||
🔬 АЛГОРИТМ АНАЛИЗА ПРИМЕРА:
|
||||
1. Прочитать ВЕСЬ код компонента-примера
|
||||
2. Понять АРХИТЕКТУРУ: где элемент размещен относительно других
|
||||
3. Понять ЛОГИКУ: почему именно так структурировано
|
||||
4. Адаптировать к ТЕКУЩЕЙ ЗАДАЧЕ: применить принципы, а не просто скопировать
|
||||
5. Проверить СООТВЕТСТВИЕ правилам проекта
|
||||
```
|
||||
|
||||
#### 🚨 **СТОП-ВОПРОСЫ ПЕРЕД РЕАЛИЗАЦИЕЙ:**
|
||||
|
||||
- "Понимаю ли я **архитектуру** этого решения?"
|
||||
- "Где именно должен располагаться элемент в **общей структуре**?"
|
||||
- "Какова **семантическая роль** этого элемента?"
|
||||
- "Как это решение **адаптируется** к моей текущей задаче?"
|
||||
|
||||
---
|
||||
|
||||
## 🔄 II. ПРОЦЕДУРНЫЙ УРОВЕНЬ: ПОСЛЕДОВАТЕЛЬНОСТИ ДЕЙСТВИЙ
|
||||
@ -563,9 +609,69 @@ ignores: ['diagnostic-script.js', 'legacy-config.js'] // конкретные ф
|
||||
|
||||
---
|
||||
|
||||
## 📚 VIII. ИЗВЛЕЧЕННЫЕ УРОКИ И АНТИ-ПАТТЕРНЫ
|
||||
|
||||
### 8.1 🚨 КРИТИЧЕСКИЕ ОШИБКИ В АНАЛИЗЕ UI КОМПОНЕНТОВ
|
||||
|
||||
#### **CASE STUDY: Ошибка с плавающей кнопкой из UI Kit**
|
||||
|
||||
**❌ ОШИБКА**: При добавлении кнопки "🌟 Вариант 1: Плавающая кнопка слева" из UI Kit:
|
||||
|
||||
1. **Поверхностный анализ**: Скопировал только стили кнопки
|
||||
2. **Игнорирование архитектуры**: Не заметил, что кнопка в **отдельном контейнере**
|
||||
3. **Неправильное размещение**: Добавил как часть блока контента
|
||||
4. **Непонимание термина**: "Плавающая" = независимая от контента, между элементами
|
||||
|
||||
**✅ ПРАВИЛЬНОЕ РЕШЕНИЕ**:
|
||||
|
||||
```tsx
|
||||
// ❌ НЕПРАВИЛЬНО - кнопка внутри блока контента
|
||||
<div className="content-block relative">
|
||||
<button className="absolute...">Назад</button>
|
||||
<div className="actual-content">...</div>
|
||||
</div>
|
||||
|
||||
// ✅ ПРАВИЛЬНО - кнопка в отдельном контейнере
|
||||
<div className="flex gap-4">
|
||||
<Sidebar />
|
||||
<div className="relative"> {/* Отдельный контейнер */}
|
||||
<button className="absolute...">Назад</button>
|
||||
</div>
|
||||
<div className="content-block">...</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### **📋 ОБЯЗАТЕЛЬНЫЙ ЧЕКЛИСТ ДЛЯ UI KIT КОМПОНЕНТОВ:**
|
||||
|
||||
```
|
||||
🔍 ПЕРЕД РЕАЛИЗАЦИЕЙ:
|
||||
□ Прочитал ВЕСЬ код компонента-примера
|
||||
□ Понял архитектуру размещения в layout
|
||||
□ Определил семантическую роль элемента
|
||||
□ Понял взаимосвязи с соседними элементами
|
||||
□ Адаптировал принципы к текущей задаче
|
||||
□ Проверил соответствие правилам проекта
|
||||
```
|
||||
|
||||
#### **⚡ АНТИ-ПАТТЕРНЫ:**
|
||||
|
||||
- **"Быстрое копирование"** - копировать стили без понимания архитектуры
|
||||
- **"Частичный анализ"** - читать только нужную часть кода
|
||||
- **"Буквальное применение"** - использовать без адаптации к контексту
|
||||
- **"Игнорирование контейнеров"** - не обращать внимание на DOM-структуру
|
||||
|
||||
#### **✅ ПРАВИЛЬНЫЕ ПАТТЕРНЫ:**
|
||||
|
||||
- **"Архитектурный анализ первым"** - понять структуру, потом стили
|
||||
- **"Контекстная адаптация"** - применять принципы, а не код буквально
|
||||
- **"Семантическое понимание"** - осознавать роль каждого элемента
|
||||
- **"Итеративная проверка"** - сверяться с примером на каждом шаге
|
||||
|
||||
---
|
||||
|
||||
**Дата создания**: Декабрь 2024
|
||||
**Последнее обновление**: Август 2025
|
||||
**Версия**: 3.0 - ПОЛНАЯ РЕСТРУКТУРИЗАЦИЯ
|
||||
**Версия**: 3.1 - ДОПОЛНЕНЫ ПРАВИЛА АНАЛИЗА UI KIT
|
||||
**Статус**: АКТИВЕН - ОБЯЗАТЕЛЕН К ИСПОЛНЕНИЮ
|
||||
|
||||
**Связанные файлы**:
|
||||
|
@ -854,7 +854,8 @@ const handleSuppliesClick = () => {
|
||||
|
||||
**БЛОК 1: ПОСТАВЩИКИ** _(адаптивная сетка)_
|
||||
|
||||
- **Заголовок**: Минималистичный "🏢 Поставщики" без лишних элементов
|
||||
- **Навигация**: Плавающая кнопка "Назад" между сайдбаром и контентом (см. детали в seller-ui-rules.md)
|
||||
- **Заголовок**: БЕЗ заголовка - блок начинается сразу с функционального контента
|
||||
- **Поиск**: Компактное поле справа "Поиск поставщиков..." (w-64)
|
||||
- **Отображение**: Карточки поставщиков из раздела "Партнеры" в адаптивной сетке
|
||||
- **Выбор**: Клик выделяет карточку поставщика
|
||||
@ -1780,6 +1781,8 @@ height: calc(100vh - headerHeight - tabsHeight - statsHeight - margins);
|
||||
|
||||
**БЛОК 1: ПОСТАВЩИКИ** _(обязательный, 180px)_:
|
||||
|
||||
- **Навигация**: Плавающая кнопка "Назад" между сайдбаром и контентом (см. seller-ui-rules.md)
|
||||
- **БЕЗ заголовка** - блок начинается сразу с поиска и карточек
|
||||
- Карточки поставщиков из раздела "Партнеры"
|
||||
- Горизонтальный скролл при превышении ширины
|
||||
- Выбор только одного поставщика одновременно
|
||||
|
@ -17,7 +17,7 @@
|
||||
**СЕЛЛЕР (`SELLER`)** имеет доступ к следующим разделам:
|
||||
|
||||
- **Мои поставки** (`/my-supplies`) - управление поставками
|
||||
- **Маркет** (`/market`) - просмотр глобального каталога
|
||||
- **Маркет** (`/market`) - просмотр глобального каталога
|
||||
- **Партнеры** (`/partners`) - управление контрагентами
|
||||
- **Мессенджер** (`/messenger`) - связь с партнерами
|
||||
- **Настройки** (`/settings`) - профиль и настройки
|
||||
@ -56,11 +56,48 @@ switch (user?.organization?.type) {
|
||||
|
||||
### 2.2 Детальные правила горизонтального скролла поставщиков
|
||||
|
||||
**СТРУКТУРА И ОТОБРАЖЕНИЕ:**
|
||||
**КНОПКА НАВИГАЦИИ "НАЗАД":**
|
||||
|
||||
**Архитектура размещения:**
|
||||
|
||||
- **Расположение**: Между сайдбаром и основным контентом
|
||||
- **Контейнер**: Отдельный `<div className="relative">`
|
||||
- **НЕ является частью блока 1** - независимый навигационный элемент
|
||||
- **Позиционирование**: `absolute left-0 top-6`
|
||||
- **z-index**: 10 (поверх контента)
|
||||
|
||||
**Визуальный дизайн:**
|
||||
|
||||
```tsx
|
||||
className =
|
||||
'absolute left-0 top-6 z-10 w-8 h-8 bg-white/10 backdrop-blur-xl border border-white/20 hover:border-white/40 rounded-full flex items-center justify-center transition-all duration-300 hover:scale-110 hover:bg-white/20 group'
|
||||
```
|
||||
|
||||
**Параметры кнопки:**
|
||||
|
||||
- **Размер**: 32×32px (`w-8 h-8`)
|
||||
- **Форма**: Круглая (`rounded-full`)
|
||||
- **Фон**: `bg-white/10` с эффектом размытия `backdrop-blur-xl`
|
||||
- **Граница**: `border border-white/20`, при hover `border-white/40`
|
||||
- **Иконка**: `ArrowLeft` 16×16px (`h-4 w-4`), цвет `text-white/70`
|
||||
- **Hover эффекты**:
|
||||
- Масштабирование `scale-110`
|
||||
- Изменение фона `bg-white/20`
|
||||
- Иконка становится белой и увеличивается до `h-5 w-5`
|
||||
- **Анимация**: `transition-all duration-300`
|
||||
|
||||
**Функциональность:**
|
||||
|
||||
- **onClick**: Переход на `/supplies?tab=goods&subTab=suppliers`
|
||||
- **aria-label**: "Вернуться к поставщикам"
|
||||
- **Семантика**: Кнопка возврата к списку поставок
|
||||
|
||||
**СТРУКТУРА И ОТОБРАЖЕНИЕ БЛОКА 1:**
|
||||
|
||||
- **БЕЗ ЗАГОЛОВКА** - блок начинается сразу с поиска и контента
|
||||
- **Источник данных**: Партнеры типа `WHOLESALE` из раздела "Партнеры"
|
||||
- **Контейнер**: Фиксированная высота 176px (h-44) с горизонтальным скроллом
|
||||
- **Блок поставщиков**: Общая высота 180px, включает заголовок + контейнер скролла
|
||||
- **Блок поставщиков**: Общая высота 180px
|
||||
- **Направление**: Слева направо (LTR)
|
||||
- **Поведение**: Плавный скролл с автоскрытием полосы прокрутки
|
||||
|
||||
@ -382,6 +419,8 @@ height: calc(100vh - headerHeight - tabsHeight - statsHeight - margins);
|
||||
|
||||
**БЛОК 1: ПОСТАВЩИКИ** _(обязательный, 180px)_:
|
||||
|
||||
- БЕЗ заголовка - сразу функциональный контент
|
||||
- Кнопка навигации "Назад" - между сайдбаром и блоком (см. раздел 2.2)
|
||||
- Карточки поставщиков из раздела "Партнеры"
|
||||
- Горизонтальный скролл при превышении ширины
|
||||
- Выбор только одного поставщика одновременно
|
||||
@ -448,6 +487,7 @@ height: calc(100vh - headerHeight - tabsHeight - statsHeight - margins);
|
||||
### 3.1 React компоненты селлера
|
||||
|
||||
**Основные компоненты:**
|
||||
|
||||
- `SellerHomePage` - главная страница селлера (4 типо-зависимых компонента)
|
||||
- `SellerEconomicsPage` - экономическая аналитика селлера
|
||||
|
||||
@ -497,4 +537,4 @@ query GetSellerCards {
|
||||
**Связанные файлы**:
|
||||
|
||||
- [rules-complete.md](./rules-complete.md) - Общие бизнес-правила
|
||||
- [visual-design-rules.md](./visual-design-rules.md) - Визуальные правила
|
||||
- [visual-design-rules.md](./visual-design-rules.md) - Визуальные правила
|
||||
|
@ -19,7 +19,13 @@ interface DatePickerProps {
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export function DatePicker({ value, onChange, placeholder = 'Выберите дату', className, disabled }: DatePickerProps) {
|
||||
export function DatePicker({
|
||||
value,
|
||||
onChange,
|
||||
placeholder: _placeholder = 'Выберите дату',
|
||||
className,
|
||||
disabled,
|
||||
}: DatePickerProps) {
|
||||
const handleDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onChange?.(e.target.value)
|
||||
}
|
||||
@ -32,7 +38,7 @@ export function DatePicker({ value, onChange, placeholder = 'Выберите д
|
||||
onChange={handleDateChange}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'flex h-9 w-full rounded-lg border border-white/10 bg-white/5 backdrop-blur px-3 py-1 text-sm text-white placeholder:text-white/50 focus:border-white/20 focus:outline-none focus:ring-1 focus:ring-white/20 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-9 w-full rounded-lg border border-white/10 bg-white/5 backdrop-blur px-3 py-1 text-sm text-white placeholder:text-white/50 hover:border-white/30 hover:bg-white/10 focus:border-purple-400/50 focus:outline-none focus:ring-2 focus:ring-purple-400/20 transition-all duration-200 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'pr-8', // Place for calendar icon
|
||||
className,
|
||||
)}
|
||||
|
46
src/components/ui/glass-date-picker.tsx
Normal file
46
src/components/ui/glass-date-picker.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
'use client'
|
||||
|
||||
import { Calendar } from 'lucide-react'
|
||||
import * as React from 'react'
|
||||
|
||||
interface GlassDatePickerProps {
|
||||
value?: string
|
||||
onChange?: (date: string) => void
|
||||
placeholder?: string
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export function GlassDatePicker({
|
||||
value,
|
||||
onChange,
|
||||
placeholder: _placeholder = 'дд.мм.гггг',
|
||||
className = '',
|
||||
disabled = false,
|
||||
}: GlassDatePickerProps) {
|
||||
const handleDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onChange?.(e.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative" data-testid="glass-date-picker-wrapper">
|
||||
<input
|
||||
type="date"
|
||||
value={value}
|
||||
onChange={handleDateChange}
|
||||
disabled={disabled}
|
||||
data-testid="glass-date-picker-input"
|
||||
className={`
|
||||
bg-white/5 border border-white/10 text-white rounded-lg h-9 px-3 text-sm
|
||||
hover:border-white/30 hover:bg-white/10
|
||||
focus:border-purple-400/50 focus:ring-2 focus:ring-purple-400/20
|
||||
transition-all duration-200 outline-none
|
||||
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-white/10 disabled:hover:bg-white/5
|
||||
pr-8
|
||||
${className}
|
||||
`}
|
||||
/>
|
||||
<Calendar className="absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 text-white/40 pointer-events-none" />
|
||||
</div>
|
||||
)
|
||||
}
|
114
src/components/ui/glass-select.tsx
Normal file
114
src/components/ui/glass-select.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
interface GlassSelectOption {
|
||||
value: string
|
||||
label: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
interface GlassSelectProps {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
placeholder?: string
|
||||
options: GlassSelectOption[]
|
||||
className?: string
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export function GlassSelect({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = 'Выберите вариант',
|
||||
options,
|
||||
className = '',
|
||||
size = 'md',
|
||||
disabled = false,
|
||||
}: GlassSelectProps) {
|
||||
const sizeClasses = {
|
||||
sm: 'h-8 text-xs px-3',
|
||||
md: 'h-9 text-sm px-3',
|
||||
lg: 'h-11 text-base px-4',
|
||||
}
|
||||
|
||||
return (
|
||||
<select
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
disabled={disabled}
|
||||
className={`
|
||||
bg-white/5 border border-white/10 text-white rounded-lg
|
||||
hover:border-white/30 hover:bg-white/10
|
||||
focus:border-purple-400/50 focus:ring-2 focus:ring-purple-400/20
|
||||
transition-all duration-200 outline-none
|
||||
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-white/10 disabled:hover:bg-white/5
|
||||
${sizeClasses[size]}
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{placeholder && (
|
||||
<option value="" disabled className="bg-gray-800 text-white/60">
|
||||
{placeholder}
|
||||
</option>
|
||||
)}
|
||||
{options.map((option) => (
|
||||
<option key={option.value} value={option.value} disabled={option.disabled} className="bg-gray-800 text-white">
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)
|
||||
}
|
||||
|
||||
// Дополнительный компонент для простого использования с children
|
||||
interface GlassSelectSimpleProps {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
placeholder?: string
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export function GlassSelectSimple({
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
children,
|
||||
className = '',
|
||||
size = 'md',
|
||||
disabled = false,
|
||||
}: GlassSelectSimpleProps) {
|
||||
const sizeClasses = {
|
||||
sm: 'h-8 text-xs px-3',
|
||||
md: 'h-9 text-sm px-3',
|
||||
lg: 'h-11 text-base px-4',
|
||||
}
|
||||
|
||||
return (
|
||||
<select
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
disabled={disabled}
|
||||
className={`
|
||||
bg-white/5 border border-white/10 text-white rounded-lg
|
||||
hover:border-white/30 hover:bg-white/10
|
||||
focus:border-purple-400/50 focus:ring-2 focus:ring-purple-400/20
|
||||
transition-all duration-200 outline-none
|
||||
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-white/10 disabled:hover:bg-white/5
|
||||
${sizeClasses[size]}
|
||||
${className}
|
||||
`}
|
||||
>
|
||||
{placeholder && (
|
||||
<option value="" disabled className="bg-gray-800 text-white/60">
|
||||
{placeholder}
|
||||
</option>
|
||||
)}
|
||||
{children}
|
||||
</select>
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user