- Add GET /api/users/:id for profile retrieval with stats - Add PATCH /api/users/:id for profile updates - Add GET /api/users/:id/reports to fetch user's reports - Add GET /api/users/:id/events to fetch user's events (registered & organized) - Add PATCH /api/users/:id/preferences for notification settings - Add POST /api/users/:id/calculate-impact for impact score calculation - Create userService with all business logic - Create userController with request handling - Create userRoutes and register in main server - Update frontend userService with new endpoints - Add comprehensive documentation in USER_PROFILE_ENDPOINTS.md - Add test script for manual endpoint testing Requirements satisfied: 6.3, 6.4, 6.5, 10.4
402 lines
8.6 KiB
Markdown
402 lines
8.6 KiB
Markdown
# User Profile Endpoints Documentation
|
||
|
||
This document describes the user profile API endpoints implemented for the Beach Litter Mapping Platform.
|
||
|
||
## Overview
|
||
|
||
The user profile endpoints provide functionality for:
|
||
- Retrieving user profiles with statistics
|
||
- Updating user profile information
|
||
- Managing notification preferences
|
||
- Fetching user's reports and events
|
||
- Calculating impact scores
|
||
|
||
## Authentication
|
||
|
||
Most endpoints require authentication via Clerk session token:
|
||
```
|
||
Authorization: Bearer <clerk_session_token>
|
||
```
|
||
|
||
## Endpoints
|
||
|
||
### 1. Get User Profile
|
||
|
||
**Endpoint:** `GET /api/users/:id`
|
||
|
||
**Description:** Retrieve a user's profile including statistics about their contributions.
|
||
|
||
**Authentication:** Not required (public profiles)
|
||
|
||
**Parameters:**
|
||
- `id` (path) - User database ID
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"clerkId": "user_xxx",
|
||
"email": "user@example.com",
|
||
"username": "johndoe",
|
||
"firstName": "John",
|
||
"lastName": "Doe",
|
||
"avatarUrl": "https://...",
|
||
"impactScore": 150,
|
||
"notificationEmail": true,
|
||
"notificationInApp": true,
|
||
"areasOfInterest": [
|
||
{
|
||
"lat": 53.3498,
|
||
"lng": -6.2603,
|
||
"radius": 10
|
||
}
|
||
],
|
||
"createdAt": "2024-01-01T00:00:00.000Z",
|
||
"updatedAt": "2024-01-01T00:00:00.000Z",
|
||
"stats": {
|
||
"reportsSubmitted": 5,
|
||
"eventsAttended": 3,
|
||
"eventsOrganized": 1
|
||
}
|
||
}
|
||
```
|
||
|
||
**Error Responses:**
|
||
- `404` - User not found
|
||
- `500` - Internal server error
|
||
|
||
---
|
||
|
||
### 2. Update User Profile
|
||
|
||
**Endpoint:** `PATCH /api/users/:id`
|
||
|
||
**Description:** Update user profile information (first name, last name, username).
|
||
|
||
**Authentication:** Required (users can only update their own profile)
|
||
|
||
**Parameters:**
|
||
- `id` (path) - User database ID
|
||
|
||
**Request Body:**
|
||
```json
|
||
{
|
||
"firstName": "John",
|
||
"lastName": "Doe",
|
||
"username": "johndoe123"
|
||
}
|
||
```
|
||
|
||
All fields are optional. Only provided fields will be updated.
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"clerkId": "user_xxx",
|
||
"email": "user@example.com",
|
||
"username": "johndoe123",
|
||
"firstName": "John",
|
||
"lastName": "Doe",
|
||
"avatarUrl": "https://...",
|
||
"impactScore": 150,
|
||
"createdAt": "2024-01-01T00:00:00.000Z",
|
||
"updatedAt": "2024-01-01T00:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
**Error Responses:**
|
||
- `400` - Username already taken
|
||
- `401` - Not authenticated
|
||
- `403` - Cannot update another user's profile
|
||
- `404` - User not found
|
||
- `500` - Internal server error
|
||
|
||
---
|
||
|
||
### 3. Get User Reports
|
||
|
||
**Endpoint:** `GET /api/users/:id/reports`
|
||
|
||
**Description:** Retrieve all reports submitted by a user.
|
||
|
||
**Authentication:** Not required
|
||
|
||
**Parameters:**
|
||
- `id` (path) - User database ID
|
||
|
||
**Response:**
|
||
```json
|
||
[
|
||
{
|
||
"id": "uuid",
|
||
"userId": "uuid",
|
||
"latitude": 53.3498,
|
||
"longitude": -6.2603,
|
||
"locationSource": "gps",
|
||
"photoUrls": ["/uploads/photo1.jpg"],
|
||
"photoTimestamp": "2024-01-01T12:00:00.000Z",
|
||
"litterType": "plastic",
|
||
"quantity": "moderate",
|
||
"description": "Plastic bottles on beach",
|
||
"verificationStatus": "verified",
|
||
"createdAt": "2024-01-01T12:00:00.000Z",
|
||
"environmentalConcerns": [],
|
||
"verifications": []
|
||
}
|
||
]
|
||
```
|
||
|
||
**Error Responses:**
|
||
- `500` - Internal server error
|
||
|
||
---
|
||
|
||
### 4. Get User Events
|
||
|
||
**Endpoint:** `GET /api/users/:id/events`
|
||
|
||
**Description:** Retrieve events the user has registered for and organized.
|
||
|
||
**Authentication:** Not required
|
||
|
||
**Parameters:**
|
||
- `id` (path) - User database ID
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"registered": [
|
||
{
|
||
"id": "uuid",
|
||
"title": "Beach Cleanup",
|
||
"description": "Community cleanup event",
|
||
"latitude": 53.3498,
|
||
"longitude": -6.2603,
|
||
"locationName": "Sandymount Beach",
|
||
"scheduledDate": "2024-02-01T10:00:00.000Z",
|
||
"duration": 120,
|
||
"status": "upcoming",
|
||
"participantCount": 15,
|
||
"organizer": {
|
||
"id": "uuid",
|
||
"username": "organizer",
|
||
"avatarUrl": "https://..."
|
||
},
|
||
"registeredAt": "2024-01-15T10:00:00.000Z",
|
||
"attended": false
|
||
}
|
||
],
|
||
"organized": [
|
||
{
|
||
"id": "uuid",
|
||
"title": "My Beach Cleanup",
|
||
"description": "Event I organized",
|
||
"latitude": 53.3498,
|
||
"longitude": -6.2603,
|
||
"locationName": "Dollymount Beach",
|
||
"scheduledDate": "2024-03-01T10:00:00.000Z",
|
||
"duration": 180,
|
||
"status": "upcoming",
|
||
"participantCount": 20,
|
||
"organizer": {
|
||
"id": "uuid",
|
||
"username": "johndoe",
|
||
"avatarUrl": "https://..."
|
||
},
|
||
"registrations": []
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Error Responses:**
|
||
- `500` - Internal server error
|
||
|
||
---
|
||
|
||
### 5. Update Notification Preferences
|
||
|
||
**Endpoint:** `PATCH /api/users/:id/preferences`
|
||
|
||
**Description:** Update user's notification preferences and areas of interest.
|
||
|
||
**Authentication:** Required (users can only update their own preferences)
|
||
|
||
**Parameters:**
|
||
- `id` (path) - User database ID
|
||
|
||
**Request Body:**
|
||
```json
|
||
{
|
||
"notificationEmail": true,
|
||
"notificationInApp": true,
|
||
"areasOfInterest": [
|
||
{
|
||
"lat": 53.3498,
|
||
"lng": -6.2603,
|
||
"radius": 10
|
||
},
|
||
{
|
||
"lat": 51.8985,
|
||
"lng": -8.4756,
|
||
"radius": 25
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Field Validation:**
|
||
- `radius` must be between 1 and 50 kilometers
|
||
- Each area must have `lat`, `lng`, and `radius`
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"id": "uuid",
|
||
"notificationEmail": true,
|
||
"notificationInApp": true,
|
||
"areasOfInterest": [
|
||
{
|
||
"lat": 53.3498,
|
||
"lng": -6.2603,
|
||
"radius": 10
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Error Responses:**
|
||
- `400` - Validation error (invalid radius, missing fields, etc.)
|
||
- `401` - Not authenticated
|
||
- `403` - Cannot update another user's preferences
|
||
- `404` - User not found
|
||
- `500` - Internal server error
|
||
|
||
---
|
||
|
||
### 6. Calculate Impact Score
|
||
|
||
**Endpoint:** `POST /api/users/:id/calculate-impact`
|
||
|
||
**Description:** Calculate and update the user's impact score based on their contributions.
|
||
|
||
**Authentication:** Not required
|
||
|
||
**Parameters:**
|
||
- `id` (path) - User database ID
|
||
|
||
**Impact Score Formula:**
|
||
```
|
||
Impact Score = (reports × 10) + (events attended × 20) + (events organized × 50)
|
||
```
|
||
|
||
**Response:**
|
||
```json
|
||
{
|
||
"impactScore": 150
|
||
}
|
||
```
|
||
|
||
**Error Responses:**
|
||
- `404` - User not found
|
||
- `500` - Internal server error
|
||
|
||
---
|
||
|
||
## Usage Examples
|
||
|
||
### Get User Profile
|
||
```bash
|
||
curl http://localhost:3005/api/users/abc-123
|
||
```
|
||
|
||
### Update Profile
|
||
```bash
|
||
curl -X PATCH http://localhost:3005/api/users/abc-123 \
|
||
-H "Authorization: Bearer <token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"firstName": "John",
|
||
"lastName": "Doe",
|
||
"username": "johndoe"
|
||
}'
|
||
```
|
||
|
||
### Update Notification Preferences
|
||
```bash
|
||
curl -X PATCH http://localhost:3005/api/users/abc-123/preferences \
|
||
-H "Authorization: Bearer <token>" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"notificationEmail": true,
|
||
"notificationInApp": true,
|
||
"areasOfInterest": [
|
||
{
|
||
"lat": 53.3498,
|
||
"lng": -6.2603,
|
||
"radius": 10
|
||
}
|
||
]
|
||
}'
|
||
```
|
||
|
||
### Calculate Impact Score
|
||
```bash
|
||
curl -X POST http://localhost:3005/api/users/abc-123/calculate-impact
|
||
```
|
||
|
||
## Frontend Integration
|
||
|
||
The frontend service (`src/services/userService.ts`) provides TypeScript methods for all endpoints:
|
||
|
||
```typescript
|
||
import { userService } from '@/services/userService';
|
||
|
||
// Get profile
|
||
const profile = await userService.getProfile(userId);
|
||
|
||
// Update profile
|
||
const updated = await userService.updateProfile(userId, {
|
||
firstName: 'John',
|
||
lastName: 'Doe',
|
||
username: 'johndoe'
|
||
});
|
||
|
||
// Get user's reports
|
||
const reports = await userService.getUserReports(userId);
|
||
|
||
// Get user's events
|
||
const events = await userService.getUserEvents(userId);
|
||
|
||
// Update notification preferences
|
||
const preferences = await userService.updateNotificationPreferences(userId, {
|
||
notificationEmail: true,
|
||
notificationInApp: true,
|
||
areasOfInterest: [
|
||
{ lat: 53.3498, lng: -6.2603, radius: 10 }
|
||
]
|
||
});
|
||
|
||
// Calculate impact score
|
||
const { impactScore } = await userService.calculateImpactScore(userId);
|
||
```
|
||
|
||
## Requirements Satisfied
|
||
|
||
This implementation satisfies the following requirements from the specification:
|
||
|
||
- **Requirement 6.3**: User profile display showing submitted reports and events attended
|
||
- **Requirement 6.4**: Impact score calculation based on contributions
|
||
- **Requirement 6.5**: User profile showing map of submitted report locations (data provided via reports endpoint)
|
||
- **Requirement 10.4**: Notification preferences with area of interest configuration (1-50km radius)
|
||
|
||
## Notes
|
||
|
||
- User authentication is handled by Clerk
|
||
- Users are automatically created in the database when they first interact with the API
|
||
- The `areasOfInterest` field is stored as JSON in the database
|
||
- Impact scores are calculated on-demand but can be cached for performance
|
||
- All endpoints follow RESTful conventions
|
||
- Error responses follow a consistent format with error codes and timestamps
|