feat(core/frontend): wire P0 team mgmt / recharge / notifications

- App.tsx: load notifications, expose team/recharge/notification handlers, real unread bell count (was hardcoded 12)
- team.tsx: create/edit/reset-password/remove member + team recharge modals
- account.tsx: recharge via wechat/alipay buttons + custom amount
- messages.tsx: real notification inbox, mark-read on select, mark-all-read
- verified: tsc --noEmit clean; e2e create->update->reset->recharge->mark-read->delete all green

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
seaislee1209
2026-06-05 12:48:51 +08:00
co-authored by Claude Opus 4.8
parent d41e487f08
commit 25bf3293df
6 changed files with 407 additions and 38 deletions
+43
View File
@@ -5,9 +5,12 @@ import type {
BillingSummary,
Ledger,
ModelConfig,
Notification,
NotificationList,
Paginated,
Product,
Project,
RechargeResult,
ScriptVersion,
Team,
TeamMember,
@@ -69,6 +72,28 @@ export const api = {
teamMembers() {
return request<TeamMember[]>("/api/auth/team/members/");
},
createTeamMember(payload: {
username: string;
password: string;
name?: string;
email?: string;
role?: string;
monthly_credit_limit?: number | string;
}) {
return request<TeamMember>("/api/auth/team/members/", { method: "POST", body: JSON.stringify(payload) });
},
updateTeamMember(id: string, payload: { role?: string; monthly_credit_limit?: number | string; name?: string }) {
return request<TeamMember>(`/api/auth/team/members/${id}/`, { method: "PATCH", body: JSON.stringify(payload) });
},
removeTeamMember(id: string) {
return request<void>(`/api/auth/team/members/${id}/`, { method: "DELETE" });
},
resetMemberPassword(id: string, password: string) {
return request<void>(`/api/auth/team/members/${id}/password/`, {
method: "POST",
body: JSON.stringify({ password })
});
},
products() {
return request<Paginated<Product>>("/api/products/");
},
@@ -157,5 +182,23 @@ export const api = {
},
aiTasks() {
return request<Paginated<AITask>>("/api/ai/tasks/");
},
recharge(payload: { amount: number | string; bonus?: number | string; channel?: string }) {
return request<RechargeResult>("/api/billing/recharge/", { method: "POST", body: JSON.stringify(payload) });
},
listNotifications(params?: { type?: string; unread?: boolean }) {
const query = new URLSearchParams();
if (params?.type && params.type !== "all") query.set("type", params.type);
if (params?.unread) query.set("unread", "1");
const qs = query.toString();
return request<NotificationList>(`/api/ops/notifications/${qs ? `?${qs}` : ""}`);
},
markAllNotificationsRead() {
return request<{ updated: number; unread_count: number }>("/api/ops/notifications/mark-all-read/", {
method: "POST"
});
},
markNotificationRead(id: string) {
return request<Notification>(`/api/ops/notifications/${id}/mark-read/`, { method: "POST" });
}
};