architect/_archive/2025-11-09-marketplace-old/marketplace-mvp/STYLE-GUIDE.md

STYLE GUIDE - Marketplace MVP

Версия: 1.0
Дата: 2025-11-08
Статус: В разработке


Философия дизайна

Наш проект - это современный SaaS-сервис для управления заказами с маркетплейсов.

Вдохновение: Airtable, Notion, Linear, Retool
Принципы:
- Компактность - много данных на экране без скроллинга
- Консистентность - единый стиль везде
- Скорость - быстрая навигация, минимум кликов
- Ясность - понятные иконки, статусы, действия


1. LAYOUT (Макет)

1.1 Структура страницы

┌─────────────────────────────────────────────────────┐
│ Header: Logo + Title                                │
├──────────┬──────────────────────────────────────────┤
│          │                                          │
│ Sidebar  │  Main Content Area                       │
│ (Menu)   │  ┌────────────────────────────────────┐  │
│          │  │ Page Header (Title + Actions)     │  │
│          │  ├────────────────────────────────────┤  │
│          │  │ Filters / Tabs                     │  │
│          │  ├────────────────────────────────────┤  │
│          │  │ Data Table / Content               │  │
│          │  │                                    │  │
│          │  └────────────────────────────────────┘  │
└──────────┴──────────────────────────────────────────┘

Правила:
- Sidebar всегда слева (Streamlit стандарт)
- Контент всегда на всю ширину (layout="wide")
- Никаких вложенных sidebar


2. НАВИГАЦИЯ

2.1 Главное меню (Sidebar)

Структура:

🏠 Главная
📡 Каналы
📦 Товары
📋 Заказы
📊 Аналитика
⚙️ Настройки
🧪 Тесты (только admin)

Правила:
- Иконка + название (emoji для быстрого распознавания)
- Активная страница подсвечивается автоматически
- Максимум 7-8 пунктов

2.2 Breadcrumbs (Хлебные крошки)

Формат:

Каналы > Управление: Ozon Магазин #1

Когда использовать:
- При переходе на детальную страницу
- При многоуровневой навигации

Реализация: Показывать в верхней части страницы, кликабельные

2.3 Паттерн "Список → Деталь"

Список:
- Таблица со всеми записями
- Кнопка "Управление" / "Открыть" в каждой строке

Деталь:
- Кнопка "← Назад к списку" в верхней части
- При клике возвращаемся к списку
- При входе через меню всегда показываем список

Код-паттерн:

# В session_state хранится ID выбранного объекта
if 'selected_channel_id' in st.session_state:
    show_detail_page(st.session_state.selected_channel_id)
else:
    show_list_page()

ВАЖНО: При клике на пункт меню всегда очищаем session_state!


3. ТАБЛИЦЫ

3.1 Дизайн таблиц

Стиль: Компактный, как в Airtable/Notion

Характеристики:
- Высота строки: 36-40px (компактно!)
- Заголовки колонок: жирным, uppercase, серый фон
- Чередующиеся строки: белый / #F9FAFB
- Hover эффект: #F3F4F6
- Границы: тонкие 1px #E5E7EB

HTML/CSS шаблон:

<style>
.compact-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}
.compact-table thead th {
    background: #F3F4F6;
    color: #374151;
    font-weight: 600;
    text-transform: uppercase;
    font-size: 12px;
    padding: 8px 12px;
    text-align: left;
    border-bottom: 2px solid #E5E7EB;
    cursor: pointer;
    user-select: none;
}
.compact-table thead th:hover {
    background: #E5E7EB;
}
.compact-table tbody tr {
    border-bottom: 1px solid #E5E7EB;
}
.compact-table tbody tr:nth-child(even) {
    background: #F9FAFB;
}
.compact-table tbody tr:hover {
    background: #F3F4F6;
}
.compact-table tbody td {
    padding: 10px 12px;
    vertical-align: middle;
}
</style>

3.2 Сортируемые заголовки

