Migrate 6 services from duplicate Axios instances to centralized apiClient (auto-refresh, multi-tab sync, proper 401 handling): - trading.service.ts, notification.service.ts, payment.service.ts - education.service.ts, chat.service.ts, investment.service.ts Add global ErrorBoundary wrapping all App routes. Move assistant module ErrorBoundary to shared components/ErrorBoundary.tsx. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
162 lines
4.1 KiB
TypeScript
162 lines
4.1 KiB
TypeScript
/**
|
|
* Notification Service
|
|
* API client for notifications management
|
|
*/
|
|
|
|
import { apiClient as api } from '../lib/apiClient';
|
|
|
|
// ============================================================================
|
|
// Types
|
|
// ============================================================================
|
|
|
|
export type NotificationType =
|
|
| 'alert_triggered'
|
|
| 'trade_executed'
|
|
| 'deposit_confirmed'
|
|
| 'withdrawal_completed'
|
|
| 'distribution_received'
|
|
| 'system_announcement'
|
|
| 'security_alert'
|
|
| 'account_update';
|
|
|
|
export type NotificationPriority = 'low' | 'normal' | 'high' | 'urgent';
|
|
export type NotificationIconType = 'success' | 'warning' | 'error' | 'info';
|
|
|
|
export interface Notification {
|
|
id: string;
|
|
userId: string;
|
|
type: NotificationType;
|
|
title: string;
|
|
message: string;
|
|
priority: NotificationPriority;
|
|
data?: Record<string, unknown>;
|
|
actionUrl?: string;
|
|
iconType: NotificationIconType;
|
|
channels: string[];
|
|
isRead: boolean;
|
|
readAt?: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface NotificationPreferences {
|
|
userId: string;
|
|
emailEnabled: boolean;
|
|
pushEnabled: boolean;
|
|
inAppEnabled: boolean;
|
|
smsEnabled: boolean;
|
|
quietHoursStart?: string;
|
|
quietHoursEnd?: string;
|
|
disabledTypes: NotificationType[];
|
|
}
|
|
|
|
export interface GetNotificationsParams {
|
|
limit?: number;
|
|
offset?: number;
|
|
unreadOnly?: boolean;
|
|
}
|
|
|
|
interface ApiResponse<T> {
|
|
success: boolean;
|
|
data: T;
|
|
error?: string;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Notification API
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get user notifications
|
|
*/
|
|
export async function getNotifications(
|
|
params: GetNotificationsParams = {}
|
|
): Promise<Notification[]> {
|
|
const response = await api.get<ApiResponse<Notification[]>>('/notifications', { params });
|
|
return response.data.data;
|
|
}
|
|
|
|
/**
|
|
* Get unread notification count
|
|
*/
|
|
export async function getUnreadCount(): Promise<number> {
|
|
const response = await api.get<ApiResponse<{ count: number }>>('/notifications/unread-count');
|
|
return response.data.data.count;
|
|
}
|
|
|
|
/**
|
|
* Mark notification as read
|
|
*/
|
|
export async function markAsRead(notificationId: string): Promise<void> {
|
|
await api.patch(`/notifications/${notificationId}/read`);
|
|
}
|
|
|
|
/**
|
|
* Mark all notifications as read
|
|
*/
|
|
export async function markAllAsRead(): Promise<number> {
|
|
const response = await api.post<ApiResponse<{ markedCount: number }>>('/notifications/read-all');
|
|
return response.data.data.markedCount;
|
|
}
|
|
|
|
/**
|
|
* Delete notification
|
|
*/
|
|
export async function deleteNotification(notificationId: string): Promise<void> {
|
|
await api.delete(`/notifications/${notificationId}`);
|
|
}
|
|
|
|
/**
|
|
* Get notification preferences
|
|
*/
|
|
export async function getPreferences(): Promise<NotificationPreferences> {
|
|
const response = await api.get<ApiResponse<NotificationPreferences>>('/notifications/preferences');
|
|
return response.data.data;
|
|
}
|
|
|
|
/**
|
|
* Update notification preferences
|
|
*/
|
|
export async function updatePreferences(
|
|
preferences: Partial<Omit<NotificationPreferences, 'userId'>>
|
|
): Promise<NotificationPreferences> {
|
|
const response = await api.patch<ApiResponse<NotificationPreferences>>(
|
|
'/notifications/preferences',
|
|
preferences
|
|
);
|
|
return response.data.data;
|
|
}
|
|
|
|
/**
|
|
* Register push notification token
|
|
*/
|
|
export async function registerPushToken(
|
|
token: string,
|
|
platform: 'web' | 'ios' | 'android',
|
|
deviceInfo?: Record<string, unknown>
|
|
): Promise<void> {
|
|
await api.post('/notifications/push-token', { token, platform, deviceInfo });
|
|
}
|
|
|
|
/**
|
|
* Remove push notification token
|
|
*/
|
|
export async function removePushToken(token: string): Promise<void> {
|
|
await api.delete('/notifications/push-token', { data: { token } });
|
|
}
|
|
|
|
// ============================================================================
|
|
// Export as object for convenience
|
|
// ============================================================================
|
|
|
|
export const notificationApi = {
|
|
getNotifications,
|
|
getUnreadCount,
|
|
markAsRead,
|
|
markAllAsRead,
|
|
deleteNotification,
|
|
getPreferences,
|
|
updatePreferences,
|
|
registerPushToken,
|
|
removePushToken,
|
|
};
|