# User API Implementation (User Management)

This document describes the implementation of the User Management API endpoints for the MaplePress frontend, integrated with the MaplePress backend API.

## Overview

The User API endpoints manage users within a tenant (organization). All operations require both JWT authentication and tenant context via the `X-Tenant-ID` header. Users are scoped to tenants, providing data isolation in the multi-tenant architecture.

## Backend API Endpoints

### Create User
**Endpoint**: `POST /api/v1/users`
**Authentication**: Required (JWT token)
**Tenant Context**: Required (`X-Tenant-ID` header)

### Get User by ID
**Endpoint**: `GET /api/v1/users/{id}`
**Authentication**: Required (JWT token)
**Tenant Context**: Required (`X-Tenant-ID` header)

**Documentation**: `/cloud/maplepress-backend/docs/API.md` (lines 560-660)

## Request/Response Structures

### Create User Request
```json
{
  "email": "jane@techstart.com",
  "name": "Jane Smith"
}
```

**Headers Required:**
- `Content-Type: application/json`
- `Authorization: JWT {access_token}`
- `X-Tenant-ID: {tenant_id}` (required in development mode)

### User Response
```json
{
  "id": "950e8400-e29b-41d4-a716-446655440000",
  "email": "jane@techstart.com",
  "name": "Jane Smith",
  "created_at": "2024-10-24T00:00:00Z",
  "updated_at": "2024-10-24T00:00:00Z"
}
```

## Frontend Implementation

### UserService (`src/services/API/UserService.js`)

Handles all user management operations with tenant context.

**Key Features:**
- Create new users within a tenant
- Retrieve user by ID within tenant context
- Client-side validation (email and name)
- Tenant context support via X-Tenant-ID header
- Response transformation (snake_case to camelCase)
- User-friendly error message mapping

**Methods:**

#### `createUser(userData, tenantId)`
Create a new user within a tenant.

```javascript
import UserService from './services/API/UserService';

const user = await UserService.createUser({
  email: "jane@techstart.com",
  name: "Jane Smith"
}, "850e8400-...");  // tenant ID

console.log(user);
// Output:
// {
//   id: "950e8400-...",
//   email: "jane@techstart.com",
//   name: "Jane Smith",
//   createdAt: Date object
// }
```

**Parameters:**
- `userData.email` (string, required): User's email address
- `userData.name` (string, required): User's full name
- `tenantId` (string, optional): Tenant ID for X-Tenant-ID header

**Returns:**
```javascript
{
  id: string,          // User ID (UUID)
  email: string,       // User email
  name: string,        // User name
  createdAt: Date      // Creation timestamp
}
```

**Throws:**
- "User data is required" - If userData is missing
- "User email is required" - If email is missing
- "User name is required" - If name is missing
- "Invalid email format" - If email format is invalid
- "User email already exists in this tenant." - Email conflict (409)
- "Tenant context required. Please provide X-Tenant-ID header." - Missing tenant context
- "Authentication required. Please log in to continue." - Missing/invalid token

#### `getUserById(userId, tenantId)`
Retrieve user information by ID within tenant context.

```javascript
const user = await UserService.getUserById("950e8400-...", "850e8400-...");
console.log(user.name); // "Jane Smith"
```

**Parameters:**
- `userId` (string, required): User ID (UUID format)
- `tenantId` (string, optional): Tenant ID for X-Tenant-ID header

**Returns:**
```javascript
{
  id: string,
  email: string,
  name: string,
  createdAt: Date,
  updatedAt: Date
}
```

**Throws:**
- "User ID is required" - If ID is missing
- "Invalid user ID format" - If ID is not a valid UUID
- "User not found in this tenant." - If user doesn't exist in tenant (404)
- "Tenant context required." - Missing tenant context
- "Authentication required." - Missing/invalid token

#### `validateEmail(email)`
Validate an email address format.

```javascript
const result = UserService.validateEmail("jane@example.com");
console.log(result); // { valid: true, error: null }

const invalid = UserService.validateEmail("invalid-email");
console.log(invalid); // { valid: false, error: "Invalid email format" }
```

**Returns:** `{ valid: boolean, error: string|null }`

**Validation Rules:**
- Required (non-empty)
- Valid email format (`user@domain.com`)
- Maximum 255 characters

#### `validateName(name)`
Validate a user name.