Формат: Название ↑ или Название ↓

Реализация:

def sortable_header(column_name, current_sort_column, sort_direction):
    arrow = ""
    if current_sort_column == column_name:
        arrow = " ↑" if sort_direction == "asc" else " ↓"
    return f"{column_name}{arrow}"

Клик: Переключает направление сортировки

3.3 Колонки таблицы

Стандартные колонки для списков:

  1. Статус (иконка) - 40px
  2. Название (основное) - гибкая ширина
  3. Атрибуты (2-3 доп. поля) - фиксированная ширина
  4. Дата (updated_at) - 120px
  5. Действия (кнопки) - 100-150px

Пример:

┌───┬──────────────────┬────────┬────────┬────────────┬──────────┐
│ 🟢│ Ozon Магазин #1  │ OZON   │ ООО 1  │ 2025-11-08 │ [Открыть]│
│ 🔴│ WB Магазин #2    │ WB     │ ООО 2  │ 2025-11-07 │ [Открыть]│
└───┴──────────────────┴────────┴────────┴────────────┴──────────┘

4. ФОРМЫ И МОДАЛЫ

4.1 Типы форм

1. Встроенная форма (в той же странице)
- Для простых действий
- Пример: Фильтры, быстрое редактирование

2. Выдвижная панель (Drawer)
- Для средних форм
- Появляется справа, перекрывает часть экрана
- Пример: Создание/редактирование записи

3. Модальное окно (Modal)
- Для критичных действий
- Затемняет фон
- Пример: Подтверждение удаления

4. Отдельная страница (Full Page)
- Для сложных многошаговых форм
- Пример: Визард подключения канала

4.2 Кнопки форм

Расположение: Внизу справа

Порядок:

[Отмена]  [Сохранить]

Цвета:
- Основная кнопка: синий #3B82F6
- Отмена: серый #6B7280
- Удаление: красный #EF4444


5. КОМПОНЕНТЫ

5.1 Кнопки

Размеры:
- Small: 28px высота, 12px padding
- Medium: 36px высота, 16px padding
- Large: 44px высота, 20px padding

Типы:

# Primary (синяя)
st.button("Сохранить", type="primary")

# Secondary (серая)
st.button("Отмена")

# Icon (только иконка)
st.button("🗑️", key="delete")

5.2 Статусы

Иконки:
- 🟢 Активен / Успешно
- 🔴 Неактивен / Ошибка
- 🟡 Ожидание / В процессе
- ⚪ Новый / Неизвестно

Бейджи:

<span style="background: #10B981; color: white; padding: 2px 8px; border-radius: 12px; font-size: 12px;">
    Активен
</span>

5.3 Карточки

Для группировки информации:

<div style="
    background: white;
    border: 1px solid #E5E7EB;
    border-radius: 8px;
    padding: 16px;
    margin-bottom: 16px;
">
    <h3>Заголовок</h3>
    <p>Контент</p>
</div>

6. ЦВЕТОВАЯ ПАЛИТРА

Основные цвета:

Primary (синий):   #3B82F6
Success (зелёный): #10B981
Warning (жёлтый):  #F59E0B
Error (красный):   #EF4444

Нейтральные (серые):

Gray 50:  #F9FAFB  (фон строк)
Gray 100: #F3F4F6  (фон заголовков)
Gray 200: #E5E7EB  (границы)
Gray 400: #9CA3AF  (вторичный текст)
Gray 700: #374151  (основной текст)
Gray 900: #111827  (заголовки)

Фоны:

Page Background: #FFFFFF
Sidebar: #F9FAFB
Card: #FFFFFF

7. ТИПОГРАФИКА

Шрифты:
- Основной: system-ui, -apple-system, sans-serif
- Моноширинный: 'SF Mono', Monaco, monospace

Размеры:

H1 (Page Title):    24px, bold
H2 (Section):       20px, semibold
H3 (Card Title):    16px, semibold
Body:               14px, regular
Small:              12px, regular
Button:             14px, medium

