feat: add user notification center

This commit is contained in:
Codex
2026-06-30 03:56:41 +08:00
parent 2fea68fe4d
commit d72ff499ce
24 changed files with 895 additions and 44 deletions

View File

@@ -66,6 +66,23 @@ export interface PointExchangeItem {
lastExchangedAt?: string | null;
}
export interface UserNotificationItem {
id: string;
notificationType?: string;
status?: 'unread' | 'read' | 'dismissed' | 'archived';
severity?: 'info' | 'success' | 'warning' | 'error';
title?: string;
message?: string;
actionLabel?: string | null;
actionPath?: string | null;
sourceType?: string | null;
sourceId?: string | null;
metadata?: Record<string, unknown>;
readAt?: string | null;
createdAt?: string;
updatedAt?: string;
}
export async function loadProfile() {
return apiRequest<{ item?: StudentProfile }>('/api/profile/me');
}
@@ -127,6 +144,26 @@ export async function redeemExchangeItem(input: {
});
}
export async function loadNotifications(query: {
status?: UserNotificationItem['status'];
notificationType?: string;
limit?: number;
} = {}) {
return apiRequest<{ items?: UserNotificationItem[]; summary?: Record<string, number> }>('/api/profile/notifications', {
query: { ...query, limit: query.limit || 50 },
});
}
export async function updateNotificationStatus(input: {
notificationIds: string[];
status: 'read' | 'dismissed' | 'archived';
}) {
return apiRequest<{ item?: Record<string, unknown> }>('/api/profile/notifications/status', {
method: 'POST',
body: input,
});
}
export async function loadBadges() {
return apiRequest<{ items?: Record<string, unknown>[] }>('/api/profile/badges');
}

View File

@@ -236,6 +236,29 @@ export interface TenantContentNotificationItem {
resolvedAt?: string | null;
}
export interface UserNotificationAdminItem {
id: string;
userId?: string;
userName?: string | null;
userPhone?: string | null;
userAvatarUrl?: string | null;
notificationType?: string;
status?: 'unread' | 'read' | 'dismissed' | 'archived';
severity?: 'info' | 'success' | 'warning' | 'error';
title?: string;
message?: string;
actionLabel?: string | null;
actionPath?: string | null;
sourceType?: string | null;
sourceId?: string | null;
metadata?: Record<string, unknown>;
createdBy?: string | null;
createdByName?: string | null;
readAt?: string | null;
createdAt?: string;
updatedAt?: string;
}
export interface ImportIssueItem {
id: string;
rowNo?: number | null;
@@ -1008,6 +1031,17 @@ export async function loadTenantContentNotifications(input: {
});
}
export async function loadUserNotifications(input: {
userId?: string;
status?: UserNotificationAdminItem['status'];
notificationType?: string;
limit?: number;
} = {}) {
return apiRequest<{ items?: UserNotificationAdminItem[]; summary?: Record<string, number> }>('/api/tenant-admin/user-notifications', {
query: { ...input, limit: input.limit || 100 },
});
}
export async function updateTenantContentNotificationStatus(input: {
notificationIds: string[];
status: 'read' | 'dismissed' | 'resolved';