-
PALM REPORT · {handText[report.hand_side]}
-
赛博先生手相报告
+
+ {readingMeta[type].label}
+ {type === "palm" ? ` · ${handText[handSide]}` : ""}
+
+
赛博先生{readingMeta[type].name}报告
{data.overall_summary}
@@ -413,6 +652,16 @@ function formatDate(value: string) {
return value.replace("T", " ").slice(0, 16);
}
+function readViewFromPath(fallback: View): View {
+ if (typeof window === "undefined") return "home";
+ const segment = window.location.pathname.replace(/^\/+/, "").split("/")[0] || "home";
+ return views.includes(segment as View) ? (segment as View) : fallback;
+}
+
+function pathForView(view: View) {
+ return view === "home" ? "/" : `/${view}`;
+}
+
function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
diff --git a/web/lib/api.ts b/web/lib/api.ts
index 0a161ee..8b51813 100644
--- a/web/lib/api.ts
+++ b/web/lib/api.ts
@@ -4,6 +4,7 @@ const TOKEN_KEY = "cyber_mister_token";
const CLIENT_KEY = "cyber_mister_client_id";
export type HandSide = "left" | "right" | "unknown";
+export type ReadingType = "palm" | "face" | "bazi";
export type Dimension = {
name: string;
@@ -28,24 +29,28 @@ export type ReportData = {
disclaimer: string;
};
-export type Report = {
+export type Reading = {
id: string;
+ reading_type: ReadingType;
status: "pending" | "processing" | "completed" | "failed";
- hand_side: HandSide;
+ input_data: Record;
error_message?: string | null;
report_data?: ReportData | null;
created_at: string;
updated_at: string;
};
-export type ReportSummary = {
+export type ReadingSummary = {
id: string;
- status: Report["status"];
- hand_side: HandSide;
+ reading_type: ReadingType;
+ status: Reading["status"];
created_at: string;
overall_summary?: string | null;
};
+export type Report = Reading & { hand_side?: HandSide };
+export type ReportSummary = ReadingSummary & { hand_side?: HandSide };
+
export function getStoredToken() {
if (typeof window === "undefined") return "";
return localStorage.getItem(TOKEN_KEY) || "";
@@ -89,11 +94,19 @@ export async function apiFetch(path: string, options: RequestInit = {}): Prom
}
export async function uploadPalmImage(file: File) {
+ return uploadImage(file, "palm");
+}
+
+export async function uploadFaceImage(file: File) {
+ return uploadImage(file, "face");
+}
+
+async function uploadImage(file: File, type: "palm" | "face") {
const token = await ensureToken();
const formData = new FormData();
formData.append("file", file);
- const response = await fetch(`${API_BASE_URL}/uploads/palm`, {
+ const response = await fetch(`${API_BASE_URL}/uploads/${type}`, {
method: "POST",
headers: { authorization: `Bearer ${token}` },
body: formData,