108 lines
3.6 KiB
TypeScript
108 lines
3.6 KiB
TypeScript
|
|
/**
|
||
|
|
* Live 邀請網絡 — 對齊 apps/backend /api/v1/invite/*
|
||
|
|
*/
|
||
|
|
import type {
|
||
|
|
InviteMemberBrief,
|
||
|
|
InviteTreeNode,
|
||
|
|
MyInviteNetwork,
|
||
|
|
Role,
|
||
|
|
MemberStatus,
|
||
|
|
} from "../../domain/types";
|
||
|
|
import type { InviteRepo } from "../repos";
|
||
|
|
import { apiRequest } from "./http";
|
||
|
|
|
||
|
|
function uidToString(uid: unknown): string {
|
||
|
|
if (typeof uid === "number" || typeof uid === "bigint") return String(uid);
|
||
|
|
if (typeof uid === "string") return uid;
|
||
|
|
return String(uid ?? "");
|
||
|
|
}
|
||
|
|
|
||
|
|
function mapBrief(raw: Record<string, unknown>): InviteMemberBrief {
|
||
|
|
const parentRaw = raw.parent_uid;
|
||
|
|
return {
|
||
|
|
uid: uidToString(raw.uid),
|
||
|
|
email: String(raw.email ?? ""),
|
||
|
|
display_name: String(raw.display_name ?? ""),
|
||
|
|
invite_code: String(raw.invite_code ?? "—"),
|
||
|
|
parent_uid:
|
||
|
|
parentRaw === null || parentRaw === undefined || parentRaw === 0 || parentRaw === "0"
|
||
|
|
? null
|
||
|
|
: uidToString(parentRaw),
|
||
|
|
roles: (Array.isArray(raw.roles) ? raw.roles : ["member"]) as Role[],
|
||
|
|
status: (raw.status === "suspended" ? "suspended" : "active") as MemberStatus,
|
||
|
|
downline_count: Number(raw.downline_count ?? 0),
|
||
|
|
total_downline_count: Number(raw.total_downline_count ?? 0),
|
||
|
|
depth: Number(raw.depth ?? 0),
|
||
|
|
joined_at: Number(raw.joined_at ?? 0),
|
||
|
|
avatar_url:
|
||
|
|
raw.avatar_url != null && String(raw.avatar_url).trim() !== ""
|
||
|
|
? String(raw.avatar_url)
|
||
|
|
: null,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function mapTree(raw: Record<string, unknown>): InviteTreeNode {
|
||
|
|
const childrenRaw = Array.isArray(raw.children) ? raw.children : [];
|
||
|
|
return {
|
||
|
|
...mapBrief(raw),
|
||
|
|
children: childrenRaw.map((c) => mapTree(c as Record<string, unknown>)),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function mapNetwork(raw: Record<string, unknown>): MyInviteNetwork {
|
||
|
|
const me = mapBrief((raw.me || {}) as Record<string, unknown>);
|
||
|
|
const uplineRaw = raw.upline as Record<string, unknown> | null | undefined;
|
||
|
|
const downRaw = Array.isArray(raw.downlines) ? raw.downlines : [];
|
||
|
|
return {
|
||
|
|
me,
|
||
|
|
upline: uplineRaw ? mapBrief(uplineRaw) : null,
|
||
|
|
downlines: downRaw.map((d) => mapBrief(d as Record<string, unknown>)),
|
||
|
|
total_downline_count: Number(raw.total_downline_count ?? me.total_downline_count ?? 0),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createLiveInviteRepo(): InviteRepo {
|
||
|
|
return {
|
||
|
|
async getMyNetwork() {
|
||
|
|
const data = await apiRequest<Record<string, unknown>>("/api/v1/invite/me");
|
||
|
|
return mapNetwork(data);
|
||
|
|
},
|
||
|
|
async applyInviteCode(code) {
|
||
|
|
const data = await apiRequest<Record<string, unknown>>("/api/v1/invite/apply", {
|
||
|
|
method: "POST",
|
||
|
|
body: { code },
|
||
|
|
});
|
||
|
|
return mapNetwork(data);
|
||
|
|
},
|
||
|
|
async getTree() {
|
||
|
|
const data = await apiRequest<{ list?: Record<string, unknown>[] }>("/api/v1/invite/tree");
|
||
|
|
return (data.list ?? []).map((n) => mapTree(n));
|
||
|
|
},
|
||
|
|
async moveMember(uid, newParentUid) {
|
||
|
|
const body: { uid: number; parent_uid?: number } = {
|
||
|
|
uid: Number(uid),
|
||
|
|
};
|
||
|
|
if (newParentUid != null && String(newParentUid).trim() !== "") {
|
||
|
|
body.parent_uid = Number(newParentUid);
|
||
|
|
} else {
|
||
|
|
body.parent_uid = 0;
|
||
|
|
}
|
||
|
|
const data = await apiRequest<Record<string, unknown>>("/api/v1/invite/move", {
|
||
|
|
method: "POST",
|
||
|
|
body,
|
||
|
|
});
|
||
|
|
return mapBrief(data);
|
||
|
|
},
|
||
|
|
async listParentCandidates(excludeSubtreeRootUid) {
|
||
|
|
const q =
|
||
|
|
excludeSubtreeRootUid && String(excludeSubtreeRootUid).trim()
|
||
|
|
? `?exclude_subtree_root_uid=${encodeURIComponent(String(excludeSubtreeRootUid))}`
|
||
|
|
: "";
|
||
|
|
const data = await apiRequest<{ list?: Record<string, unknown>[] }>(
|
||
|
|
`/api/v1/invite/parent-candidates${q}`,
|
||
|
|
);
|
||
|
|
return (data.list ?? []).map((b) => mapBrief(b));
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|