Show synastry evidence report card
This commit is contained in:
@@ -392,6 +392,11 @@ button:disabled { cursor: default; opacity: .45; }
|
|||||||
.chart-library-item small, .chart-library-item > span, .empty-library-copy { color: var(--color-ink-secondary); font-size: var(--type-caption); }
|
.chart-library-item small, .chart-library-item > span, .empty-library-copy { color: var(--color-ink-secondary); font-size: var(--type-caption); }
|
||||||
.chart-library-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); }
|
.chart-library-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-2); }
|
||||||
.chart-library-form { padding-top: var(--space-4); border-top: 1px solid var(--color-border); }
|
.chart-library-form { padding-top: var(--space-4); border-top: 1px solid var(--color-border); }
|
||||||
|
.synastry-report-card { display: grid; gap: var(--space-3); padding: var(--space-4); border: 1px solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface-subtle); }
|
||||||
|
.synastry-report-card span, .synastry-report-card small, .synastry-report-card li { color: var(--color-ink-secondary); font-size: var(--type-caption); }
|
||||||
|
.synastry-report-card strong, .synastry-report-card p { color: var(--color-ink); }
|
||||||
|
.synastry-report-card strong { display: block; margin-top: 4px; font-size: var(--type-body); font-weight: 500; }
|
||||||
|
.synastry-report-card p, .synastry-report-card ul { margin: 0; }
|
||||||
|
|
||||||
input, select { width: 100%; min-height: 44px; padding: 0 12px; border: 1px solid var(--color-border-strong); color: var(--color-ink); border-color: var(--color-border-strong); border-radius: var(--radius-md); background: var(--color-canvas); font-size: 14px; }
|
input, select { width: 100%; min-height: 44px; padding: 0 12px; border: 1px solid var(--color-border-strong); color: var(--color-ink); border-color: var(--color-border-strong); border-radius: var(--radius-md); background: var(--color-canvas); font-size: 14px; }
|
||||||
input:disabled, select:disabled { color: var(--color-ink-tertiary); background: var(--color-canvas-muted); }
|
input:disabled, select:disabled { color: var(--color-ink-tertiary); background: var(--color-canvas-muted); }
|
||||||
|
|||||||
@@ -45,6 +45,17 @@ type ChartLibraryApiRecord = {
|
|||||||
profile: Profile;
|
profile: Profile;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
};
|
};
|
||||||
|
type SynastryReportCard = {
|
||||||
|
partnerName: string;
|
||||||
|
score?: number;
|
||||||
|
maxScore?: number;
|
||||||
|
assessment?: string;
|
||||||
|
headline?: string;
|
||||||
|
scoreBand?: string;
|
||||||
|
strengths?: string[];
|
||||||
|
risks?: string[];
|
||||||
|
nextEvidence?: string[];
|
||||||
|
};
|
||||||
type ChatSession = { id: string; title: string; theme: Theme; modelId: string; messages: Message[]; updatedAt: number };
|
type ChatSession = { id: string; title: string; theme: Theme; modelId: string; messages: Message[]; updatedAt: number };
|
||||||
type RequestError = { sessionId: string; message: string };
|
type RequestError = { sessionId: string; message: string };
|
||||||
type StreamingReply = { sessionId: string; text: string };
|
type StreamingReply = { sessionId: string; text: string };
|
||||||
@@ -519,6 +530,7 @@ export default function Home() {
|
|||||||
const [chartLibrary, setChartLibrary] = useState<ChartLibraryRecord[]>([]);
|
const [chartLibrary, setChartLibrary] = useState<ChartLibraryRecord[]>([]);
|
||||||
const [chartLibraryOpen, setChartLibraryOpen] = useState(false);
|
const [chartLibraryOpen, setChartLibraryOpen] = useState(false);
|
||||||
const [otherProfileDraft, setOtherProfileDraft] = useState<Profile>(emptyProfile);
|
const [otherProfileDraft, setOtherProfileDraft] = useState<Profile>(emptyProfile);
|
||||||
|
const [synastryReportCard, setSynastryReportCard] = useState<SynastryReportCard | null>(null);
|
||||||
const [profileOpen, setProfileOpen] = useState(false);
|
const [profileOpen, setProfileOpen] = useState(false);
|
||||||
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
||||||
const [profileNotice, setProfileNotice] = useState("");
|
const [profileNotice, setProfileNotice] = useState("");
|
||||||
@@ -1256,12 +1268,23 @@ export default function Home() {
|
|||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ selfProfile: profile, partnerProfile: record.profile }),
|
body: JSON.stringify({ selfProfile: profile, partnerProfile: record.profile }),
|
||||||
});
|
});
|
||||||
const payload = await response.json().catch(() => null) as { status?: string; evidenceLayers?: string[]; synastry?: { total_score?: number; max_score?: number; assessment?: string }; relationshipReport?: { headline?: string } } | null;
|
const payload = await response.json().catch(() => null) as { status?: string; evidenceLayers?: string[]; synastry?: { total_score?: number; max_score?: number; assessment?: string }; relationshipReport?: { headline?: string; scoreBand?: string; strengths?: string[]; risks?: string[]; nextEvidence?: string[] } } | null;
|
||||||
if (response.ok && payload?.status === "ok") {
|
if (response.ok && payload?.status === "ok") {
|
||||||
const score = payload.synastry?.total_score;
|
const score = payload.synastry?.total_score;
|
||||||
const max = payload.synastry?.max_score;
|
const max = payload.synastry?.max_score;
|
||||||
const assessment = payload.synastry?.assessment;
|
const assessment = payload.synastry?.assessment;
|
||||||
const layers = (payload.evidenceLayers || []).join(" / ") || "Ashtakoot / Moon / D9";
|
const layers = (payload.evidenceLayers || []).join(" / ") || "Ashtakoot / Moon / D9";
|
||||||
|
setSynastryReportCard({
|
||||||
|
partnerName: record.profile.name || "对方",
|
||||||
|
score,
|
||||||
|
maxScore: max,
|
||||||
|
assessment,
|
||||||
|
headline: payload.relationshipReport?.headline,
|
||||||
|
scoreBand: payload.relationshipReport?.scoreBand,
|
||||||
|
strengths: payload.relationshipReport?.strengths,
|
||||||
|
risks: payload.relationshipReport?.risks,
|
||||||
|
nextEvidence: payload.relationshipReport?.nextEvidence,
|
||||||
|
});
|
||||||
chooseSuggestedQuestion([
|
chooseSuggestedQuestion([
|
||||||
baseQuestion,
|
baseQuestion,
|
||||||
"",
|
"",
|
||||||
@@ -1983,6 +2006,24 @@ export default function Home() {
|
|||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
{synastryReportCard && (
|
||||||
|
<article className="synastry-report-card" aria-label="合盘结果摘要">
|
||||||
|
<div>
|
||||||
|
<span>合盘结果摘要</span>
|
||||||
|
<strong>{synastryReportCard.partnerName}</strong>
|
||||||
|
<small>Ashtakoot {synastryReportCard.score ?? "?"}/{synastryReportCard.maxScore ?? "?"} · {synastryReportCard.assessment || synastryReportCard.scoreBand || "待解释"}</small>
|
||||||
|
</div>
|
||||||
|
{synastryReportCard.headline && <p>{synastryReportCard.headline}</p>}
|
||||||
|
<details>
|
||||||
|
<summary>查看证据</summary>
|
||||||
|
<ul>
|
||||||
|
{(synastryReportCard.strengths || []).map((item) => <li key={item}>{item}</li>)}
|
||||||
|
{(synastryReportCard.risks || []).map((item) => <li key={item}>{item}</li>)}
|
||||||
|
</ul>
|
||||||
|
<small>下一步证据:{(synastryReportCard.nextEvidence || []).join(" / ") || "双方 Dasha / UL-DK / D9 7宫"}</small>
|
||||||
|
</details>
|
||||||
|
</article>
|
||||||
|
)}
|
||||||
<form className="profile-form chart-library-form" onSubmit={saveOtherChart}>
|
<form className="profile-form chart-library-form" onSubmit={saveOtherChart}>
|
||||||
<div className="section-heading"><b>添加其他星盘</b><small>用于合盘、亲友盘或客户盘。</small></div>
|
<div className="section-heading"><b>添加其他星盘</b><small>用于合盘、亲友盘或客户盘。</small></div>
|
||||||
<ProfileFields value={otherProfileDraft} onChange={setOtherProfileDraft} nameInputId="other-profile-name" />
|
<ProfileFields value={otherProfileDraft} onChange={setOtherProfileDraft} nameInputId="other-profile-name" />
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ def test_chart_profile_library_has_cloud_table_api_and_local_fallback() -> None:
|
|||||||
"deleteCloudChartProfile",
|
"deleteCloudChartProfile",
|
||||||
"buildSynastryQuestion",
|
"buildSynastryQuestion",
|
||||||
"draftSynastryQuestionFromChart",
|
"draftSynastryQuestionFromChart",
|
||||||
|
"synastryReportCard",
|
||||||
|
"synastry-report-card",
|
||||||
'fetch("/api/synastry"',
|
'fetch("/api/synastry"',
|
||||||
"Ashtakoot",
|
"Ashtakoot",
|
||||||
"chartLibraryStorageKey",
|
"chartLibraryStorageKey",
|
||||||
|
|||||||
Reference in New Issue
Block a user