- 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>
454 lines
18 KiB
Markdown
454 lines
18 KiB
Markdown
# WellNuo - Project Architecture
|
||
|
||
## API-First Architecture
|
||
|
||
**IMPORTANT: This project uses an API-first approach. NO local storage for business data!**
|
||
|
||
### Key Principles
|
||
|
||
1. **All beneficiary data comes from the remote API only**
|
||
- No AsyncStorage for beneficiaries
|
||
- No local caching of beneficiary lists
|
||
- Every CRUD operation goes through the WellNuo API
|
||
|
||
2. **Single Source of Truth: WellNuo Backend**
|
||
- All beneficiary data is stored in PostgreSQL (hosted on eluxnetworks.net)
|
||
- Changes are immediately persisted to the server
|
||
- App always fetches fresh data on screen focus
|
||
|
||
### API Endpoints
|
||
|
||
#### WellNuo API (Primary)
|
||
Base URL: `https://wellnuo.smartlaunchhub.com/api`
|
||
|
||
| Method | Endpoint | Description |
|
||
|--------|----------|-------------|
|
||
| GET | `/me/beneficiaries` | List all beneficiaries for current user |
|
||
| GET | `/me/beneficiaries/:id` | Get single beneficiary details |
|
||
| POST | `/me/beneficiaries` | Create new beneficiary |
|
||
| PATCH | `/me/beneficiaries/:id` | Update beneficiary |
|
||
| DELETE | `/me/beneficiaries/:id` | Remove beneficiary access |
|
||
| POST | `/me/beneficiaries/:id/activate` | Activate device for beneficiary |
|
||
| GET | `/auth/me` | Get current user profile with beneficiaries |
|
||
| PATCH | `/auth/profile` | Update user profile (firstName, lastName, phone) |
|
||
|
||
Authentication: Bearer token (JWT from `/auth/verify-otp`)
|
||
|
||
#### Legacy API (WebView Dashboard)
|
||
Base URL: `https://eluxnetworks.net/function/well-api/api`
|
||
|
||
Used only for:
|
||
- Developer Mode / WebView dashboard
|
||
- Real sensor data visualization (from NDK devices)
|
||
|
||
### Data Flow
|
||
|
||
```
|
||
┌─────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
||
│ React App │ ──▶ │ WellNuo Backend │ ──▶ │ PostgreSQL │
|
||
│ (Expo) │ ◀── │ (Express.js) │ ◀── │ (eluxnetworks.net)│
|
||
└─────────────┘ └──────────────────┘ └──────────────────┘
|
||
```
|
||
|
||
### Database Schema
|
||
|
||
- `users` - User accounts (email, password hash)
|
||
- `person_details` - Beneficiary profiles (firstName, lastName, avatar, etc.)
|
||
- `user_access` - Links accessor_id → beneficiary_id with role (owner/viewer)
|
||
- `subscriptions` - Subscription status for beneficiaries
|
||
- `devices` - IoT devices linked to beneficiaries
|
||
|
||
### What NOT to do
|
||
|
||
- ❌ Don't use `localBeneficiaries` from BeneficiaryContext for actual data
|
||
- ❌ Don't save beneficiary data to AsyncStorage
|
||
- ❌ Don't cache API responses locally for offline use
|
||
- ❌ Don't assume data is available without API call
|
||
- ❌ Don't save user profile data (name, phone) to SecureStore
|
||
- ❌ Don't use `userName` from SecureStore - it's legacy!
|
||
|
||
### What TO do
|
||
|
||
- ✅ Always fetch fresh data via `api.getAllBeneficiaries()` or `api.getWellNuoBeneficiary(id)`
|
||
- ✅ Get user profile via `api.getMe()`, update via `api.updateProfile()` (calls `/auth/profile`)
|
||
- ✅ Use `useFocusEffect` to reload data when screen gains focus
|
||
- ✅ Handle loading and error states for all API calls
|
||
- ✅ Show appropriate feedback on successful operations
|
||
- ✅ Only store in SecureStore: `accessToken`, `userId`, `userEmail` (technical auth data)
|
||
|
||
## Navigation Controller (Centralized Routing)
|
||
|
||
All navigation decisions are centralized in `services/NavigationController.ts`.
|
||
|
||
**IMPORTANT: NavigationController is NOT automatic middleware!**
|
||
- It does NOT intercept or control all navigation automatically
|
||
- It's a utility that must be EXPLICITLY called at specific points in the app
|
||
- You must manually call `nav.navigateAfterLogin()`, `nav.navigateAfterAddBeneficiary()`, etc.
|
||
- Regular navigation (e.g., `router.push('/profile')`) works independently
|
||
|
||
### When to use NavigationController:
|
||
- ✅ After OTP verification (login flow)
|
||
- ✅ After creating a new beneficiary
|
||
- ✅ After completing purchase/demo selection
|
||
- ✅ When you need to determine the "correct next screen" based on user state
|
||
|
||
### When NOT needed:
|
||
- ❌ Simple screen-to-screen navigation (just use `router.push()`)
|
||
- ❌ Tab navigation (handled by Expo Router)
|
||
- ❌ Back navigation (handled automatically)
|
||
|
||
### Key Files
|
||
- `services/NavigationController.ts` - All routing logic and conditions
|
||
- `hooks/useNavigationFlow.ts` - React hook for easy usage in components
|
||
|
||
### Navigation Flow After Login
|
||
|
||
```
|
||
User enters OTP code
|
||
↓
|
||
Has firstName in profile?
|
||
│
|
||
NO → /(auth)/enter-name
|
||
│
|
||
YES → Has beneficiaries?
|
||
│
|
||
NO → /(auth)/add-loved-one
|
||
│
|
||
YES → Check equipment status of first beneficiary
|
||
├── none → /(auth)/purchase
|
||
├── ordered/shipped → /(tabs)/beneficiaries/:id/equipment
|
||
├── delivered → /(auth)/activate
|
||
└── active/demo → /(tabs)/dashboard
|
||
```
|
||
|
||
### Navigation After Adding Beneficiary
|
||
|
||
```
|
||
Beneficiary created in DB
|
||
↓
|
||
User already has WellNuo devices?
|
||
│
|
||
NO → /(auth)/purchase (buy equipment)
|
||
│
|
||
YES → /(auth)/activate (connect existing device)
|
||
```
|
||
|
||
### Navigation After Purchase
|
||
|
||
```
|
||
Payment completed / Demo selected
|
||
↓
|
||
Demo mode?
|
||
│
|
||
YES → /(auth)/activate (immediate activation)
|
||
│
|
||
NO → /(tabs)/beneficiaries/:id/equipment (track delivery)
|
||
```
|
||
|
||
### Usage Example
|
||
|
||
```typescript
|
||
import { useNavigationFlow } from '@/hooks/useNavigationFlow';
|
||
|
||
function MyScreen() {
|
||
const nav = useNavigationFlow();
|
||
|
||
// After login - automatically determines correct screen
|
||
nav.navigateAfterLogin(profile, beneficiaries);
|
||
|
||
// After adding beneficiary
|
||
nav.navigateAfterAddBeneficiary(42, hasExistingDevices);
|
||
|
||
// After purchase
|
||
nav.navigateAfterPurchase(42, { demo: true });
|
||
|
||
// Quick shortcuts
|
||
nav.goToDashboard();
|
||
nav.goToPurchase(beneficiaryId);
|
||
nav.goToActivate(beneficiaryId);
|
||
}
|
||
```
|
||
|
||
### Available Routes (ROUTES constant)
|
||
|
||
```typescript
|
||
ROUTES.AUTH.LOGIN // /(auth)/login
|
||
ROUTES.AUTH.VERIFY_OTP // /(auth)/verify-otp
|
||
ROUTES.AUTH.ENTER_NAME // /(auth)/enter-name
|
||
ROUTES.AUTH.ADD_LOVED_ONE // /(auth)/add-loved-one
|
||
ROUTES.AUTH.PURCHASE // /(auth)/purchase
|
||
ROUTES.AUTH.ACTIVATE // /(auth)/activate
|
||
|
||
ROUTES.TABS.DASHBOARD // /(tabs)/dashboard
|
||
ROUTES.TABS.BENEFICIARIES // /(tabs)/beneficiaries
|
||
ROUTES.TABS.CHAT // /(tabs)/chat
|
||
ROUTES.TABS.VOICE // /(tabs)/voice
|
||
ROUTES.TABS.PROFILE // /(tabs)/profile
|
||
|
||
ROUTES.BENEFICIARY.DETAIL(id) // /(tabs)/beneficiaries/:id
|
||
ROUTES.BENEFICIARY.SUBSCRIPTION(id) // /(tabs)/beneficiaries/:id/subscription
|
||
ROUTES.BENEFICIARY.EQUIPMENT(id) // /(tabs)/beneficiaries/:id/equipment
|
||
ROUTES.BENEFICIARY.SHARE(id) // /(tabs)/beneficiaries/:id/share
|
||
```
|
||
|
||
## Development
|
||
|
||
### Server Location
|
||
- Production: `root@91.98.205.156:/var/www/wellnuo-api/`
|
||
- PM2 process: `wellnuo-api`
|
||
|
||
### Key Files
|
||
- `services/api.ts` - All API methods
|
||
- `services/NavigationController.ts` - Centralized routing logic
|
||
- `hooks/useNavigationFlow.ts` - Navigation hook for components
|
||
- `contexts/BeneficiaryContext.tsx` - Beneficiary state management (current selection only)
|
||
- `app/(tabs)/beneficiaries/` - Beneficiary screens
|
||
|
||
### Running Locally
|
||
```bash
|
||
# Start Expo on port 8081 with iPhone 16 Pro Max
|
||
IOS_SIMULATOR_UDID=6BB240A2-0F2F-41E4-B568-9FFAF9B7FBA2 npx expo start --port 8081 --ios
|
||
```
|
||
|
||
## Specs (Спецификации задач)
|
||
|
||
**Структурированные спецификации для планирования и отслеживания задач.**
|
||
|
||
### Структура
|
||
```
|
||
specs/
|
||
├── wellnuo/ # Основное приложение
|
||
├── wellnuo-lite/ # Lite версия
|
||
├── shared/ # Общие задачи (backend, API)
|
||
└── templates/ # Шаблоны
|
||
```
|
||
|
||
### Типы спек
|
||
- `FEATURE-XXX` — новая функциональность
|
||
- `BUG-XXX` — исправление бага
|
||
- `TASK-XXX` — техническая задача
|
||
|
||
### Как работать со спеками
|
||
|
||
**Создать спеку:**
|
||
```
|
||
Создай спеку для фичи "название" в specs/wellnuo/
|
||
```
|
||
|
||
**Начать работу:**
|
||
```
|
||
Прочитай specs/wellnuo/FEATURE-001-voice-integration.md и начни реализацию
|
||
```
|
||
|
||
**Продолжить:**
|
||
```
|
||
Продолжи по спеке FEATURE-001 с шага 3
|
||
```
|
||
|
||
**Обновить статус:**
|
||
```
|
||
Отметь шаги 1-3 как выполненные в FEATURE-001
|
||
```
|
||
|
||
### Важно
|
||
- Спека = план работы. Claude следует шагам из Implementation Steps
|
||
- Статусы: 🔴 Not Started | 🟡 In Progress | 🟢 Done
|
||
- Подробности в `specs/README.md`
|
||
|
||
---
|
||
|
||
## Правила разработки (Development Approach)
|
||
|
||
**Методология и подход к качеству кода и тестированию.**
|
||
|
||
### ГЛАВНОЕ ПРАВИЛО: НЕ ДОБАВЛЯЙ НИЧЕГО БЕЗ СОГЛАСОВАНИЯ
|
||
|
||
**ЗАПРЕЩЕНО без явного согласования с юзером:**
|
||
- ❌ Добавлять новые UI элементы (бейджи, кнопки, тексты)
|
||
- ❌ Менять поведение которое юзер не просил менять
|
||
- ❌ "Улучшать" что-то по своей инициативе
|
||
- ❌ Интерпретировать данные API и решать как их показывать
|
||
|
||
**Если видишь что-то непонятное (например `equipmentStatus: "demo"`):**
|
||
1. СПРОСИ юзера: "Что это значит? Как это должно отображаться?"
|
||
2. НЕ ПРИДУМЫВАЙ сам UI решения
|
||
3. НЕ ДОБАВЛЯЙ бейджи/тексты которые юзер не просил
|
||
|
||
**Твоя задача — делать ТОЛЬКО то что просят, не больше.**
|
||
|
||
---
|
||
|
||
### Правило: ЧИТАЙ КОД ПЕРЕД ИЗМЕНЕНИЯМИ
|
||
|
||
1. **Всегда читай существующий код компонента перед изменением**
|
||
- Понять текущую логику, state machine, conditional rendering
|
||
- Не добавлять логику которая уже есть
|
||
- Не ломать существующее поведение
|
||
|
||
2. **Проследи весь user flow, не только один экран**
|
||
- Если фиксишь список — проверь как это влияет на detail page
|
||
- Если фиксишь backend — проверь как UI отображает новые данные
|
||
- Backend fix без проверки UI — это НЕ завершённый fix
|
||
|
||
### Правило edge cases: ПРОВЕРЯЙ ВСЕ СОСТОЯНИЯ
|
||
|
||
Перед тем как сказать "готово", проверь UI для ВСЕХ возможных состояний:
|
||
- Happy path (всё работает)
|
||
- Пустое состояние (нет данных)
|
||
- Ошибка (API недоступен)
|
||
- Специальные режимы (demo mode, trial, expired и т.д.)
|
||
|
||
### Не проси юзера тестить пока сам не проверил
|
||
|
||
1. Сначала проверь API ответ (curl)
|
||
2. Потом проверь что UI правильно отображает этот ответ
|
||
3. Потом сделай скриншот и убедись
|
||
4. Только потом говори юзеру "готово, проверь"
|
||
|
||
### Частые ошибки которых НЕЛЬЗЯ допускать
|
||
|
||
- ❌ Фиксить backend и НЕ проверять frontend
|
||
- ❌ Проверить только один сценарий и сказать "готово"
|
||
- ❌ Не читать существующий код и добавлять дублирующую логику
|
||
- ❌ Игнорировать edge cases (demo mode, expired subscription, etc.)
|
||
- ❌ Делать изменения "вслепую" без понимания текущей логики
|
||
|
||
---
|
||
|
||
## Julia AI Voice Agent (LiveKit)
|
||
|
||
### Расположение скрипта
|
||
|
||
**Python Agent для голосового ассистента Julia находится здесь:**
|
||
```
|
||
/Users/sergei/Desktop/WellNuo/WellNuoLite/julia-agent/julia-ai/src/agent.py
|
||
```
|
||
|
||
### Архитектура Voice Assistant
|
||
|
||
```
|
||
┌─────────────┐ ┌────────────────────────┐ ┌─────────────────┐ ┌──────────────────┐
|
||
│ Mobile App │ ──▶ │ Julia Token Server │ ──▶ │ LiveKit Cloud │ ──▶ │ Python Agent │
|
||
│ (Expo) │ │ wellnuo.smartlaunchhub │ │ (Agents Cloud) │ │ (agent.py) │
|
||
└─────────────┘ └────────────────────────┘ └─────────────────┘ └──────────────────┘
|
||
│ │ │
|
||
│ │ metadata: {deploymentId, beneficiaryNamesDict} │
|
||
│ └──────────────────────────────────────────────────────┘
|
||
│ │
|
||
│ ▼
|
||
│ ┌──────────────────┐
|
||
│ │ WellNuo API │
|
||
└─────────────────────────────────────────────────────────────────│ eluxnetworks.net│
|
||
text chat goes directly here └──────────────────┘
|
||
```
|
||
|
||
### SINGLE_DEPLOYMENT_MODE
|
||
|
||
Флаг `SINGLE_DEPLOYMENT_MODE` контролирует отправку `beneficiary_names_dict`:
|
||
|
||
| Режим | `SINGLE_DEPLOYMENT_MODE` | Что отправляется |
|
||
|-------|--------------------------|------------------|
|
||
| Lite | `true` | только `deployment_id` |
|
||
| Full | `false` | `deployment_id` + `beneficiary_names_dict` |
|
||
|
||
Файлы с флагом:
|
||
- `WellNuoLite/app/(tabs)/chat.tsx` — текстовый чат
|
||
- `WellNuoLite/services/livekitService.ts` — голосовой ассистент
|
||
|
||
### Ключевые файлы
|
||
|
||
| Файл | Назначение |
|
||
|------|------------|
|
||
| `julia-agent/julia-ai/src/agent.py` | Python агент для LiveKit Cloud |
|
||
| `services/livekitService.ts` | Клиент для получения токена |
|
||
| `components/VoiceCall.tsx` | UI голосового звонка |
|
||
|
||
### Серверы
|
||
|
||
| Сервис | URL | Расположение |
|
||
|--------|-----|--------------|
|
||
| Julia Token Server | `https://wellnuo.smartlaunchhub.com/julia` | `root@91.98.205.156:/var/www/julia-token-server/` |
|
||
| WellNuo API | `https://eluxnetworks.net/function/well-api/api` | Внешний сервис |
|
||
| Debug Console | `https://wellnuo.smartlaunchhub.com/debug/` | `root@91.98.205.156:/var/www/wellnuo-debug/` |
|
||
|
||
### Деплой Python агента на LiveKit Cloud
|
||
|
||
**Путь к агенту (локально):**
|
||
```
|
||
/Users/sergei/Desktop/WellNuo/WellNuoLite/julia-agent/julia-ai/
|
||
```
|
||
|
||
**Структура директории:**
|
||
```
|
||
julia-ai/
|
||
├── src/
|
||
│ └── agent.py # Основной Python агент
|
||
├── livekit.toml # Конфигурация LiveKit Cloud
|
||
├── Dockerfile # Для сборки на LiveKit Cloud
|
||
├── pyproject.toml # Python зависимости
|
||
└── AGENTS.md # Документация LiveKit
|
||
```
|
||
|
||
**Текущий Agent ID:** `CA_Yd3qcuYEVKKE`
|
||
**LiveKit Project:** `live-kit-demo-70txlh6a`
|
||
**Region:** `eu-central`
|
||
|
||
#### Редактирование агента
|
||
|
||
```bash
|
||
# Открыть код агента
|
||
code /Users/sergei/Desktop/WellNuo/WellNuoLite/julia-agent/julia-ai/src/agent.py
|
||
```
|
||
|
||
Основные места в agent.py:
|
||
- **Инструкции Julia** — строка ~50-100 (system prompt)
|
||
- **Обработка metadata** — функция `_build_request_data()`
|
||
- **Вызов API** — метод `send_to_wellnuo_api()`
|
||
- **agent_name** — строка 435: `agent_name="julia-ai"`
|
||
|
||
#### Деплой изменений
|
||
|
||
```bash
|
||
cd /Users/sergei/Desktop/WellNuo/WellNuoLite/julia-agent/julia-ai
|
||
|
||
# 1. Проверить что работает локально (опционально)
|
||
uv run python src/agent.py console
|
||
|
||
# 2. Задеплоить на LiveKit Cloud
|
||
lk agent deploy
|
||
|
||
# Это:
|
||
# - Соберёт Docker образ
|
||
# - Запушит в LiveKit Cloud registry
|
||
# - Развернёт новую версию агента
|
||
```
|
||
|
||
#### Полезные команды
|
||
|
||
```bash
|
||
# Список агентов в проекте
|
||
lk agent list
|
||
|
||
# Логи агента (в реальном времени)
|
||
lk agent logs
|
||
|
||
# Логи определённого агента
|
||
lk agent logs --id CA_Yd3qcuYEVKKE
|
||
|
||
# Статус агента
|
||
lk agent list --verbose
|
||
```
|
||
|
||
#### Связка Agent ↔ Token Server
|
||
|
||
Token Server использует имя `julia-ai` для диспетчеризации агента:
|
||
```javascript
|
||
// /var/www/julia-token-server/server.js
|
||
const AGENT_NAME = 'julia-ai'; // Должно совпадать с agent_name в agent.py
|
||
```
|
||
|
||
При создании нового агента:
|
||
1. Измени `agent_name` в `agent.py`
|
||
2. Обнови `AGENT_NAME` в Token Server
|
||
3. Перезапусти Token Server: `pm2 restart julia-token-server`
|