Connect evidence-backed business model assessments to company categories
This commit is contained in:
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import IncomeDistribution from "./IncomeDistribution";
|
||||
import ArrDisclosures from "./ArrDisclosures";
|
||||
import BusinessModelAssessment from "./BusinessModelAssessment";
|
||||
import {
|
||||
categorySummary, evidenceUrl, technicalReviewLabels, reviewCompanyIds, type ResearchEvidence, type ResearchReview, type ResearchSnapshot,
|
||||
} from "@/lib/ai-research";
|
||||
@@ -56,7 +57,7 @@ export default function AICompanyResearch() {
|
||||
: router.isReady && typeof linkedCompany === "string" && data?.companies.some((c) => c.id === linkedCompany)
|
||||
? linkedCompany : null;
|
||||
function setSelected(id: string | null) { setSelection({ route: router.asPath, id }); }
|
||||
const [tab, setTab] = useState<"companies" | "revenue" | "sources" | "reviews">("companies");
|
||||
const [tab, setTab] = useState<"companies" | "revenue" | "models" | "sources" | "reviews">("companies");
|
||||
const companySection = useRef<HTMLElement>(null);
|
||||
const detailSection = useRef<HTMLElement>(null);
|
||||
useEffect(() => {
|
||||
@@ -128,7 +129,7 @@ export default function AICompanyResearch() {
|
||||
<section ref={companySection} className="research-section" aria-labelledby="research-records-title">
|
||||
<h2 id="research-records-title">研究数据</h2>
|
||||
<div className="research-tabs" role="group" aria-label="研究数据视图">
|
||||
{([['companies','公司与产品'],['revenue','收入观测'],['sources','原始证据'],['reviews','核查记录']] as const).map(([id,label]) =>
|
||||
{([['companies','公司与产品'],['revenue','收入观测'],['models','商业模式初评'],['sources','原始证据'],['reviews','核查记录']] as const).map(([id,label]) =>
|
||||
<button key={id} aria-pressed={tab === id} onClick={() => setTab(id)}>{label}</button>)}
|
||||
</div>
|
||||
{tab === "companies" && <>
|
||||
@@ -153,6 +154,7 @@ export default function AICompanyResearch() {
|
||||
{!filtered.length && <p className="research-message">没有匹配公司,请调整筛选条件。</p>}
|
||||
{filtered.length > pageSize && <nav className="research-pagination" aria-label="公司数据分页"><button disabled={page === 0} onClick={() => setPage((n) => n - 1)}>上一页</button><span>{page + 1} / {Math.ceil(filtered.length / pageSize)}</span><button disabled={(page + 1) * pageSize >= filtered.length} onClick={() => setPage((n) => n + 1)}>下一页</button></nav>}
|
||||
</>}
|
||||
{tab === "models" && <BusinessModelAssessment data={data} onSelect={setSelected} onChooseCategory={chooseCategory} />}
|
||||
{tab === "revenue" && <>
|
||||
<IncomeDistribution data={data} onSelect={setSelected} />
|
||||
<ArrDisclosures data={data} onSelect={setSelected} />
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { businessModelReviewType, evidenceUrl, reviewCompanyIds, type ResearchSnapshot } from "@/lib/ai-research";
|
||||
|
||||
export default function BusinessModelAssessment({ data, onSelect, onChooseCategory }: {
|
||||
data: ResearchSnapshot; onSelect: (id: string) => void; onChooseCategory: (id: string) => void;
|
||||
}) {
|
||||
const reviews = data.reviews.filter((review) => review.type === businessModelReviewType && review.scope === "companies");
|
||||
const evidence = new Map(data.evidence.map((item) => [item.id, item]));
|
||||
return <section aria-labelledby="research-models-title">
|
||||
<h3 id="research-models-title">商业模式初评</h3>
|
||||
<p>从已核个案提炼收费方式、成本约束与下一步验证条件。已知事实与研究判断分列;同一种收费方式可以跨业务分类,以下个案不能代表整个行业。</p>
|
||||
<p className="research-muted">这是基于当前证据的初步评估,不是收入预测或盈利排名。判断能否用于我们的业务,还需用实际客户任务、付费留存与交付成本验证;ARR 或平台 MRR 单独不能证明利润。</p>
|
||||
{reviews.length ? <div className="research-table-wrap"><table>
|
||||
<caption>收费模式与业务分类、公司明细及原始证据相互关联</caption>
|
||||
<thead><tr><th scope="col">模式 / 业务分类</th><th scope="col">已核事实</th><th scope="col">初步判断 / 验证条件</th><th scope="col">参考个案与证据</th></tr></thead>
|
||||
<tbody>{reviews.map((review) => {
|
||||
const companyIds = new Set(reviewCompanyIds(data, review));
|
||||
const companies = data.companies.filter((company) => companyIds.has(company.id));
|
||||
const categories = data.categories.filter((category) => companies.some((company) => company.cat === category.id));
|
||||
return <tr key={review.id}>
|
||||
<td><strong>{review.company}</strong><div>{categories.map((category) => <button key={category.id} onClick={() => onChooseCategory(category.id)}>{category.name}</button>)}</div></td>
|
||||
<td>{review.summary}</td><td>{review.handling}</td>
|
||||
<td><div>{companies.map((company) => <button key={company.id} className="research-company-name" onClick={() => onSelect(company.id)}>{company.name}</button>)}</div>
|
||||
<div>{review.evidenceIds.map((id) => {
|
||||
const item = evidence.get(id); const url = item && evidenceUrl(item.url);
|
||||
return item && url ? <p key={id}><a href={url} target="_blank" rel="noopener noreferrer">{item.title} ↗</a></p> : null;
|
||||
})}</div>
|
||||
</td>
|
||||
</tr>;
|
||||
})}</tbody>
|
||||
</table></div> : <p>当前数据版本尚未记录商业模式初评。</p>}
|
||||
</section>;
|
||||
}
|
||||
Reference in New Issue
Block a user