fix
This commit is contained in:
@@ -43,6 +43,31 @@ interface ApiResponse<T> {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface FieldValueInput {
|
||||
fieldSlug: string;
|
||||
value: string | number | boolean | string[] | Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
export interface FieldValueResponse {
|
||||
fieldSlug: string;
|
||||
fieldName: string;
|
||||
fieldType: string;
|
||||
sectionSlug: string;
|
||||
sectionName: string;
|
||||
value: unknown;
|
||||
}
|
||||
|
||||
export interface GetFieldValuesResponse {
|
||||
agentProfileId: string;
|
||||
fieldValues: FieldValueResponse[];
|
||||
}
|
||||
|
||||
export interface SaveFieldValuesResponse {
|
||||
message: string;
|
||||
savedCount: number;
|
||||
data: unknown[];
|
||||
}
|
||||
|
||||
// Agents Service for Web
|
||||
class AgentsService {
|
||||
private basePath = '/agents';
|
||||
@@ -61,6 +86,21 @@ class AgentsService {
|
||||
const response = await api.put<ApiResponse<AgentProfile>>(`${this.basePath}/profile`, data);
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
async saveFieldValues(fieldValues: FieldValueInput[]): Promise<SaveFieldValuesResponse> {
|
||||
const response = await api.put<ApiResponse<SaveFieldValuesResponse>>(
|
||||
`${this.basePath}/profile/field-values`,
|
||||
{ fieldValues }
|
||||
);
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
async getFieldValues(): Promise<GetFieldValuesResponse> {
|
||||
const response = await api.get<ApiResponse<GetFieldValuesResponse>>(
|
||||
`${this.basePath}/profile/field-values`
|
||||
);
|
||||
return response.data.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const agentsService = new AgentsService();
|
||||
|
||||
@@ -31,3 +31,7 @@ export type {
|
||||
// Agents Service
|
||||
export { agentsService } from './agents.service';
|
||||
export type { AgentProfile, AgentType } from './agents.service';
|
||||
|
||||
// Upload Service
|
||||
export { uploadService } from './upload.service';
|
||||
export type { UploadedFile, UploadProgress } from './upload.service';
|
||||
|
||||
@@ -12,7 +12,9 @@ export type FieldType =
|
||||
| 'RANGE'
|
||||
| 'NUMBER'
|
||||
| 'DATE'
|
||||
| 'TAG_INPUT';
|
||||
| 'TAG_INPUT'
|
||||
| 'FILE'
|
||||
| 'FILE_UPLOAD';
|
||||
|
||||
// Types
|
||||
export interface FieldOption {
|
||||
@@ -33,6 +35,8 @@ export interface FieldValidation {
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
pattern?: string;
|
||||
allowedFormats?: string[];
|
||||
maxFileSize?: number;
|
||||
}
|
||||
|
||||
export interface FieldUiConfig {
|
||||
|
||||
151
src/services/upload.service.ts
Normal file
151
src/services/upload.service.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import api from './api';
|
||||
|
||||
export interface UploadedFile {
|
||||
id: string;
|
||||
name: string;
|
||||
size: number;
|
||||
type: string;
|
||||
url: string;
|
||||
uploadedAt: string;
|
||||
}
|
||||
|
||||
export interface UploadProgress {
|
||||
loaded: number;
|
||||
total: number;
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
interface PresignedUrlResponse {
|
||||
uploadUrl: string;
|
||||
publicUrl: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
type UploadFolder = 'avatars' | 'documents' | 'properties' | 'verification';
|
||||
|
||||
class UploadService {
|
||||
private basePath = '/upload';
|
||||
|
||||
/**
|
||||
* Get presigned URL from backend
|
||||
*/
|
||||
private async getPresignedUrl(
|
||||
filename: string,
|
||||
contentType: string,
|
||||
folder: UploadFolder = 'documents'
|
||||
): Promise<PresignedUrlResponse> {
|
||||
const response = await api.post<{ data: PresignedUrlResponse }>(
|
||||
`${this.basePath}/presigned-url`,
|
||||
{
|
||||
filename,
|
||||
contentType,
|
||||
folder,
|
||||
}
|
||||
);
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file directly to S3 using presigned URL
|
||||
*/
|
||||
private async uploadToS3(
|
||||
uploadUrl: string,
|
||||
file: File,
|
||||
onProgress?: (progress: UploadProgress) => void
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.upload.addEventListener('progress', (event) => {
|
||||
if (event.lengthComputable && onProgress) {
|
||||
const percentage = Math.round((event.loaded * 100) / event.total);
|
||||
onProgress({
|
||||
loaded: event.loaded,
|
||||
total: event.total,
|
||||
percentage,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
xhr.addEventListener('load', () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Upload failed with status ${xhr.status}`));
|
||||
}
|
||||
});
|
||||
|
||||
xhr.addEventListener('error', () => {
|
||||
reject(new Error('Upload failed'));
|
||||
});
|
||||
|
||||
xhr.open('PUT', uploadUrl);
|
||||
xhr.setRequestHeader('Content-Type', file.type);
|
||||
xhr.send(file);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a single file to S3
|
||||
*/
|
||||
async uploadFile(
|
||||
file: File,
|
||||
folder: UploadFolder = 'documents',
|
||||
onProgress?: (progress: UploadProgress) => void
|
||||
): Promise<UploadedFile> {
|
||||
// Step 1: Get presigned URL from backend
|
||||
const { uploadUrl, publicUrl, key } = await this.getPresignedUrl(
|
||||
file.name,
|
||||
file.type,
|
||||
folder
|
||||
);
|
||||
|
||||
// Step 2: Upload directly to S3
|
||||
await this.uploadToS3(uploadUrl, file, onProgress);
|
||||
|
||||
// Step 3: Return uploaded file info
|
||||
return {
|
||||
id: key, // Use the S3 key as the ID
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
url: publicUrl,
|
||||
uploadedAt: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload multiple files to S3
|
||||
*/
|
||||
async uploadFiles(
|
||||
files: File[],
|
||||
folder: UploadFolder = 'documents',
|
||||
onProgress?: (fileIndex: number, progress: UploadProgress) => void
|
||||
): Promise<UploadedFile[]> {
|
||||
const uploadedFiles: UploadedFile[] = [];
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
const uploadedFile = await this.uploadFile(
|
||||
file,
|
||||
folder,
|
||||
onProgress ? (progress) => onProgress(i, progress) : undefined
|
||||
);
|
||||
uploadedFiles.push(uploadedFile);
|
||||
}
|
||||
|
||||
return uploadedFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file from S3
|
||||
*/
|
||||
async deleteFile(fileKey: string): Promise<void> {
|
||||
// The key needs to be URL encoded for path parameter
|
||||
const encodedKey = encodeURIComponent(fileKey);
|
||||
await api.delete(`${this.basePath}/${encodedKey}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const uploadService = new UploadService();
|
||||
Reference in New Issue
Block a user