Line Height:
- Заголовки: 1.2
- Текст: 1.5


8. SPACING (Отступы)

Система 4px grid:

xs:  4px
sm:  8px
md:  16px
lg:  24px
xl:  32px
2xl: 48px

Правила:
- Между секциями: 24px
- Между элементами в форме: 16px
- Padding карточек: 16px
- Margin кнопок: 8px


9. ИКОНКИ

Источник: Unicode Emoji (встроенные)

Стандартные иконки:

Действия:
✏️ Редактировать
🗑️ Удалить
👁️ Просмотр
📥 Загрузить
📤 Экспорт
🔄 Обновить
➕ Добавить
✅ Подтвердить
❌ Отменить

Статусы:
🟢 Активно
🔴 Неактивно
🟡 Ожидание
⚪ Новое

Объекты:
📡 Канал
📦 Товар
📋 Заказ
🏢 Юр.лицо
🏪 Склад
🚚 Доставка

10. СОСТОЯНИЯ UI

10.1 Загрузка

Spinner:

with st.spinner("Загрузка..."):
    # код

Skeleton (для таблиц):

┌───┬──────────────────┬────────┐
│ ▓ │ ▓▓▓▓▓▓▓▓▓        │ ▓▓▓▓   │
│ ▓ │ ▓▓▓▓▓▓▓          │ ▓▓▓    │
└───┴──────────────────┴────────┘

10.2 Пустое состояние

Формат:

┌─────────────────────────────────┐
│         📋                      │
│   Нет данных                    │
│   Добавьте первую запись        │
│   [+ Добавить]                  │
└─────────────────────────────────┘

10.3 Ошибка

Формат:

st.error("❌ Ошибка: текст ошибки")

11. RESPONSIVE (Адаптивность)

Брейкпоинты:

Mobile:  < 768px   (не поддерживается)
Tablet:  768-1024px (минимально)
Desktop: > 1024px   (основной)

Правила:
- Приложение оптимизировано для Desktop
- Минимальная ширина: 1024px
- Таблицы: горизонтальный скролл если нужно


12. ИНТЕРАКТИВНОСТЬ

12.1 Hover эффекты

Кнопки: Затемнение на 10%
Строки таблицы: Фон #F3F4F6
Ссылки: Подчёркивание

12.2 Transitions

Стандартная анимация:

transition: all 0.15s ease-in-out;

Используется для:
- Hover состояний
- Раскрытия/скрытия элементов


13. STREAMLIT-СПЕЦИФИЧНЫЕ ПРАВИЛА

13.1 Page Config

Всегда в начале файла:

st.set_page_config(
    page_title="Marketplace MVP",
    page_icon="🛒",
    layout="wide",
    initial_sidebar_state="expanded"
)

13.2 Custom CSS

Создать файл: /home/claude-helper/marketplace-mvp/styles/main.css

Подключить:

def load_css():
    with open('styles/main.css') as f:
        st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)

13.3 Избегать


14. ПАТТЕРНЫ КОДА

14.1 Структура страницы

"""
Название страницы - краткое описание
"""
import streamlit as st
from core.auth import check_authentication

# Проверка авторизации
if not check_authentication():
    st.warning("⚠️ Требуется авторизация")
    st.stop()

# Загрузка стилей
load_css()

# Заголовок
st.title("📡 Название страницы")

# Логика списка vs деталей
if 'selected_id' in st.session_state:
    show_detail_page()
else:
    show_list_page()

14.2 Таблица с сортировкой

