- 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>
18 KiB
WellNuo - Project Architecture
API-First Architecture
IMPORTANT: This project uses an API-first approach. NO local storage for business data!
Key Principles
-
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
-
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 beneficiariesdevices- IoT devices linked to beneficiaries
What NOT to do
- ❌ Don't use
localBeneficiariesfrom 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
userNamefrom SecureStore - it's legacy!
What TO do
- ✅ Always fetch fresh data via
api.getAllBeneficiaries()orapi.getWellNuoBeneficiary(id) - ✅ Get user profile via
api.getMe(), update viaapi.updateProfile()(calls/auth/profile) - ✅ Use
useFocusEffectto 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 conditionshooks/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
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)
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 methodsservices/NavigationController.ts- Centralized routing logichooks/useNavigationFlow.ts- Navigation hook for componentscontexts/BeneficiaryContext.tsx- Beneficiary state management (current selection only)app/(tabs)/beneficiaries/- Beneficiary screens
Running Locally
# 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"):
- СПРОСИ юзера: "Что это значит? Как это должно отображаться?"
- НЕ ПРИДУМЫВАЙ сам UI решения
- НЕ ДОБАВЛЯЙ бейджи/тексты которые юзер не просил
Твоя задача — делать ТОЛЬКО то что просят, не больше.
Правило: ЧИТАЙ КОД ПЕРЕД ИЗМЕНЕНИЯМИ
-
Всегда читай существующий код компонента перед изменением
- Понять текущую логику, state machine, conditional rendering
- Не добавлять логику которая уже есть
- Не ломать существующее поведение
-
Проследи весь user flow, не только один экран
- Если фиксишь список — проверь как это влияет на detail page
- Если фиксишь backend — проверь как UI отображает новые данные
- Backend fix без проверки UI — это НЕ завершённый fix
Правило edge cases: ПРОВЕРЯЙ ВСЕ СОСТОЯНИЯ
Перед тем как сказать "готово", проверь UI для ВСЕХ возможных состояний:
- Happy path (всё работает)
- Пустое состояние (нет данных)
- Ошибка (API недоступен)
- Специальные режимы (demo mode, trial, expired и т.д.)
Не проси юзера тестить пока сам не проверил
- Сначала проверь API ответ (curl)
- Потом проверь что UI правильно отображает этот ответ
- Потом сделай скриншот и убедись
- Только потом говори юзеру "готово, проверь"
Частые ошибки которых НЕЛЬЗЯ допускать
- ❌ Фиксить 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
Редактирование агента
# Открыть код агента
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"
Деплой изменений
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
# - Развернёт новую версию агента
Полезные команды
# Список агентов в проекте
lk agent list
# Логи агента (в реальном времени)
lk agent logs
# Логи определённого агента
lk agent logs --id CA_Yd3qcuYEVKKE
# Статус агента
lk agent list --verbose
Связка Agent ↔ Token Server
Token Server использует имя julia-ai для диспетчеризации агента:
// /var/www/julia-token-server/server.js
const AGENT_NAME = 'julia-ai'; // Должно совпадать с agent_name в agent.py
При создании нового агента:
- Измени
agent_nameвagent.py - Обнови
AGENT_NAMEв Token Server - Перезапусти Token Server:
pm2 restart julia-token-server