AUDIT_REPORT.md: - Full security audit (90 findings reviewed) - 6 critical tasks for immediate fix - 45 recommendations for later - Complete RLS implementation plan (1-2 weeks) - Doppler for secrets management - Winston + Sentry for logging PRD.md: - Personalized beneficiary names feature - custom_name in user_access table - Backend + Frontend tasks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
151 lines
7.9 KiB
Markdown
151 lines
7.9 KiB
Markdown
# PRD — Персонализированные имена beneficiaries
|
||
|
||
## Цель
|
||
Позволить каждому пользователю иметь своё персональное имя для каждого beneficiary. Custodian редактирует оригинальное имя (видно всем по умолчанию), остальные роли — своё `custom_name`.
|
||
|
||
## Контекст
|
||
Сейчас имя beneficiary хранится в `beneficiaries.name` и одинаково для всех пользователей. Нужно добавить возможность персонализации: каждый accessor (кроме custodian) может задать своё имя через `user_access.custom_name`.
|
||
|
||
## User Flow
|
||
|
||
### Flow 1: Custodian редактирует имя (оригинал)
|
||
|
||
| # | Актор | Действие | Система | Результат |
|
||
|---|-------|----------|---------|-----------|
|
||
| 1 | Custodian | Открывает список beneficiaries | GET `/me/beneficiaries` | Показывает `name` из `beneficiaries` таблицы |
|
||
| 2 | Custodian | Нажимает на beneficiary | GET `/me/beneficiaries/:id` | Открывает детали |
|
||
| 3 | Custodian | Нажимает "Edit" | — | Открывает Edit модал |
|
||
| 4 | Custodian | Меняет имя, нажимает "Save" | PATCH `/me/beneficiaries/:id` | Обновляет `beneficiaries.name` |
|
||
| 5 | System | — | Сохраняет в БД | Имя обновлено для ВСЕХ |
|
||
|
||
### Flow 2: Guardian/Caretaker редактирует имя (персональное)
|
||
|
||
| # | Актор | Действие | Система | Результат |
|
||
|---|-------|----------|---------|-----------|
|
||
| 1 | Caretaker | Открывает список beneficiaries | GET `/me/beneficiaries` | Показывает `custom_name` || `name` |
|
||
| 2 | Caretaker | Нажимает на beneficiary | GET `/me/beneficiaries/:id` | Открывает детали |
|
||
| 3 | Caretaker | Нажимает "Edit" | — | Открывает Edit модал |
|
||
| 4 | Caretaker | Меняет имя, нажимает "Save" | PATCH `/me/beneficiaries/:id` | Обновляет `user_access.custom_name` |
|
||
| 5 | System | — | Сохраняет в БД | Имя видно только ЭТОМУ пользователю |
|
||
|
||
### Flow 3: Отображение (все роли)
|
||
|
||
| # | Актор | Действие | Система | Результат |
|
||
|---|-------|----------|---------|-----------|
|
||
| 1 | User | Открывает Dashboard/список | GET `/me/beneficiaries` | — |
|
||
| 2 | System | — | Для каждого: `custom_name \|\| name` | Возвращает `displayName` |
|
||
| 3 | User | Видит список | — | Каждый beneficiary показан с персональным именем |
|
||
|
||
---
|
||
|
||
## Задачи
|
||
|
||
### Backend
|
||
|
||
- [x] **Migration: добавить custom_name в user_access**
|
||
- Путь: `backend/migrations/009_add_custom_name.sql`
|
||
- SQL: `ALTER TABLE user_access ADD COLUMN custom_name VARCHAR(200);`
|
||
- Индекс не нужен (поле не для поиска)
|
||
|
||
- [x] **API: изменить GET /me/beneficiaries (список)**
|
||
- Файл: `backend/src/routes/beneficiaries.js`
|
||
- В SELECT добавить `custom_name` из `user_access`
|
||
- В ответе добавить поле `displayName`: `custom_name || name`
|
||
- Также вернуть `originalName` (из `beneficiaries.name`) для UI
|
||
|
||
- [x] **API: изменить GET /me/beneficiaries/:id (детали)**
|
||
- Файл: `backend/src/routes/beneficiaries.js`
|
||
- Добавить `custom_name` из `user_access` в SELECT
|
||
- В ответе: `displayName`, `originalName`, `customName`
|
||
|
||
- [x] **API: изменить PATCH /me/beneficiaries/:id (обновление)**
|
||
- Файл: `backend/src/routes/beneficiaries.js`
|
||
- Логика:
|
||
- Если `role === 'custodian'` → обновить `beneficiaries.name`
|
||
- Иначе → обновить `user_access.custom_name`
|
||
- Добавить параметр `customName` в body
|
||
|
||
- [x] **Деплой миграции на сервер**
|
||
- SSH: `root@91.98.205.156`
|
||
- Путь: `/var/www/wellnuo-api/`
|
||
- Команда: `node run-migration.js`
|
||
- PM2: `pm2 restart wellnuo-api`
|
||
|
||
### Frontend
|
||
|
||
- [x] **Types: обновить Beneficiary interface**
|
||
- Файл: `types/index.ts` или где определён тип
|
||
- Добавить: `displayName?: string`, `originalName?: string`, `customName?: string`
|
||
|
||
- [x] **API service: обновить типы ответов**
|
||
- Файл: `services/api.ts`
|
||
- Обновить интерфейсы для beneficiary endpoints
|
||
|
||
- [x] **UI: список beneficiaries — показывать displayName**
|
||
- Файл: `app/(tabs)/index.tsx` или где рендерится список
|
||
- Заменить `beneficiary.name` на `beneficiary.displayName || beneficiary.name`
|
||
|
||
- [x] **UI: header в BeneficiaryDetail — показывать displayName**
|
||
- Файл: `app/(tabs)/beneficiaries/[id]/index.tsx`
|
||
- Строка 378: `{beneficiary.name}` → `{beneficiary.displayName || beneficiary.name}`
|
||
|
||
- [x] **UI: Edit модал — разная логика для ролей**
|
||
- Файл: `app/(tabs)/beneficiaries/[id]/index.tsx`
|
||
- Для custodian:
|
||
- Label: "Name"
|
||
- Редактирует `name` (оригинал)
|
||
- Для guardian/caretaker:
|
||
- Label: "Your name for [originalName]"
|
||
- Placeholder: originalName
|
||
- Редактирует `customName`
|
||
- При сохранении отправлять правильное поле
|
||
|
||
- [x] **UI: MockDashboard — показывать displayName**
|
||
- Файл: `components/MockDashboard.tsx`
|
||
- Передавать `displayName` вместо `name`
|
||
|
||
---
|
||
|
||
## Вне scope (не делаем)
|
||
|
||
- Синхронизация имён с голосовым AI (Ultravox) — будет отдельной задачей
|
||
- Интеграция с WellNuo Lite — пока не трогаем
|
||
- Миграция существующих данных — `custom_name` изначально NULL, fallback работает
|
||
|
||
---
|
||
|
||
## Чеклист верификации
|
||
|
||
### Функциональность
|
||
- [x] Custodian может редактировать оригинальное имя (`beneficiaries.name`)
|
||
- [x] Guardian/Caretaker могут редактировать своё персональное имя (`user_access.custom_name`)
|
||
- [x] Список beneficiaries показывает `displayName` (custom_name || name)
|
||
- [x] Header на детальной странице показывает `displayName`
|
||
- [x] Edit модал показывает разные labels для разных ролей
|
||
- [x] При первом открытии (custom_name = NULL) показывается оригинальное имя
|
||
|
||
### Backend
|
||
- [x] Миграция применена без ошибок
|
||
- [x] GET `/me/beneficiaries` возвращает `displayName`, `originalName`
|
||
- [x] GET `/me/beneficiaries/:id` возвращает `displayName`, `originalName`, `customName`
|
||
- [x] PATCH `/me/beneficiaries/:id` правильно определяет что обновлять по роли
|
||
|
||
### Код
|
||
- [x] Нет TypeScript ошибок (`npx tsc --noEmit`)
|
||
- [x] Backend работает без ошибок в логах PM2
|
||
- [x] Нет console.log в продакшн коде (кроме отладочных с `[DEBUG]`)
|
||
|
||
### UI/UX
|
||
- [x] Имена отображаются корректно во всех местах
|
||
- [x] Edit модал понятен для обоих типов редактирования
|
||
- [x] Нет визуальных багов
|
||
|
||
### Edge Cases
|
||
- [x] custom_name = NULL → показывается originalName
|
||
- [x] Пустая строка custom_name = "" → считается как NULL
|
||
- [x] Длинные имена не ломают UI
|
||
|
||
---
|
||
|
||
**Минимальный проходной балл: 8/10**
|