def show_sortable_table(data, columns):
    # Сортировка
    if 'sort_column' not in st.session_state:
        st.session_state.sort_column = columns[0]
        st.session_state.sort_direction = 'asc'

    # Заголовки
    cols = st.columns(len(columns))
    for i, col_name in enumerate(columns):
        with cols[i]:
            if st.button(
                sortable_header(col_name,
                    st.session_state.sort_column,
                    st.session_state.sort_direction),
                key=f"sort_{col_name}"
            ):
                if st.session_state.sort_column == col_name:
                    st.session_state.sort_direction = 'desc' if st.session_state.sort_direction == 'asc' else 'asc'
                else:
                    st.session_state.sort_column = col_name
                    st.session_state.sort_direction = 'asc'
                st.rerun()

    # Данные
    sorted_data = sorted(data,
        key=lambda x: x[st.session_state.sort_column],
        reverse=(st.session_state.sort_direction == 'desc'))

    for row in sorted_data:
        cols = st.columns(len(columns))
        for i, col_name in enumerate(columns):
            with cols[i]:
                st.write(row[col_name])

15. ЧЕКЛИСТ ДЛЯ НОВОЙ СТРАНИЦЫ


РЕАЛИЗАЦИЯ

Этап 1: Базовые компоненты

  1. Создать /styles/main.css с основными стилями
  2. Создать /components/table.py - компонент таблицы
  3. Создать /components/buttons.py - стандартные кнопки
  4. Создать /utils/navigation.py - хелперы навигации

Этап 2: Применение к страницам

  1. Каналы - эталонная страница
  2. Товары
  3. Заказы
  4. Аналитика
  5. Настройки

Этап 3: Доработка

  1. Тестирование на разных разрешениях
  2. Оптимизация производительности
  3. Документирование компонентов

16. NAMING CONVENTIONS (Именование)

16.1 Файлы и папки

Структура проекта:

/home/claude-helper/marketplace-mvp/
├── app.py                      # Главная точка входа
├── config.yaml                 # Конфигурация
├── requirements.txt            # Зависимости
├── STYLE-GUIDE.md             # Этот файл
├── PROJECT-MASTER.txt         # Мастер-файл проекта
├── CLAUDE.md                  # Документация для Claude
├── core/                      # Ядро приложения
│   ├── __init__.py
│   ├── auth.py               # Авторизация
│   ├── config.py             # Настройки
│   └── database.py           # БД
├── database/                  # Модели данных
│   ├── __init__.py
│   └── models.py
├── modules/                   # Бизнес-логика
│   ├── api/                  # API интеграции
│   │   ├── ozon.py
│   │   └── wildberries.py
│   ├── legal_entities.py     # Работа с юр.лицами
│   └── test_data.py          # Тестовые данные
├── pages/                     # Страницы Streamlit
│   ├── 01_🏠_Главная.py
│   ├── 02_📡_Каналы.py
│   ├── 03_📦_Товары.py
│   ├── 04_📋_Заказы.py
│   ├── 05_📊_Аналитика.py
│   ├── 06_⚙️_Настройки.py
│   └── 07_🧪_Тесты.py
├── components/                # UI компоненты (будущее)
│   ├── table.py
│   ├── buttons.py
│   └── forms.py
├── styles/                    # CSS стили
│   └── main.css
├── utils/                     # Утилиты
│   ├── navigation.py
│   └── formatters.py
├── data/                      # Данные
│   └── marketplace.db
└── logs/                      # Логи
    └── app.log

Правила именования:

Python файлы:
- Модули: snake_case.py
- Классы: PascalCase
- Функции: snake_case()
- Константы: UPPER_SNAKE_CASE
- Приватные: _leading_underscore

Страницы Streamlit:

NN_ICON_Название.py

NN - номер для сортировки (01, 02, 03...)
ICON - эмодзи иконка
Название - кириллица или латиница

Пример: 02_📡_Каналы.py

16.2 Переменные и функции

Python:

# ✅ Хорошо
user_id = 1
channel_name = "Ozon Store"
get_current_user()
calculate_total_price()

# ❌ Плохо
userId = 1  # не camelCase
nm = "Ozon"  # неясное имя
getUserData()  # не snake_case

SQL/Database:

# Таблицы: множественное число, snake_case
users
legal_entities
delivery_services

# Колонки: snake_case
user_id
created_at
name_full

Session State:

