Sergei 71f194cc4d Add Loading & Error UI components for web application
Implemented comprehensive loading and error handling components for the
WellNuo web application with full test coverage.

Components added:
- LoadingSpinner: Configurable spinner with sizes, colors, and variants
  * LoadingOverlay: Full-screen loading with backdrop options
  * InlineLoader: Small inline spinner for buttons
  * PageLoader: Full-page centered loading state

- ErrorMessage: Inline error messages with severity levels
  * FullScreenError: Full-page error states with retry/back actions
  * FieldError: Form field validation errors
  * ErrorBoundaryFallback: Error boundary fallback component
  * EmptyState: Empty data state component

- Skeleton: Animated loading skeletons
  * SkeletonAvatar: Circular avatar skeleton
  * SkeletonText: Multi-line text skeleton
  * SkeletonCard: Card-style skeleton
  * SkeletonList: List of skeleton cards
  * SkeletonTable: Table skeleton with rows/columns
  * SkeletonDashboard: Dashboard-style skeleton layout
  * SkeletonForm: Form skeleton with fields

Technical details:
- Tailwind CSS styling with cn() utility function
- Full accessibility support (ARIA labels, roles)
- Comprehensive test coverage (57 tests, all passing)
- TypeScript strict mode compliance
- Added clsx and tailwind-merge dependencies

Files:
- web/components/ui/LoadingSpinner.tsx
- web/components/ui/ErrorMessage.tsx
- web/components/ui/Skeleton.tsx
- web/components/ui/index.ts
- web/lib/utils.ts
- web/components/ui/__tests__/*.test.tsx

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-31 18:26:28 -08:00
..

Web API Client

This directory contains the web-adapted version of the WellNuo API client.

Key Differences from Mobile API

The web API client (api.ts) is adapted from the mobile version (../../services/api.ts) with the following key changes:

Storage

  • Mobile: Uses expo-secure-store for secure token storage and AsyncStorage for local data
  • Web: Uses browser localStorage for all storage needs

File Uploads

  • Mobile: Uses expo-file-system File API to read files and convert to base64
  • Web: Uses browser File API and FileReader to convert files to base64

Crypto

  • Mobile: Uses react-native-get-random-values polyfill for crypto.getRandomValues
  • Web: Uses native browser crypto.getRandomValues

Dependencies Removed

The following React Native-specific dependencies were removed:

  • expo-secure-storelocalStorage
  • @react-native-async-storage/async-storagelocalStorage
  • expo-file-systemFileReader API
  • react-native-get-random-values → native crypto

Usage

Basic Authentication

import { api } from '@/lib/api';

// Check if email exists
const emailCheck = await api.checkEmail('user@example.com');

// Request OTP code
if (!emailCheck.data?.exists) {
  await api.requestOTP('user@example.com');
}

// Verify OTP
const verifyResult = await api.verifyOTP('user@example.com', '123456');
if (verifyResult.ok) {
  console.log('Logged in!', verifyResult.data.token);
}

// Check authentication status
const isAuth = await api.isAuthenticated();

// Logout
await api.logout();

Working with Beneficiaries

// Get all beneficiaries
const beneficiariesResult = await api.getAllBeneficiaries();
if (beneficiariesResult.ok) {
  const beneficiaries = beneficiariesResult.data;
}

// Get single beneficiary
const beneficiaryResult = await api.getWellNuoBeneficiary(123);

// Create new beneficiary
const createResult = await api.createBeneficiary({
  name: 'John Doe',
  phone: '+1234567890',
  address: '123 Main St',
});

// Update beneficiary avatar
const file = new File([blob], 'avatar.jpg', { type: 'image/jpeg' });
await api.updateBeneficiaryAvatar(123, file);

// Delete beneficiary
await api.deleteBeneficiary(123);

Profile Management

// Get user profile
const profile = await api.getProfile();

// Update profile
await api.updateProfile({
  firstName: 'Jane',
  lastName: 'Doe',
  phone: '+1234567890',
});

// Update profile avatar
const avatarFile = new File([blob], 'avatar.jpg', { type: 'image/jpeg' });
await api.updateProfileAvatar(avatarFile);

Error Handling

All API methods return an ApiResponse<T> object:

interface ApiResponse<T> {
  data?: T;
  error?: ApiError;
  ok: boolean;
}

interface ApiError {
  message: string;
  code?: string;
  status?: number;
}

Example error handling:

const result = await api.getAllBeneficiaries();

if (!result.ok) {
  console.error(result.error?.message);

  // Handle specific error codes
  if (result.error?.code === 'UNAUTHORIZED') {
    // Redirect to login
  }

  if (result.error?.code === 'NETWORK_ERROR') {
    // Show offline message
  }
}

Unauthorized Callback

Set up automatic logout on 401 responses:

import { setOnUnauthorizedCallback } from '@/lib/api';

setOnUnauthorizedCallback(() => {
  // Clear auth state and redirect to login
  router.push('/login');
});

Type Definitions

All types are re-exported from the main types directory (../../types/index.ts) to ensure consistency between mobile and web apps.

Import types like this:

import type { Beneficiary, User, ApiResponse } from '@/types';

Testing

Tests are located in __tests__/lib/api.test.ts.

Run tests:

npm test

Run specific test file:

npm test -- api.test.ts

API Endpoints

WellNuo Backend API

Base URL: https://wellnuo.smartlaunchhub.com/api

  • /auth/check-email - Check if email exists
  • /auth/request-otp - Send OTP code
  • /auth/verify-otp - Verify OTP and get JWT
  • /auth/me - Get user profile
  • /auth/profile - Update user profile
  • /auth/avatar - Update user avatar
  • /me/beneficiaries - List beneficiaries
  • /me/beneficiaries/:id - Get/update/delete beneficiary
  • /me/beneficiaries/:id/avatar - Update beneficiary avatar

Legacy API (Dashboard)

Base URL: https://eluxnetworks.net/function/well-api/api

Used for:

  • Developer Mode / WebView dashboard
  • Real sensor data visualization
  • Legacy device management

Browser Compatibility

The web API client works in all modern browsers that support:

  • localStorage (all modern browsers)
  • crypto.getRandomValues (all modern browsers)
  • fetch API (all modern browsers)
  • FileReader API (all modern browsers)

No polyfills required for target browsers (Chrome 70+, Edge 79+, Opera 57+).