```javascript
const result = UserService.validateName("Jane Smith");
console.log(result); // { valid: true, error: null }
```

**Returns:** `{ valid: boolean, error: string|null }`

**Validation Rules:**
- Required (non-empty)
- Length: 2-100 characters

#### `isValidUUID(uuid)`
Check if a string is a valid UUID.

```javascript
const isValid = UserService.isValidUUID("950e8400-e29b-41d4-a716-446655440000");
console.log(isValid); // true
```

**Returns:** `boolean`

## Important: Tenant Context

All user operations require tenant context via the `X-Tenant-ID` header. This header can be provided in two ways:

### Option 1: Explicit Tenant ID
Pass tenant ID to each method call:

```javascript
const tenantId = "850e8400-...";
const user = await UserService.createUser(userData, tenantId);
```

### Option 2: Automatic from Current User (Recommended)
Enhance ApiClient to automatically add X-Tenant-ID from the current user's tenant:

```javascript
// In ApiClient.js
import { authManager } from './Services';

// Add tenant header automatically
const tenant = authManager.getTenant();
if (tenant && tenant.id) {
  requestHeaders["X-Tenant-ID"] = tenant.id;
}
```

Then use without explicit tenant ID:

```javascript
// Tenant ID automatically added from current user
const user = await UserService.createUser(userData);
```

## Data Flow

### Create User Flow
```
User provides email and name
    ↓
UserService.createUser()
    ↓
Validate email and name (client-side)
    ↓
ApiClient.post() with JWT token + X-Tenant-ID
    ↓
Token automatically refreshed if needed
    ↓
POST /api/v1/users with tenant context
    ↓
Backend validates email uniqueness within tenant
    ↓
Backend creates user in database (scoped to tenant)
    ↓
Backend returns user data
    ↓
UserService transforms response
    ↓
Component receives user data
```

### Get User Flow
```
Component needs user data
    ↓
UserService.getUserById()
    ↓
Validate UUID format
    ↓
ApiClient.get() with JWT token + X-Tenant-ID
    ↓
Token automatically refreshed if needed
    ↓
GET /api/v1/users/{id} with tenant context
    ↓
Backend retrieves user from database (tenant-scoped)
    ↓
Backend returns user data
    ↓
UserService transforms response
    ↓
Component receives user data
```

## Error Handling

### Error Types

| Error Condition | Response | Frontend Behavior |
|----------------|----------|-------------------|
| Missing authentication | 401 Unauthorized | "Authentication required." |
| Missing tenant context | 400 Bad Request | "Tenant context required." |
| Invalid user data | 400 Bad Request | Specific validation error |
| Email already exists | 409 Conflict | "User email already exists in this tenant." |
| User not found | 404 Not Found | "User not found in this tenant." |
| Server error | 500 Internal Server Error | Generic error message |

## Usage Examples

### Create a New User

```javascript
import React, { useState } from 'react';
import UserService from '../../services/API/UserService';
import MeService from '../../services/API/MeService';

function CreateUserForm() {
  const [email, setEmail] = useState('');
  const [name, setName] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError('');
    setLoading(true);

    // Validate before sending
    const emailValidation = UserService.validateEmail(email);
    if (!emailValidation.valid) {
      setError(emailValidation.error);
      setLoading(false);
      return;
    }

    const nameValidation = UserService.validateName(name);
    if (!nameValidation.valid) {
      setError(nameValidation.error);
      setLoading(false);
      return;
    }

    try {
      // Get current user's tenant ID
      const profile = await MeService.getMe();
      const tenantId = profile.tenantId;

      // Create user in current tenant
      const user = await UserService.createUser(
        { email, name },
        tenantId
      );

      console.log("User created:", user);
      // Reset form or redirect
      setEmail('');
      setName('');
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Email:</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="user@example.com"
          maxLength={255}
        />
      </div>

      <div>
        <label>Name:</label>
        <input
          type="text"
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="John Doe"
          maxLength={100}
        />
      </div>

      {error && <p className="error">{error}</p>}

      <button type="submit" disabled={loading}>
        {loading ? 'Creating...' : 'Create User'}
      </button>
    </form>
  );
}

export default CreateUserForm;
```

### Display User Profile

