import { THEME_CSS, escapeHtml, escapeAttr, tabNavHTML } from "../render/layout"; import { countProjectAdmins, listProjectMembersWithUsers, listUsers, type ProjectMemberWithUser, type ProjectRole, type ProjectRow, type UserRow, } from "../store"; import { projectSettingsTabsHTML } from "./projectUpstreams"; interface Flash { kind: "ok" | "err"; msg: string; } const ALL_ROLES: ProjectRole[] = ["reader", "writer", "admin"]; function roleBadge(role: ProjectRole): string { const cls = role === "admin" ? "admin" : role === "writer" ? "writer" : "reader"; return `${escapeHtml(role)}`; } export async function buildProjectMembersPage( viewer: UserRow, project: ProjectRow, flash: Flash | null, ): Promise { const members = await listProjectMembersWithUsers(project.id); const users = await listUsers(); const memberLogins = new Set(members.map((m) => m.user_login)); const candidates = users.filter((u) => u.is_active === 1 && !memberLogins.has(u.login)); const flashHtml = flash ? `
${escapeHtml(flash.msg)}
` : ""; const memberRows = await Promise.all(members.map((m) => memberRowHtml(m, viewer, project))); const rowsHtml = members.length ? ` ${memberRows.join("")}
用户角色加入时间操作
` : `
该项目还没有成员记录(site-admin 已自动获得 admin 权限,可在下方添加)。
`; const candOptions = candidates .map((u) => ``) .join(""); const addFormHtml = candidates.length ? `

添加成员

reader = 只读(预留);writer = 可创建 issue、评论、关闭、上传;admin = 可管理 webhook 和成员。
` : `

添加成员

没有可添加的候选用户。先在 用户管理 创建账户。
`; return ` 成员 · ${escapeHtml(project.owner)}/${escapeHtml(project.name)}
👥 ${escapeHtml(project.owner)}/${escapeHtml(project.name)} · 成员
${tabNavHTML("projects")}

${escapeHtml(project.owner)}/${escapeHtml(project.name)} · 成员

${projectSettingsTabsHTML(project.owner, project.name, "members")} ${flashHtml}

可见性

公开 = 任何登录用户都能查看 issue 和评论;私有 = 需要项目成员(reader+)权限。Bot 用户已自动获得 writer 角色,不受影响。

当前成员(${members.length})

${rowsHtml}
${addFormHtml}
`; } async function memberRowHtml(m: ProjectMemberWithUser, viewer: UserRow, project: ProjectRow): Promise { const isSelf = m.user_login === viewer.login; const isAdminCountOne = m.role === "admin" && (await countProjectAdmins(project.id)) <= 1; const kindBadge = m.user_kind === "bot" ? `bot` : m.user_kind === "system" ? `system` : ""; const inactiveBadge = m.user_is_active === 1 ? "" : `禁用`; const selfNote = isSelf ? `(你)` : ""; const disableRole = isSelf && isAdminCountOne; const disableRemove = isAdminCountOne; const roleSelect = `
${disableRole ? `🔒` : ""}
`; const removeBtn = `
`; return ` ${escapeHtml(m.user_login)} ${selfNote} ${kindBadge} ${inactiveBadge} ${roleBadge(m.role)} ${escapeHtml((m.created_at || "").slice(0, 10))} ${roleSelect} ${removeBtn} `; }