# Формат: page_feature_data
st.session_state.channel_wizard_step
st.session_state.selected_channel_id
st.session_state.sort_column
st.session_state.filter_status

16.3 UI элементы

IDs и Keys:

# Формат: action_object_identifier
key="edit_channel_123"
key="delete_order_456"
key="sort_name"
key="filter_status"

17. КОММЕНТАРИИ И ДОКУМЕНТАЦИЯ

17.1 Docstrings

Функции:

def get_company_by_inn(inn: str) -> dict:
    """
    Получить данные компании по ИНН из API.

    Args:
        inn: ИНН компании (10 или 12 цифр)

    Returns:
        dict: Данные компании или None если не найдено

    Raises:
        ValueError: Если ИНН невалидный
    """
    pass

Классы:

class OzonAPI:
    """
    Клиент для работы с Ozon Seller API.

    Attributes:
        client_id: ID клиента Ozon
        api_key: API ключ

    Methods:
        get_seller_info(): Получить информацию о продавце
        get_warehouses(): Получить список складов
    """
    pass

17.2 Inline комментарии

Когда писать:
- Сложная бизнес-логика
- Workarounds и хаки
- TODO/FIXME/HACK

# TODO: Добавить кеширование
# FIXME: Не работает для WB API
# HACK: Временное решение до версии 2.0

# Проверяем ИНН по контрольной сумме
if len(inn) == 10:
    # Для юр.лиц
    checksum = calculate_inn_checksum(inn[:9])
else:
    # Для ИП
    checksum = calculate_inn_checksum(inn[:11])

17.3 Секции кода

Разделители:

# ============================================
# КОНСТАНТЫ И НАСТРОЙКИ
# ============================================

APP_NAME = "Marketplace MVP"
VERSION = "1.0"

# ============================================
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
# ============================================

def helper():
    pass

# ============================================
# ОСНОВНОЙ КОД
# ============================================

18. ERROR HANDLING (Обработка ошибок)

18.1 Стратегия

3 уровня:
1. Validation - на входе
2. Try/Except - в коде
3. User Message - на выходе

18.2 Паттерны

API вызовы:

try:
    api = OzonAPI(client_id, api_key)
    seller_info = api.get_seller_info()
    st.success("✅ Подключение успешно")
except OzonAuthError as e:
    st.error(f"❌ Ошибка авторизации: {e}")
except OzonAPIError as e:
    st.error(f"❌ Ошибка API: {e}")
except Exception as e:
    st.error(f"❌ Неизвестная ошибка: {e}")
    if APP_MODE == "test":
        st.exception(e)

Database операции:

session = get_session()
try:
    channel = Channel(...)
    session.add(channel)
    session.commit()
    st.success("✅ Канал создан")
except IntegrityError:
    session.rollback()
    st.error("❌ Канал с таким Client ID уже существует")
except Exception as e:
    session.rollback()
    st.error(f"❌ Ошибка БД: {e}")
finally:
    session.close()

18.3 Пользовательские сообщения

Формат:

[ИКОНКА] [ТИП]: [ОПИСАНИЕ]

✅ Успех: Канал успешно создан
❌ Ошибка: Не удалось подключиться к API
⚠️ Внимание: Данные могут быть устаревшими
ℹ️ Инфо: Для работы нужно настроить каналы

Не писать:
- Технические детали (stack trace)
- "Something went wrong"
- Коды ошибок без описания

Писать:
- Что произошло
- Почему
- Как исправить


19. PERFORMANCE (Производительность)

19.1 Кеширование

Streamlit cache:

@st.cache_data(ttl=3600)  # 1 час
def get_channels_list():
    """Закешированный список каналов"""
    session = get_session()
    try:
        return session.query(Channel).all()
    finally:
        session.close()

@st.cache_resource
def get_database_connection():
    """Singleton подключение к БД"""
    return create_engine(DATABASE_URL)

Когда кешировать:
- Справочные данные (редко меняются)
- Тяжелые вычисления
- API запросы (с осторожностью)

