feat: Introduce user profile service and enable role-based profile management for users and agents, including avatar uploads and a dismissible error message in the hero section.
This commit is contained in:
@@ -148,7 +148,8 @@ class UploadService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload avatar image (uses agent profile ID as filename for auto-replacement)
|
||||
* Upload avatar image for AGENTS (uses agent profile ID as filename for auto-replacement)
|
||||
* S3 path: {env}/agents/{agentId}/avatar.{ext}
|
||||
* Returns the S3 key (not full URL) for storage in database
|
||||
*/
|
||||
async uploadAvatar(
|
||||
@@ -179,6 +180,39 @@ class UploadService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload avatar image for regular USERS (uses user ID as folder)
|
||||
* S3 path: {env}/users/{userId}/avatar.{ext}
|
||||
* Returns the S3 key (not full URL) for storage in database
|
||||
*/
|
||||
async uploadUserAvatar(
|
||||
file: File,
|
||||
onProgress?: (progress: UploadProgress) => void
|
||||
): Promise<UploadedFile> {
|
||||
// Step 1: Get presigned URL from backend (uses user ID as folder)
|
||||
const response = await api.post<{ data: PresignedUrlResponse }>(
|
||||
`${this.basePath}/user-avatar-presigned-url`,
|
||||
{
|
||||
filename: file.name,
|
||||
contentType: file.type,
|
||||
}
|
||||
);
|
||||
const { uploadUrl, key } = response.data.data;
|
||||
|
||||
// Step 2: Upload directly to S3
|
||||
await this.uploadToS3(uploadUrl, file, onProgress);
|
||||
|
||||
// Step 3: Return uploaded file info with KEY (not full URL) for database storage
|
||||
return {
|
||||
id: key,
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
url: key, // Store the S3 key, not the full URL
|
||||
uploadedAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a presigned download URL for an S3 key
|
||||
*/
|
||||
|
||||
50
src/services/users.service.ts
Normal file
50
src/services/users.service.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import api from './api';
|
||||
|
||||
export interface UserProfile {
|
||||
id: string;
|
||||
userId: string;
|
||||
email: string;
|
||||
firstName: string | null;
|
||||
lastName: string | null;
|
||||
phone: string | null;
|
||||
avatar: string | null;
|
||||
city: string | null;
|
||||
state: string | null;
|
||||
country: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface UpdateUserProfileData {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
phone?: string;
|
||||
avatar?: string | null;
|
||||
city?: string;
|
||||
state?: string;
|
||||
country?: string;
|
||||
}
|
||||
|
||||
interface ApiResponse<T> {
|
||||
success: boolean;
|
||||
data: T;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export const usersService = {
|
||||
/**
|
||||
* Get current user's profile
|
||||
*/
|
||||
async getMyProfile(): Promise<UserProfile> {
|
||||
const response = await api.get<ApiResponse<UserProfile>>('/users/profile/me');
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update current user's profile
|
||||
*/
|
||||
async updateProfile(data: UpdateUserProfileData): Promise<UserProfile> {
|
||||
const response = await api.patch<ApiResponse<UserProfile>>('/users/profile/me', data);
|
||||
return response.data.data;
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user