/* views/projects.js —— 项目池与项目详情。 */ (function () { "use strict"; const T = window.KF_TAXONOMY; const U = window.KF_UTIL; const Q = window.KF_QUERY; const UI = window.KF_UI; const esc = U.esc; const SORTS = { newest: { label: "最近活动", fn: (a, b) => new Date(b.lastActiveAt || 0) - new Date(a.lastActiveAt || 0) }, score: { label: "评分", fn: (a, b) => U.cmpNum(a.score, b.score, -1) }, priority: { label: "优先级", fn: (a, b) => { const r = (p) => (p === "S" ? 0 : p === "A" ? 1 : p === "B" ? 2 : 3); return r(a.priority) - r(b.priority) || U.cmpNum(a.score, b.score, -1); } }, artifactCount: { label: "成果数", fn: (a, b) => U.cmpNum(a.artifactCount, b.artifactCount, -1) } }; /* 项目层的优先级/品类可能为空(早期数据):用"未定"作为明确的筛选值,计数里列出来, * 而不是让它们从筛选条里悄悄消失。 */ const UNSET = "未定"; function rowOf(p) { return { ...p, evidenceKey: "none", ownerName: p.ownerName, priority: T.PRIORITIES.includes(p.priority) ? p.priority : UNSET, category: p.category || UNSET }; } function facet(rows, c, key, keyFn) { const m = new Map(); for (const r of rows) { if (!Q.test(r, { ...c, [key]: [] })) continue; const k = keyFn(r); if (k != null) m.set(k, (m.get(k) || 0) + 1); } return m; } function row(p) { return ` ${esc(p.title)}${esc(p.category || "品类未定")}${p.summary ? " · " + esc(p.summary) : ""} ${UI.person(p.ownerName)} ${UI.stageCell(p.stageObj, p.stage)} ${UI.priorityBadge(p.priority)} ${UI.stateBadge(p.status, p.statusLabel)} ${p.score == null ? "—" : p.score.toFixed(1)} ${p.artifactCount} ${esc(U.fmtAgo(p.lastActiveAt))} `; } function listView(db, c) { const all = db.projects.map(rowOf); const rows = all.filter((r) => Q.test(r, c)); const sortKey = SORTS[c.sort] ? c.sort : "newest"; const sorted = rows.slice().sort(SORTS[sortKey].fn); const pc = facet(all, c, "priority", (r) => r.priority); const sc = facet(all, c, "stage", (r) => (r.stageObj ? r.stageObj.code : null)); const cats = [...new Set(all.map((r) => r.category).filter(Boolean))].sort((a, b) => a.localeCompare(b, "zh")); const cc = facet(all, c, "category", (r) => r.category); const mc = facet(all, c, "member", (r) => r.ownerName); const dims = [ { key: "priority", label: "优先级", options: T.PRIORITIES.map((v) => ({ value: v, label: v, count: pc.get(v) || 0 })) }, { key: "stage", label: "阶段", options: T.STAGES.map((s) => ({ value: s.code, label: s.code, count: sc.get(s.code) || 0 })) }, { key: "category", label: "品类", options: cats.map((v) => ({ value: v, label: v, count: cc.get(v) || 0 })) }, { key: "member", label: "成员", multi: true, options: [...mc.entries()].map(([v, n]) => ({ value: v, label: v, count: n })) } ]; dims.forEach((d) => { d.active = c[d.key] || []; }); let body; if (!all.length) { body = UI.empty({ title: "项目池是空的", body: "学员在 S3 以后从机会卡立项,KfProject 会出现新项目;这里会显示它的阶段、优先级、评分和成果数。" }); } else if (!sorted.length) { body = UI.empty({ title: "当前条件下没有项目", body: `项目池有 ${all.length} 个,但没有同时满足全部条件的。`, actions: [{ label: "清除全部筛选", act: "clear-filters" }] }); } else { body = `
${sorted.map(row).join("")}
项目负责人阶段优先级状态评分成果最近活动
`; } return `

项目

每个项目是一个学员从机会卡推进出的提案。评分右上对齐,方便横向比较。

${Object.entries(SORTS).map(([k, s]) => ``).join("")}
${UI.filterBar(dims, c, rows.length, "个项目")}
${UI.activeLine(c)}
${body}
`; } /* ---------- 详情 ---------- */ function detail(db, id) { const p = db.projectById[id]; if (!p) return null; const arts = p.artifactIds.map((x) => db.artifactById[x]).sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)); const withQuote = arts.filter((a) => a.quoteCount > 0).length; const withOwner = arts.filter((a) => a.ownerKey).length; const citedInsights = db.insights.filter((i) => i.artifactIds.some((x) => p.artifactIds.includes(x))); const head = `
${UI.stateBadge(p.status, p.statusLabel)}创建于 ${esc(U.fmtDate(p.createdAt, true))}

${esc(p.title)}

负责人${esc(p.ownerName || "—")}
当前阶段${esc(p.stage)}
优先级${esc(p.priority || "—")}
评分${p.score == null ? "—" : p.score.toFixed(1)}
品类${esc(p.category || "待定")}
成果数${arts.length}
带出身(谁/会话)${withOwner} / ${arts.length}
带原文出处${withQuote} / ${arts.length}
被归纳引用${citedInsights.length} 条
`; const list = arts.length ? `
${arts.map((a) => ``).join("")}
成果阶段层证据提交时间
${esc(a.title)}${esc(a.typeLabel)} ${UI.stageCell(a.stageObj, a.stage)} ${UI.layerBadge(a.layer)} ${UI.evidenceBadge(a)} ${esc(U.fmtDate(a.createdAt, true))}
` : UI.empty({ sm: true, title: "这个项目还没有成果", body: "学员在这个项目下的对话产出会在这里依次出现,每一条都带阶段和证据情况。" }); const ins = citedInsights.length ? `
${citedInsights.map((i) => `

${esc(i.title)}

${i.ownerCount} 人提到${i.quoteCount} 条原文引用本项 ${i.artifactIds.filter((x) => p.artifactIds.includes(x)).length} 条记录
`).join("")}
` : UI.empty({ sm: true, title: "尚未被归纳引用", body: "当本项目的成果足够支撑某个结论时,归纳区会指向这里的记录。" }); return `
${UI.crumb([{ label: "项目", href: "#/projects" }, { label: p.title }])}
${head}

成果时间线

${arts.length} 条
${list}

相关归纳

${ins} ${p.summary ? `

项目摘要

${esc(p.summary)}
` : ""}
`; } window.KF_VIEW_PROJECTS = { listView, detail, SORTS }; })();