Когда НЕ кешировать:
- Пользовательский ввод
- Операции записи
- Данные в реальном времени

19.2 Pagination

Для больших списков (>100 записей):

PAGE_SIZE = 50

if 'page' not in st.session_state:
    st.session_state.page = 1

total_records = session.query(Order).count()
total_pages = (total_records + PAGE_SIZE - 1) // PAGE_SIZE

offset = (st.session_state.page - 1) * PAGE_SIZE
orders = session.query(Order).limit(PAGE_SIZE).offset(offset).all()

# Пагинатор
col1, col2, col3 = st.columns([1, 2, 1])
with col1:
    if st.button("← Назад", disabled=(st.session_state.page == 1)):
        st.session_state.page -= 1
        st.rerun()
with col2:
    st.write(f"Страница {st.session_state.page} из {total_pages}")
with col3:
    if st.button("Вперёд →", disabled=(st.session_state.page == total_pages)):
        st.session_state.page += 1
        st.rerun()

19.3 Lazy Loading

Подгружать данные по требованию:

# Сначала показываем основной список
for channel in channels:
    with st.expander(channel.name):
        if st.button("Показать детали", key=f"details_{channel.id}"):
            # Только сейчас грузим детали
            warehouses = load_warehouses(channel.id)
            st.write(warehouses)

20. SECURITY (Безопасность)

20.1 Аутентификация

Проверка на каждой странице:

from core.auth import check_authentication

if not check_authentication():
    st.warning("⚠️ Требуется авторизация")
    st.stop()

20.2 Хранение секретов

НЕ коммитить:
- API ключи
- Пароли
- Токены
- Секретные ключи

Использовать:

# .gitignore
config.yaml
.env
*.db

Хранить в:
- config.yaml (НЕ в git)
- Environment variables
- Encrypted database fields

20.3 Validation

Всегда проверять:

def create_channel(client_id: str, api_key: str):
    # Валидация
    if not client_id or len(client_id) < 6:
        raise ValueError("Client ID должен быть минимум 6 символов")

    if not api_key or len(api_key) < 20:
        raise ValueError("API ключ слишком короткий")

    # Sanitization
    client_id = client_id.strip()

    # Создание
    channel = Channel(client_id=client_id, api_key=api_key)

21. TESTING (Тестирование)

21.1 Типы тестов

1. Playwright E2E:

// Тестируем UI flow
await page.fill('input[type="text"]', 'admin');
await page.click('button:has-text("Login")');
await expect(page.locator('text=Welcome')).toBeVisible();

2. Python Unit:

def test_inn_validation():
    assert validate_inn("1234567890") == True
    assert validate_inn("123") == False

3. Ручное тестирование:
- Чеклист основных сценариев
- Тестирование на разных браузерах

21.2 Test Coverage

Минимум:
- Критичные бизнес-процессы (авторизация, создание заказа)
- API интеграции
- Расчеты и валидация

Не обязательно:
- UI компоненты (Streamlit сам протестирован)
- Simple CRUD


22. LOGGING (Логирование)

22.1 Уровни

import logging

logging.debug("Детальная инфа для разработки")
logging.info("Обычные события (старт/стоп)")
logging.warning("Что-то странное но не критично")
logging.error("Ошибка, нужно разобраться")
logging.critical("Всё сломалось!")

22.2 Формат

# core/config.py
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
    handlers=[
        logging.FileHandler('logs/app.log'),
        logging.StreamHandler()
    ]
)

Пример:

2025-11-08 12:34:56 [INFO] modules.api.ozon: Подключение к Ozon API (client_id=123456)
2025-11-08 12:34:57 [ERROR] modules.api.ozon: Ошибка авторизации: Invalid API key

22.3 Что логировать

ДА:
- API запросы и ответы
- Создание/изменение записей в БД
- Ошибки и исключения
- Важные бизнес-события

НЕТ:
- Пароли и API ключи
- Персональные данные
- Каждый запрос к БД (в production)


