666 lines
23 KiB
TypeScript
666 lines
23 KiB
TypeScript
// API Configuration
|
||
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001'
|
||
|
||
// API Endpoints
|
||
export const API_ENDPOINTS = {
|
||
// Auth
|
||
LOGIN: '/api/login',
|
||
REFRESH: '/api/refresh',
|
||
LOGOUT: '/api/logout',
|
||
ME: '/api/me',
|
||
|
||
// User Status & Settings
|
||
USER_STATUS: '/api/user/status',
|
||
USER_STATUS_PROGRESS: '/api/user/status-progress',
|
||
USER_SETTINGS: '/api/user/settings',
|
||
|
||
// Email Verification
|
||
SEND_VERIFICATION_EMAIL: '/api/send-verification-email',
|
||
VERIFY_EMAIL_CODE: '/api/verify-email-code',
|
||
|
||
// Document Upload
|
||
UPLOAD_PERSONAL_ID: '/api/upload/personal-id',
|
||
UPLOAD_COMPANY_ID: '/api/upload/company-id',
|
||
|
||
// Profile Completion
|
||
COMPLETE_PERSONAL_PROFILE: '/api/profile/personal/complete',
|
||
COMPLETE_COMPANY_PROFILE: '/api/profile/company/complete',
|
||
|
||
// Contract Signing
|
||
UPLOAD_PERSONAL_CONTRACT: '/api/upload/contract/personal',
|
||
UPLOAD_COMPANY_CONTRACT: '/api/upload/contract/company',
|
||
|
||
// Documents
|
||
USER_DOCUMENTS: '/api/users/:id/documents',
|
||
|
||
// Admin
|
||
ADMIN_USERS: '/api/admin/users/:id/full',
|
||
ADMIN_USER_DETAILED: '/api/admin/users/:id/detailed',
|
||
ADMIN_USER_STATS: '/api/admin/user-stats',
|
||
ADMIN_USER_LIST: '/api/admin/user-list',
|
||
ADMIN_VERIFICATION_PENDING: '/api/admin/verification-pending-users',
|
||
ADMIN_UNVERIFIED_USERS: '/api/admin/unverified-users',
|
||
ADMIN_VERIFY_USER: '/api/admin/verify-user/:id',
|
||
ADMIN_ARCHIVE_USER: '/api/admin/archive-user/:id',
|
||
ADMIN_UNARCHIVE_USER: '/api/admin/unarchive-user/:id',
|
||
ADMIN_UPDATE_USER_VERIFICATION: '/api/admin/update-verification/:id',
|
||
ADMIN_UPDATE_USER_PROFILE: '/api/admin/update-user-profile/:id',
|
||
ADMIN_UPDATE_USER_STATUS: '/api/admin/update-user-status/:id',
|
||
// Pools (admin)
|
||
ADMIN_POOL_MEMBERS: '/api/admin/pools/:id/members',
|
||
// Coffee products (admin)
|
||
ADMIN_COFFEE_LIST: '/api/admin/coffee',
|
||
ADMIN_COFFEE_CREATE: '/api/admin/coffee',
|
||
ADMIN_COFFEE_UPDATE: '/api/admin/coffee/:id',
|
||
ADMIN_COFFEE_SET_STATE: '/api/admin/coffee/:id/state',
|
||
ADMIN_COFFEE_DELETE: '/api/admin/coffee/:id',
|
||
// Contract preview (admin) – matches backend route
|
||
ADMIN_CONTRACT_PREVIEW: '/api/admin/contracts/:id/preview',
|
||
ADMIN_CONTRACT_DOCS: '/api/admin/contracts/:id/documents',
|
||
ADMIN_CONTRACT_MOVE: '/api/admin/contracts/:id/move',
|
||
ADMIN_CONTRACT_FILES: '/api/admin/contracts/:id/files',
|
||
}
|
||
|
||
// API Helper Functions
|
||
export class ApiClient {
|
||
static async makeRequest(
|
||
endpoint: string,
|
||
options: RequestInit = {},
|
||
token?: string
|
||
): Promise<Response> {
|
||
const url = `${API_BASE_URL}${endpoint}`
|
||
|
||
const defaultHeaders: HeadersInit = {
|
||
'Content-Type': 'application/json',
|
||
}
|
||
|
||
if (token) {
|
||
defaultHeaders['Authorization'] = `Bearer ${token}`
|
||
}
|
||
|
||
const config: RequestInit = {
|
||
...options,
|
||
headers: {
|
||
...defaultHeaders,
|
||
...options.headers,
|
||
},
|
||
}
|
||
|
||
return fetch(url, config)
|
||
}
|
||
|
||
static async get(endpoint: string, token?: string): Promise<Response> {
|
||
return this.makeRequest(endpoint, { method: 'GET' }, token)
|
||
}
|
||
|
||
static async post(
|
||
endpoint: string,
|
||
data?: any,
|
||
token?: string
|
||
): Promise<Response> {
|
||
return this.makeRequest(
|
||
endpoint,
|
||
{
|
||
method: 'POST',
|
||
body: data ? JSON.stringify(data) : undefined,
|
||
},
|
||
token
|
||
)
|
||
}
|
||
|
||
static async postFormData(
|
||
endpoint: string,
|
||
formData: FormData,
|
||
token?: string
|
||
): Promise<Response> {
|
||
const url = `${API_BASE_URL}${endpoint}`
|
||
|
||
const headers: HeadersInit = {}
|
||
if (token) {
|
||
headers['Authorization'] = `Bearer ${token}`
|
||
}
|
||
|
||
return fetch(url, {
|
||
method: 'POST',
|
||
headers,
|
||
body: formData,
|
||
})
|
||
}
|
||
|
||
static async put(
|
||
endpoint: string,
|
||
data?: any,
|
||
token?: string
|
||
): Promise<Response> {
|
||
return this.makeRequest(
|
||
endpoint,
|
||
{
|
||
method: 'PUT',
|
||
body: data ? JSON.stringify(data) : undefined,
|
||
},
|
||
token
|
||
)
|
||
}
|
||
|
||
static async patch(
|
||
endpoint: string,
|
||
data?: any,
|
||
token?: string
|
||
): Promise<Response> {
|
||
return this.makeRequest(
|
||
endpoint,
|
||
{
|
||
method: 'PATCH',
|
||
body: data ? JSON.stringify(data) : undefined,
|
||
},
|
||
token
|
||
)
|
||
}
|
||
|
||
static async delete(endpoint: string, token?: string, data?: any): Promise<Response> {
|
||
return this.makeRequest(
|
||
endpoint,
|
||
{
|
||
method: 'DELETE',
|
||
body: data ? JSON.stringify(data) : undefined,
|
||
},
|
||
token
|
||
)
|
||
}
|
||
}
|
||
|
||
// Specific API Functions for QuickActions
|
||
export class QuickActionsAPI {
|
||
static async getUserStatus(token: string) {
|
||
const response = await ApiClient.get(API_ENDPOINTS.USER_STATUS, token)
|
||
if (!response.ok) {
|
||
throw new Error('Failed to fetch user status')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async sendVerificationEmail(token: string) {
|
||
const response = await ApiClient.post(API_ENDPOINTS.SEND_VERIFICATION_EMAIL, {}, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to send verification email')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async verifyEmailCode(token: string, code: string) {
|
||
const response = await ApiClient.post(
|
||
API_ENDPOINTS.VERIFY_EMAIL_CODE,
|
||
{ code },
|
||
token
|
||
)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Invalid verification code')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async uploadDocuments(token: string, userType: string, files: { front: File, back?: File }) {
|
||
const formData = new FormData()
|
||
formData.append('front', files.front)
|
||
if (files.back) {
|
||
formData.append('back', files.back)
|
||
}
|
||
|
||
const endpoint = userType === 'company'
|
||
? API_ENDPOINTS.UPLOAD_COMPANY_ID
|
||
: API_ENDPOINTS.UPLOAD_PERSONAL_ID
|
||
|
||
const response = await ApiClient.postFormData(endpoint, formData, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to upload documents')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async completeProfile(token: string, userType: string, profileData: any) {
|
||
const endpoint = userType === 'company'
|
||
? API_ENDPOINTS.COMPLETE_COMPANY_PROFILE
|
||
: API_ENDPOINTS.COMPLETE_PERSONAL_PROFILE
|
||
|
||
const response = await ApiClient.post(endpoint, profileData, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to complete profile')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async uploadContract(token: string, userType: string, contractFile: File) {
|
||
const formData = new FormData()
|
||
formData.append('contract', contractFile)
|
||
|
||
const endpoint = userType === 'company'
|
||
? API_ENDPOINTS.UPLOAD_COMPANY_CONTRACT
|
||
: API_ENDPOINTS.UPLOAD_PERSONAL_CONTRACT
|
||
|
||
const response = await ApiClient.postFormData(endpoint, formData, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to upload contract')
|
||
}
|
||
return response.json()
|
||
}
|
||
}
|
||
|
||
// Error Types
|
||
export interface ApiError {
|
||
message: string
|
||
status?: number
|
||
code?: string
|
||
}
|
||
|
||
// Admin API Functions
|
||
export class AdminAPI {
|
||
static async getUserStats(token: string) {
|
||
const response = await ApiClient.get(API_ENDPOINTS.ADMIN_USER_STATS, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to fetch user stats' }))
|
||
throw new Error(error.message || 'Failed to fetch user stats')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async getUserList(token: string) {
|
||
const response = await ApiClient.get(API_ENDPOINTS.ADMIN_USER_LIST, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to fetch user list' }))
|
||
throw new Error(error.message || 'Failed to fetch user list')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async getVerificationPendingUsers(token: string) {
|
||
const response = await ApiClient.get(API_ENDPOINTS.ADMIN_VERIFICATION_PENDING, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to fetch pending users' }))
|
||
throw new Error(error.message || 'Failed to fetch pending users')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async getUnverifiedUsers(token: string) {
|
||
const response = await ApiClient.get(API_ENDPOINTS.ADMIN_UNVERIFIED_USERS, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to fetch unverified users' }))
|
||
throw new Error(error.message || 'Failed to fetch unverified users')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async getDetailedUserInfo(token: string, userId: string) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_USER_DETAILED.replace(':id', userId)
|
||
const response = await ApiClient.get(endpoint, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to fetch detailed user info' }))
|
||
throw new Error(error.message || 'Failed to fetch detailed user info')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async verifyUser(token: string, userId: string, permissions: string[] = []) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_VERIFY_USER.replace(':id', userId)
|
||
const response = await ApiClient.post(endpoint, { permissions }, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to verify user')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async archiveUser(token: string, userId: string) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_ARCHIVE_USER.replace(':id', userId)
|
||
const response = await ApiClient.patch(endpoint, { status: 'inactive' }, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to archive user')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async unarchiveUser(token: string, userId: string) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_UNARCHIVE_USER.replace(':id', userId)
|
||
const response = await ApiClient.patch(endpoint, { status: 'active' }, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to unarchive user')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async updateUserVerification(token: string, userId: string, isVerified: number) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_UPDATE_USER_VERIFICATION.replace(':id', userId)
|
||
const response = await ApiClient.patch(endpoint, { is_admin_verified: isVerified }, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to update user verification')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async updateUserProfile(token: string, userId: string, profileData: any, userType: string) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_UPDATE_USER_PROFILE.replace(':id', userId)
|
||
const response = await ApiClient.patch(endpoint, { profileData, userType }, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to update user profile')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async getPoolMembers(token: string, poolId: string | number) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_POOL_MEMBERS.replace(':id', String(poolId))
|
||
const response = await ApiClient.get(endpoint, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to fetch pool members' }))
|
||
throw new Error(error.message || 'Failed to fetch pool members')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async addPoolMembers(token: string, poolId: string | number, userIds: Array<string | number>) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_POOL_MEMBERS.replace(':id', String(poolId))
|
||
const response = await ApiClient.post(endpoint, { userIds }, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to add pool members' }))
|
||
throw new Error(error.message || 'Failed to add pool members')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async removePoolMembers(token: string, poolId: string | number, userIds: Array<string | number>) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_POOL_MEMBERS.replace(':id', String(poolId))
|
||
const response = await ApiClient.delete(endpoint, token, { userIds })
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to remove pool members' }))
|
||
throw new Error(error.message || 'Failed to remove pool members')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async updateUserStatus(token: string, userId: string, status: string) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_UPDATE_USER_STATUS.replace(':id', userId)
|
||
const response = await ApiClient.patch(endpoint, { status }, token)
|
||
if (!response.ok) {
|
||
const error = await response.json()
|
||
throw new Error(error.message || 'Failed to update user status')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
// Coffee products (admin)
|
||
static async listCoffee(token: string) {
|
||
const response = await ApiClient.get(API_ENDPOINTS.ADMIN_COFFEE_LIST, token)
|
||
if (!response.ok) throw new Error('Failed to list products')
|
||
return response.json()
|
||
}
|
||
|
||
static async createCoffee(token: string, data: { title: string; description: string; quantity: number; price: number; currency?: string; tax_rate?: number; is_featured?: boolean; billing_interval?: 'day'|'week'|'month'|'year'; interval_count?: number; sku?: string; slug?: string; state?: boolean; pictureFile?: File }) {
|
||
if (data.pictureFile) {
|
||
const fd = new FormData()
|
||
fd.append('title', data.title)
|
||
fd.append('description', data.description)
|
||
fd.append('quantity', String(data.quantity))
|
||
fd.append('price', String(data.price))
|
||
if (data.currency) fd.append('currency', data.currency)
|
||
if (data.tax_rate !== undefined) fd.append('tax_rate', String(data.tax_rate))
|
||
if (data.is_featured !== undefined) fd.append('is_featured', String(!!data.is_featured))
|
||
if (data.billing_interval) fd.append('billing_interval', data.billing_interval)
|
||
if (data.interval_count !== undefined) fd.append('interval_count', String(data.interval_count))
|
||
if (data.sku) fd.append('sku', data.sku)
|
||
if (data.slug) fd.append('slug', data.slug)
|
||
if (data.state !== undefined) fd.append('state', String(!!data.state))
|
||
fd.append('picture', data.pictureFile)
|
||
const resp = await ApiClient.postFormData(API_ENDPOINTS.ADMIN_COFFEE_CREATE, fd, token)
|
||
if (!resp.ok) throw new Error('Failed to create product')
|
||
return resp.json()
|
||
} else {
|
||
const resp = await ApiClient.post(API_ENDPOINTS.ADMIN_COFFEE_CREATE, {
|
||
title: data.title,
|
||
description: data.description,
|
||
quantity: data.quantity,
|
||
price: data.price,
|
||
currency: data.currency,
|
||
tax_rate: data.tax_rate,
|
||
is_featured: data.is_featured,
|
||
billing_interval: data.billing_interval,
|
||
interval_count: data.interval_count,
|
||
sku: data.sku,
|
||
slug: data.slug,
|
||
state: data.state,
|
||
}, token)
|
||
if (!resp.ok) throw new Error('Failed to create product')
|
||
return resp.json()
|
||
}
|
||
}
|
||
|
||
static async updateCoffee(token: string, id: number, data: { title?: string; description?: string; quantity?: number; price?: number; currency?: string; tax_rate?: number; is_featured?: boolean; billing_interval?: 'day'|'week'|'month'|'year'; interval_count?: number; sku?: string; slug?: string; state?: boolean; pictureFile?: File }) {
|
||
if (data.pictureFile) {
|
||
const fd = new FormData()
|
||
if (data.title !== undefined) fd.append('title', data.title)
|
||
if (data.description !== undefined) fd.append('description', data.description)
|
||
if (data.quantity !== undefined) fd.append('quantity', String(data.quantity))
|
||
if (data.price !== undefined) fd.append('price', String(data.price))
|
||
if (data.currency !== undefined) fd.append('currency', data.currency)
|
||
if (data.tax_rate !== undefined) fd.append('tax_rate', String(data.tax_rate))
|
||
if (data.is_featured !== undefined) fd.append('is_featured', String(!!data.is_featured))
|
||
if (data.billing_interval !== undefined) fd.append('billing_interval', data.billing_interval)
|
||
if (data.interval_count !== undefined) fd.append('interval_count', String(data.interval_count))
|
||
if (data.sku !== undefined) fd.append('sku', data.sku)
|
||
if (data.slug !== undefined) fd.append('slug', data.slug)
|
||
if (data.state !== undefined) fd.append('state', String(!!data.state))
|
||
fd.append('picture', data.pictureFile)
|
||
const endpoint = API_ENDPOINTS.ADMIN_COFFEE_UPDATE.replace(':id', String(id))
|
||
const resp = await ApiClient.postFormData(endpoint, fd, token)
|
||
if (!resp.ok) throw new Error('Failed to update product')
|
||
return resp.json()
|
||
} else {
|
||
const endpoint = API_ENDPOINTS.ADMIN_COFFEE_UPDATE.replace(':id', String(id))
|
||
const resp = await ApiClient.put(endpoint, data, token)
|
||
if (!resp.ok) throw new Error('Failed to update product')
|
||
return resp.json()
|
||
}
|
||
}
|
||
|
||
static async setCoffeeState(token: string, id: number, state: boolean) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_COFFEE_SET_STATE.replace(':id', String(id))
|
||
const resp = await ApiClient.patch(endpoint, { state }, token)
|
||
if (!resp.ok) throw new Error('Failed to set state')
|
||
return resp.json()
|
||
}
|
||
|
||
static async deleteCoffee(token: string, id: number) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_COFFEE_DELETE.replace(':id', String(id))
|
||
const resp = await ApiClient.delete(endpoint, token)
|
||
if (!resp.ok) throw new Error('Failed to delete product')
|
||
return true
|
||
}
|
||
|
||
static async getContractPreviewHtml(token: string, userId: string, userType?: 'personal' | 'company', contractType?: 'contract' | 'gdpr', documentId?: number, objectKey?: string) {
|
||
let endpoint = API_ENDPOINTS.ADMIN_CONTRACT_PREVIEW.replace(':id', userId)
|
||
const qs = new URLSearchParams()
|
||
if (userType) qs.set('userType', userType)
|
||
if (contractType) qs.set('contract_type', contractType)
|
||
if (documentId) qs.set('documentId', String(documentId))
|
||
if (objectKey) qs.set('objectKey', objectKey)
|
||
const qsStr = qs.toString()
|
||
if (qsStr) endpoint += `?${qsStr}`
|
||
const response = await ApiClient.get(endpoint, token)
|
||
const warningHeader = response.headers.get('x-contract-preview-warning')
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to fetch contract preview' }))
|
||
const err: any = new Error(error.message || 'Failed to fetch contract preview')
|
||
if (warningHeader) err.warning = warningHeader
|
||
if (error?.warning) err.warning = error.warning
|
||
throw err
|
||
}
|
||
// Return HTML string
|
||
const html = await response.text()
|
||
return { html, warning: warningHeader }
|
||
}
|
||
|
||
static async listContractDocuments(token: string, userId: string, userType?: 'personal' | 'company') {
|
||
let endpoint = API_ENDPOINTS.ADMIN_CONTRACT_DOCS.replace(':id', userId)
|
||
const qs = new URLSearchParams()
|
||
if (userType) qs.set('userType', userType)
|
||
const qsStr = qs.toString()
|
||
if (qsStr) endpoint += `?${qsStr}`
|
||
const response = await ApiClient.get(endpoint, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to load contract documents' }))
|
||
throw new Error(error.message || 'Failed to load contract documents')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async listContractFiles(token: string, userId: string, userType?: 'personal' | 'company') {
|
||
let endpoint = API_ENDPOINTS.ADMIN_CONTRACT_FILES.replace(':id', userId)
|
||
const qs = new URLSearchParams()
|
||
if (userType) qs.set('userType', userType)
|
||
const qsStr = qs.toString()
|
||
if (qsStr) endpoint += `?${qsStr}`
|
||
const response = await ApiClient.get(endpoint, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to load contract files' }))
|
||
throw new Error(error.message || 'Failed to load contract files')
|
||
}
|
||
return response.json()
|
||
}
|
||
|
||
static async moveContractDocument(token: string, userId: string, documentId: number | undefined, targetType: 'contract' | 'gdpr', objectKey?: string) {
|
||
const endpoint = API_ENDPOINTS.ADMIN_CONTRACT_MOVE.replace(':id', userId)
|
||
const response = await ApiClient.post(endpoint, { documentId, targetType, objectKey }, token)
|
||
if (!response.ok) {
|
||
const error = await response.json().catch(() => ({ message: 'Failed to move contract document' }))
|
||
throw new Error(error.message || 'Failed to move contract document')
|
||
}
|
||
return response.json()
|
||
}
|
||
}
|
||
|
||
// Response Types
|
||
export interface UserStatus {
|
||
emailVerified: boolean
|
||
documentsUploaded: boolean
|
||
profileCompleted: boolean
|
||
contractSigned: boolean
|
||
}
|
||
|
||
export interface AdminUserStats {
|
||
totalUsers: number
|
||
adminUsers: number
|
||
verificationPending: number
|
||
activeUsers: number
|
||
personalUsers: number
|
||
companyUsers: number
|
||
}
|
||
|
||
export interface PendingUser {
|
||
id: number
|
||
email: string
|
||
user_type: 'personal' | 'company'
|
||
role: 'user' | 'admin'
|
||
created_at: string
|
||
last_login_at: string | null
|
||
status: string
|
||
is_admin_verified: number
|
||
email_verified?: number
|
||
profile_completed?: number
|
||
documents_uploaded?: number
|
||
contract_signed?: number
|
||
first_name?: string
|
||
last_name?: string
|
||
company_name?: string
|
||
}
|
||
|
||
export interface DetailedUserInfo {
|
||
user: {
|
||
id: number
|
||
email: string
|
||
user_type: 'personal' | 'company'
|
||
role: 'user' | 'admin' | 'super_admin'
|
||
created_at: string
|
||
last_login_at: string | null
|
||
}
|
||
personalProfile?: {
|
||
user_id: number
|
||
first_name: string
|
||
last_name: string
|
||
date_of_birth?: string
|
||
phone?: string
|
||
address?: string
|
||
city?: string
|
||
zip_code?: string
|
||
country?: string
|
||
}
|
||
companyProfile?: {
|
||
user_id: number
|
||
company_name: string
|
||
tax_id?: string
|
||
registration_number?: string
|
||
phone?: string
|
||
address?: string
|
||
city?: string
|
||
zip_code?: string
|
||
country?: string
|
||
}
|
||
userStatus?: {
|
||
user_id: number
|
||
status: string
|
||
email_verified: number
|
||
profile_completed: number
|
||
documents_uploaded: number
|
||
contract_signed: number
|
||
is_admin_verified: number
|
||
admin_verified_at?: string
|
||
}
|
||
permissions: Array<{
|
||
id: number
|
||
name: string
|
||
description: string
|
||
is_active: boolean
|
||
}>
|
||
documents: Array<{
|
||
id: number
|
||
user_id: number
|
||
document_type: string
|
||
file_name: string
|
||
file_size: number
|
||
uploaded_at: string
|
||
}>
|
||
contracts: Array<{
|
||
id: number
|
||
user_id: number
|
||
document_type: string
|
||
file_name: string
|
||
file_size: number
|
||
uploaded_at: string
|
||
}>
|
||
idDocuments: Array<{
|
||
id: number
|
||
user_id: number
|
||
document_type: string
|
||
front_object_storage_id?: string
|
||
back_object_storage_id?: string
|
||
frontUrl?: string
|
||
backUrl?: string
|
||
uploaded_at: string
|
||
}>
|
||
storageStatus?: {
|
||
idDocumentsPresent?: boolean
|
||
contractPresent?: boolean
|
||
}
|
||
}
|
||
|
||
export interface ApiResponse<T = any> {
|
||
success: boolean
|
||
message?: string
|
||
data?: T
|
||
} |