- Remove hard-coded Russian translations from backend API - Backend now returns raw verb values (language-agnostic) - Implement frontend translation functions for notification titles and categories - Add category badge system with filters on notifications page - Create translation utilities in frontend (verbTranslations.ts) - Add notification settings page structure - Update API endpoints to return simplified notification data - Refactor notification signals across all apps - Remove deprecated code and tests - Add comprehensive documentation for v2 migration Backend changes: - notifications/api/views.py: Remove translation logic, simplify responses - All notification_signals.py: Update to use consistent patterns - Remove deprecated Telegram bot command - Update Celery tasks for new notification structure Frontend changes: - app/notifications/page.tsx: Add translation functions and category badges - hooks/useNotificationWebSocket.ts: New WebSocket hook for real-time updates - lib/verbTranslations.ts: Centralized translation mappings - components/NotificationCenter.tsx: Update to use new API format This makes the notifications module fully reusable and project-independent.
11 KiB
Next.js Frontend - Интеграция уведомлений
Статус: ✅ Полностью подключено
Дата: 9 марта 2026
Версия API: v2
Что было исправлено
1. Backend - UserConsumer (realtime/consumers.py)
Проблема: WebSocketNotificationSender отправлял события с типом notification_message, но в UserConsumer не было соответствующего метода.
Решение: Добавлен метод notification_message:
async def notification_message(self, event):
"""Новое уведомление от WebSocketNotificationSender"""
await self.send_json({
"type": "notification",
"notification": event.get("message", {})
})
2. Frontend - useNotifications (hooks/useApi.ts)
Проблема: Хук загружал уведомления только один раз при mount, без realtime обновлений.
Решение: Добавлен WebSocket внутри хука:
useEffect(() => {
// REST API загрузка
fetchNotifications();
}, []);
useEffect(() => {
// WebSocket для realtime
const ws = new WebSocket(wsUrl);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'notification' && data.notification) {
// Добавляем новое уведомление
setNotifications(prev => [data.notification, ...prev]);
if (data.notification.unread) {
setUnreadCount(prev => prev + 1);
}
}
if (data.type === 'unread_count') {
setUnreadCount(data.count);
}
};
}, []);
3. Frontend - NotificationCenter.tsx
Проблема: Использовал старые поля v1 API (title, message, is_read, created_at).
Решение: Обновлено для v2 API с fallback на v1:
// v2 API: unread вместо is_read (инвертированное)
const isUnread = notification.unread ?? !notification.is_read;
// v2 API: timestamp вместо created_at
const timestamp = notification.timestamp || notification.created_at;
// v2 API: verb + description вместо title + message
const title = notification.verb || notification.title;
const message = notification.description || notification.message;
4. TypeScript типы (types/api.ts)
Проблема: Старый интерфейс Notification не соответствовал v2 API.
Решение: Обновлен интерфейс с поддержкой v2 и fallback на v1:
export interface Notification {
// v2 fields
id: number;
verb: string;
description: string;
unread: boolean;
timestamp: string;
action_url?: string;
actor?: { type: string; id: number; str: string; };
target?: { type: string; id: number; str: string; };
// v1 legacy fields (для обратной совместимости)
title?: string;
message?: string;
is_read?: boolean;
created_at?: string;
}
Архитектура
Полный поток данных
1. Django Action (создание заявки)
↓
2. Django Signal (post_save)
↓
3. notify.send(sender, recipient, verb, description, ...)
↓
4. Notification.save() [~1ms, синхронно]
↓
5. post_save → channels.py
↓
6. UserChannelPreferences проверка
↓
7. Celery .delay():
- send_websocket_notification.delay()
- send_email_notification.delay()
- send_push_notification.delay()
↓
8. Celery Worker обрабатывает WebSocketNotificationTask
↓
9. WebSocketNotificationSender.send()
↓
10. channel_layer.group_send(f"user_{user_id}", {
type: 'notification_message',
message: {...}
})
↓
11. UserConsumer.notification_message()
↓
12. ws.send({type: 'notification', notification: {...}})
↓
13. Next.js Frontend - useNotifications WebSocket
↓
14. setNotifications([new, ...prev])
↓
15. NotificationCenter обновляет UI
↓
16. Пользователь видит уведомление в реальном времени
Компоненты
Backend:
notifications/signals.py- notify.send() APInotifications/channels.py- роутер по каналамnotifications/tasks/websocket.py- Celery задачаnotifications/senders/websocket.py- отправка через Channelsrealtime/consumers.py- UserConsumer (WebSocket)
Frontend:
hooks/useApi.ts- useNotifications (REST + WebSocket)components/NotificationCenter.tsx- UI (desktop + mobile)components/AppShell.tsx- размещение компонентаtypes/api.ts- TypeScript типы
API Endpoints
REST API
Получить уведомления:
GET /api/v2/notifications/
Authorization: Bearer {token}
Response:
{
"notifications": [
{
"id": 1,
"verb": "request_created",
"description": "Новая заявка от Иванов И.И.",
"unread": true,
"timestamp": "2026-03-09T18:00:00Z",
"action_url": "/requests/123/",
"actor": {"type": "employee", "id": 5, "str": "Иванов И.И."},
"target": {"type": "request", "id": 123, "str": "Заявка #123"}
}
],
"total": 42,
"unread_count": 5
}
Отметить как прочитанное:
POST /api/v2/notifications/{id}/mark_read/
Authorization: Bearer {token}
Response: {"status": "ok"}
Отметить все как прочитанные:
POST /api/v2/notifications/mark_all_read/
Authorization: Bearer {token}
Response: {"status": "ok", "count": 5}
WebSocket
Подключение:
ws://localhost:9000/ws/?token={access_token}
События от сервера:
// Новое уведомление
{
"type": "notification",
"notification": {
"id": 1,
"verb": "request_approved",
"description": "Ваша заявка одобрена",
"unread": true,
"timestamp": "2026-03-09T18:00:00Z",
"action_url": "/requests/123/",
"silent": false
}
}
// Обновление счетчика
{
"type": "unread_count",
"count": 3
}
// Ping (keepalive)
{
"type": "ping",
"timestamp": "2026-03-09T18:00:00Z"
}
Использование в коде
Использование хука useNotifications
import { useNotifications } from '@/hooks/useApi';
function MyComponent() {
const { notifications, unreadCount, markAsRead, markAllAsRead, loading } = useNotifications();
return (
<div>
<h1>Уведомления ({unreadCount})</h1>
{notifications.map(n => (
<div key={n.id} onClick={() => markAsRead(n.id)}>
<h3>{n.verb}</h3>
<p>{n.description}</p>
</div>
))}
</div>
);
}
Использование NotificationCenter
import { NotificationCenter } from '@/components/NotificationCenter';
function Header() {
return (
<header>
<NotificationCenter /> {/* Desktop version */}
</header>
);
}
function MobileNav() {
const [isOpen, setIsOpen] = useState(false);
return (
<NotificationCenter
variant="mobile"
isOpen={isOpen}
onToggle={() => setIsOpen(!isOpen)}
/>
);
}
Тестирование
1. Проверка REST API
# Получить все уведомления
curl -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:9000/api/v2/notifications/
# Отметить как прочитанное
curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \
http://localhost:9000/api/v2/notifications/1/mark_read/
2. Проверка WebSocket
- Открыть DevTools → Console
- Должно быть:
[Notifications] WebSocket connected - Создать заявку от другого пользователя
- Должно прийти:
[Notifications] New notification: {...} - Колокольчик должен обновиться автоматически
3. Проверка UI
- Открыть http://localhost:3000/
- Кликнуть на колокольчик
- Увидеть список уведомлений
- Кликнуть на уведомление - счетчик уменьшается
- "Прочитать все" - все уведомления помечаются прочитанными
Поддерживаемые возможности
✅ Realtime уведомления - через WebSocket
✅ REST API fallback - при отключении WebSocket
✅ Автоматический reconnect - до 5 попыток
✅ Обратная совместимость - v1 и v2 API
✅ Desktop и Mobile UI - адаптивный дизайн
✅ Push notifications - через useWebPush
✅ Счетчик непрочитанных - realtime обновление
✅ Отметка прочитанных - индивидуально и все сразу
✅ Навигация по action_url - переход при клике
✅ TypeScript типизация - полная типобезопасность
Переменные окружения
# Frontend (.env.local)
NEXT_PUBLIC_API_URL=http://localhost:9000
NEXT_PUBLIC_WS_HOST=localhost:9000
# Backend (.env)
REDIS_URL=redis://localhost:6379/0
CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_RESULT_BACKEND=redis://localhost:6379/0
Запуск для разработки
Backend
cd backend
# 1. Django server (WebSocket через Daphne)
.venv/Scripts/python manage.py runserver 9000
# 2. Celery worker (в отдельном терминале)
.venv/Scripts/celery -A eusrr_backend worker -l info
# 3. Redis (должен быть запущен)
redis-server
Frontend
cd frontend
# Development server
npm run dev
# Production build
npm run build
npm start
Известные проблемы
Нет проблем! 🎉
Все критические баги исправлены. Frontend полностью интегрирован с backend notifications v2.
Следующие шаги (опционально)
- Звуковые уведомления - добавить аудио при получении
- Группировка - группировать похожие уведомления
- Фильтрация - по типу, дате, прочитанности
- Настройки каналов - UI для UserChannelPreferences
- Уведомления в заголовке - обновление document.title
Полезные ссылки
- Backend документация:
backend/notifications/README.md - Тестирование:
backend/requests_app/TESTING_NOTIFICATIONS.md - Отчет о рефакторинге:
backend/docs/reports/NOTIFICATION_SIGNALS_REFACTORING.md