23. ВЕРСИОНИРОВАНИЕ

23.1 Semantic Versioning

Формат: MAJOR.MINOR.PATCH

1.0.0 - Первая стабильная версия
1.1.0 - Добавлена новая фича
1.1.1 - Исправлен баг
2.0.0 - Breaking changes

Где указывать:

# core/config.py
VERSION = "1.0.0"

# app.py
st.sidebar.write(f"v{VERSION}")

23.2 Changelog

Файл: CHANGELOG.md

# Changelog

## [1.1.0] - 2025-11-08

### Added
- Компактные таблицы с сортировкой
- Страница управления каналом

### Changed
- Улучшена навигация

### Fixed
- Исправлена ошибка авторизации

24. ACCESSIBILITY (Доступность)

24.1 Контрастность

Минимум WCAG AA:
- Текст: контраст 4.5:1
- Заголовки: контраст 3:1

Проверить:

Текст #374151 на фоне #FFFFFF = 12.63:1 ✅
Кнопка #3B82F6 на фоне #FFFFFF = 4.56:1 ✅

24.2 Keyboard Navigation

Должно работать:
- Tab - переход между элементами
- Enter - активация кнопок/ссылок
- Escape - закрытие модалов

Streamlit:

# Автоматически работает для:
st.button()
st.text_input()
st.selectbox()

24.3 Screen Readers

Альт-тексты:

# НЕ ТОЛЬКО иконки
st.button("🗑️")  # ❌

# Добавить текст
st.button("🗑️ Удалить")  # ✅

25. INTERNATIONALIZATION (i18n)

25.1 Подготовка

Сейчас: Только русский язык
Будущее: Мультиязычность

Структура:

# locales/ru.py
MESSAGES = {
    "login.title": "Вход в систему",
    "login.username": "Имя пользователя",
    "login.password": "Пароль",
    "button.save": "Сохранить",
    "button.cancel": "Отмена",
}

# locales/en.py
MESSAGES = {
    "login.title": "Sign In",
    "login.username": "Username",
    "login.password": "Password",
    "button.save": "Save",
    "button.cancel": "Cancel",
}

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

from locales import get_message

st.title(get_message("login.title"))

25.2 Даты и числа

Сейчас:

from datetime import datetime

# Формат даты: DD.MM.YYYY
date_str = datetime.now().strftime("%d.%m.%Y")

# Числа: 1 234,56
amount = f"{price:,.2f}".replace(",", " ").replace(".", ",")

26. DEPLOYMENT (Развертывание)

26.1 Requirements

Зафиксировать версии:

streamlit==1.28.0
streamlit-authenticator==0.3.3
sqlalchemy==2.0.23
bcrypt==4.1.2

Обновить:

pip freeze > requirements.txt

26.2 Environment

Development:

APP_MODE = "test"
DEBUG = True
DATABASE_URL = "sqlite:///data/marketplace.db"

Production:

APP_MODE = "production"
DEBUG = False
DATABASE_URL = "postgresql://..."

26.3 Healthcheck

# /healthcheck endpoint (если есть)
@app.get("/health")
def health():
    return {
        "status": "ok",
        "version": VERSION,
        "database": check_db_connection()
    }

27. MONITORING (Мониторинг)

27.1 Метрики

Отслеживать:
- Uptime
- Response time
- Error rate
- Active users
- Database size

27.2 Alerts

Настроить уведомления:
- App crashed
- Database full
- API rate limit exceeded


ДОПОЛНИТЕЛЬНЫЕ ДОКУМЕНТЫ

Рекомендуется создать:

  1. README.md - Как запустить проект
  2. CONTRIBUTING.md - Как контрибьютить
  3. API.md - Описание API (если есть)
  4. DATABASE.md - Схема БД
  5. DEPLOYMENT.md - Инструкция по деплою

Этот документ - living document. Обновляется по мере развития проекта.

Последнее обновление: 2025-11-08
Версия: 1.1