WellNuo/PRD-DEPLOYMENT.md
Sergei d453126c89 feat: Room location picker + robster credentials
- Backend: Update Legacy API credentials to robster/rob2
- Frontend: ROOM_LOCATIONS with icons and legacyCode mapping
- Device Settings: Modal picker for room selection
- api.ts: Bidirectional conversion (code ↔ name)
- Various UI/UX improvements across screens

PRD-DEPLOYMENT.md completed (Score: 9/10)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-24 15:22:40 -08:00

251 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PRD — Deployment + Sensors Integration (v2)
## Цель
Обеспечить полную интеграцию: при создании beneficiary автоматически создаётся deployment на Legacy API, к которому затем привязываются BLE сенсоры. Использовать credentials `robster/rob2` (больше прав). Добавить Dropdown для выбора комнаты сенсора.
## Текущее состояние (уже работает!)
### ✅ Что УЖЕ реализовано:
1. **Deployment создаётся автоматически** при создании beneficiary (`beneficiaries.js:445-501`)
2. **Legacy API integration** полностью работает (`legacyAPI.js`)
3. **BLE сенсоры** подключаются и настраиваются (`PRD-SENSORS.md` — все задачи выполнены)
4. **Device Settings** есть поля location/description (TextInput)
### ❌ Что НЕ работает:
1. Credentials `anandk` имеют ограниченные права → нужен `robster/rob2`
2. Location вводится текстом → нужен Dropdown с комнатами
3. `updateDeviceMetadata` отправляет строку вместо числового кода
---
## User Flow
### Flow 1: Создание Beneficiary + Deployment (УЖЕ РАБОТАЕТ)
| # | Актор | Действие | Система | Результат |
|---|-------|----------|---------|-----------|
| 1 | User | Заполняет форму "Add Loved One" | — | Вводит имя, адрес |
| 2 | User | Нажимает "Continue" | POST `/me/beneficiaries` | — |
| 3 | Backend | Создаёт beneficiary в PostgreSQL | INSERT `beneficiaries` | beneficiary_id |
| 4 | Backend | Создаёт deployment в PostgreSQL | INSERT `beneficiary_deployments` | deployment.id |
| 5 | Backend | Авторизуется на Legacy API | `legacyAPI.getLegacyToken()` | legacy_token |
| 6 | Backend | Создаёт deployment на Legacy | `legacyAPI.createLegacyDeployment()` | legacy_deployment_id |
| 7 | Backend | Сохраняет legacy_deployment_id | UPDATE `beneficiary_deployments` | Связь установлена |
| 8 | User | Переходит к purchase/demo | — | Deployment готов |
**Статус:** ✅ Полностью реализовано в `beneficiaries.js:419-501`
### Flow 2: Настройка устройства с Dropdown
| # | Актор | Действие | Система | Результат |
|---|-------|----------|---------|-----------|
| 1 | User | Открывает Device Settings | GET devices | Текущие данные |
| 2 | User | Видит Dropdown "Location" | — | Показывает текущую комнату |
| 3 | User | Выбирает комнату из списка | — | "Bedroom", "Kitchen", etc. |
| 4 | User | Вводит description (опционально) | — | Свободный текст |
| 5 | User | Нажимает "Save" | POST `device_form` | location=102 |
| 6 | System | Сохраняет на Legacy API | — | Обновлено |
---
## Задачи
### Backend (Простые)
- [x] **1. Обновить Legacy API credentials**
- Путь: `backend/.env`
- Изменить:
```
LEGACY_API_USERNAME=robster
LEGACY_API_PASSWORD=rob2
```
- Задеплоить: `scp .env root@91.98.205.156:/var/www/wellnuo-api/.env`
- Перезапустить: `ssh root@91.98.205.156 "pm2 restart wellnuo-api"`
### Frontend (Основная работа)
- [x] **2. Добавить константы ROOM_LOCATIONS в api.ts**
- Путь: `services/api.ts`
- Добавить в начало файла:
```typescript
// Room location codes for Legacy API
export const ROOM_LOCATIONS: Record<string, number> = {
'Bedroom': 102,
'Living Room': 103,
'Kitchen': 104,
'Bathroom': 105,
'Hallway': 106,
'Office': 107,
'Garage': 108,
'Dining Room': 109,
'Basement': 110,
'Other': 200
};
export const LOCATION_NAMES: Record<number, string> = Object.fromEntries(
Object.entries(ROOM_LOCATIONS).map(([k, v]) => [v, k])
);
```
- [x] **3. Исправить updateDeviceMetadata для location codes**
- Путь: `services/api.ts` (строка ~1783)
- Изменить:
```typescript
// БЫЛО:
formData.append('location', updates.location);
// СТАЛО:
if (updates.location !== undefined) {
// Convert room name to location code
const locationCode = ROOM_LOCATIONS[updates.location] || ROOM_LOCATIONS['Other'];
formData.append('location', locationCode.toString());
}
```
- [x] **4. Device Settings: заменить TextInput на Picker**
- Путь: `app/(tabs)/beneficiaries/[id]/device-settings/[deviceId].tsx`
- Импорт: `import { Picker } from '@react-native-picker/picker'`
- Или использовать: `@react-native-community/picker` / кастомный ActionSheet
**Заменить (строки 349-358):**
```tsx
// БЫЛО:
<TextInput
style={styles.editableInput}
value={location}
onChangeText={setLocation}
placeholder="e.g., Living Room, Kitchen..."
placeholderTextColor={AppColors.textMuted}
/>
// СТАЛО:
<View style={styles.pickerContainer}>
<Picker
selectedValue={location}
onValueChange={(value) => setLocation(value)}
style={styles.picker}
>
<Picker.Item label="Select room..." value="" />
<Picker.Item label="Bedroom" value="Bedroom" />
<Picker.Item label="Living Room" value="Living Room" />
<Picker.Item label="Kitchen" value="Kitchen" />
<Picker.Item label="Bathroom" value="Bathroom" />
<Picker.Item label="Hallway" value="Hallway" />
<Picker.Item label="Office" value="Office" />
<Picker.Item label="Garage" value="Garage" />
<Picker.Item label="Dining Room" value="Dining Room" />
<Picker.Item label="Basement" value="Basement" />
<Picker.Item label="Other" value="Other" />
</Picker>
</View>
```
- [x] **5. Конвертировать location code → name при загрузке**
- Путь: `app/(tabs)/beneficiaries/[id]/device-settings/[deviceId].tsx`
- В `loadSensorInfo()` добавить конвертацию:
```typescript
import { LOCATION_NAMES } from '@/services/api';
// При получении sensor:
const locationName = sensor.location
? (LOCATION_NAMES[parseInt(sensor.location)] || sensor.location)
: '';
setLocation(locationName);
```
- [x] **6. Добавить стили для Picker**
- Путь: тот же файл
- Добавить в StyleSheet:
```typescript
pickerContainer: {
backgroundColor: AppColors.background,
borderRadius: BorderRadius.md,
borderWidth: 1,
borderColor: AppColors.border,
overflow: 'hidden',
},
picker: {
height: 50,
color: AppColors.textPrimary,
},
```
- [x] **7. Установить @react-native-picker/picker**
- Команда: `npx expo install @react-native-picker/picker`
---
## Справочник: Location Codes (из legacyAPI.js)
| Код | Название | Описание |
|-----|----------|----------|
| 102 | Bedroom | Спальня |
| 103 | Living Room | Гостиная |
| 104 | Kitchen | Кухня |
| 105 | Bathroom | Ванная |
| 106 | Hallway | Коридор |
| 107 | Office | Кабинет |
| 108 | Garage | Гараж |
| 109 | Dining Room | Столовая |
| 110 | Basement | Подвал |
| 200 | Other | Другое |
---
## Вне scope
- Синхронизация location с голосовым AI (Julia) — отдельная задача
- WellNuo Lite интеграция — пока не трогаем
- Редактирование deployment после создания — пока не нужно
- Front Door (101) — нет в текущем mapping, не добавляем
---
## Чеклист верификации
### Backend
- [x] Credentials обновлены на `robster/rob2` в .env
- [x] PM2 перезапущен
- [x] Тест: создать beneficiary → в логах видно "Created Legacy deployment: XXX"
### Frontend
- [x] Device Settings показывает Picker/Dropdown вместо TextInput для location
- [x] Picker содержит все 10 комнат
- [x] При выборе комнаты — сохраняется location_code (число) на Legacy API
- [x] При загрузке — location_code конвертируется в название
- [x] Description остаётся TextInput
- [x] Сохранение работает без ошибок
### End-to-End Flow
- [x] Создать beneficiary → deployment создан на Legacy API
- [x] Подключить BLE сенсор → привязан к deployment
- [x] Открыть Device Settings → видно Dropdown
- [x] Выбрать "Kitchen" → Save → проверить в Legacy API что location=104
- [x] Перезагрузить экран → показывает "Kitchen"
---
## Риски и Edge Cases
1. **Picker на Android vs iOS** — выглядит по-разному, возможно нужен ActionSheet
2. **Старые данные** — если location уже сохранён как текст "kitchen", не найдётся в LOCATION_NAMES
3. **Нет интернета** — Legacy API недоступен, нужен graceful error
---
## Порядок выполнения
1. ✅ Backend: обновить credentials (5 мин)
2. 🔨 Frontend: добавить константы ROOM_LOCATIONS (5 мин)
3. 🔨 Frontend: исправить updateDeviceMetadata (10 мин)
4. 🔨 Frontend: установить Picker пакет (2 мин)
5. 🔨 Frontend: заменить TextInput на Picker (20 мин)
6. 🔨 Frontend: конвертация code↔name (15 мин)
7. ✅ Тестирование E2E (15 мин)
**Общее время: ~1.5 часа**
---
**Минимальный проходной балл: 8/10**