```javascript
import React, { useEffect, useState } from 'react';
import UserService from '../../services/API/UserService';
import MeService from '../../services/API/MeService';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  useEffect(() => {
    const fetchUser = async () => {
      try {
        // Get tenant context
        const profile = await MeService.getMe();

        // Get user data
        const data = await UserService.getUserById(userId, profile.tenantId);
        setUser(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();
  }, [userId]);

  if (loading) return <div>Loading user...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!user) return null;

  return (
    <div className="user-profile">
      <h2>{user.name}</h2>
      <p>Email: {user.email}</p>
      <p>ID: {user.id}</p>
      <p>Created: {user.createdAt.toLocaleDateString()}</p>
      <p>Updated: {user.updatedAt.toLocaleDateString()}</p>
    </div>
  );
}

export default UserProfile;
```

### List Users in Tenant (Helper)

```javascript
// Note: There's no list endpoint in the API yet
// This is a pattern for when it's added

async function listUsersInTenant(tenantId) {
  // This would call GET /api/v1/users with tenant context
  // For now, you can only get users by ID
  console.log("List endpoint not yet available");
}
```

## Testing

### Test Create User

```javascript
// In browser console after login

import UserService from './services/API/UserService';
import MeService from './services/API/MeService';

// Get tenant context
const profile = await MeService.getMe();
const tenantId = profile.tenantId;

// Validate email
const validation = UserService.validateEmail("test@example.com");
console.log("Email valid:", validation.valid);

// Create user
const user = await UserService.createUser({
  email: "test@example.com",
  name: "Test User"
}, tenantId);

console.log("Created user:", user);
```

### Test Get User

```javascript
// Using user ID from creation
const user = await UserService.getUserById(user.id, tenantId);
console.log("Retrieved user:", user);
```

### Test with curl

```bash
# 1. Login and get access token
ACCESS_TOKEN=$(curl -X POST http://localhost:8000/api/v1/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "SecurePass123!"
  }' | jq -r '.access_token')

# Get tenant ID from login response
TENANT_ID=$(curl -X GET http://localhost:8000/api/v1/me \
  -H "Authorization: JWT $ACCESS_TOKEN" | jq -r '.tenant_id')

# 2. Create user
curl -X POST http://localhost:8000/api/v1/users \
  -H "Content-Type: application/json" \
  -H "Authorization: JWT $ACCESS_TOKEN" \
  -H "X-Tenant-ID: $TENANT_ID" \
  -d '{
    "email": "newuser@example.com",
    "name": "New User"
  }' | jq

# 3. Get user by ID (use ID from creation response)
USER_ID="950e8400-..."
curl -X GET "http://localhost:8000/api/v1/users/$USER_ID" \
  -H "Authorization: JWT $ACCESS_TOKEN" \
  -H "X-Tenant-ID: $TENANT_ID" | jq
```

## Multi-Tenant Isolation

Users are scoped to tenants for data isolation:

```javascript
// Users in tenant A cannot see users in tenant B
const tenantA = "850e8400-...";
const tenantB = "950e8400-...";

// Create user in tenant A
const userA = await UserService.createUser(userData, tenantA);

// Try to get user A from tenant B context (will fail - not found)
try {
  const user = await UserService.getUserById(userA.id, tenantB);
} catch (error) {
  console.log("Cannot access user from different tenant");
}
```

## Related Files

### Created Files
```
src/services/API/UserService.js
docs/USER_API.md
```

### Backend Reference Files
```
cloud/maplepress-backend/docs/API.md (lines 560-660)
```

## Related Documentation

- [TENANT_API.md](./TENANT_API.md) - Tenant management (parent context)
- [ME_API.md](./ME_API.md) - Current user profile includes tenant ID
- [REGISTRATION_API.md](./REGISTRATION_API.md) - Initial user creation
- [FRONTEND_ARCHITECTURE.md](./FRONTEND_ARCHITECTURE.md) - Architecture overview
- [README.md](./README.md) - Documentation index

## Summary

The User API implementation provides:

1. **User Creation**: Create users within tenant context
2. **User Retrieval**: Get user by ID with tenant isolation
3. **Validation Helpers**: Client-side validation before API calls
4. **Tenant Context**: Multi-tenant data isolation
5. **Error Handling**: Clear error messages and graceful failures

Essential for managing team members within organizations (tenants) in a multi-tenant SaaS application.

---

**Last Updated**: October 30, 2024
**Frontend Version**: 0.0.0
**Documentation Version**: 1.0.0
