// 내 지난 예측 — 로그인 없이 이메일 기준 조회/저장 // ───────────────────────────────────────────────────────────── // UI(Arena.tsx)는 아래 4개 함수의 "시그니처(입출력 타입)"에만 의존한다. // 백엔드 개발자는 fetchMyPredictions / saveMyPrediction 의 *본문만* // Firebase 콜러블 함수(getMyPredictions / submitPrediction) 호출로 교체하면 // UI 코드는 수정할 필요가 없다. (rememberEmail/recallEmail 은 그대로 클라 보관) // // 현재 구현: 데모용 localStorage (기기 1대 기준). 운영에서는 Firestore // user_predictions 를 이메일로 조회하는 콜러블로 대체 → 기기 간 동기화. import type { Outcome } from "./types"; /** 이메일 기준으로 조회되는 1건의 사용자 예측 (표시·정렬용 필드 포함) */ export type MyPrediction = { matchId: string; teamAShort: string; // "멕시코" teamBShort: string; // "한국" kickoffKst: string; // ISO — 정렬/표시용 outcome: Outcome; scoreA: number; scoreB: number; submittedAt: string; // ISO — 제출 시각(최신순 정렬 기준) /** 경기 종료 시 백엔드가 채움. 미종료면 undefined */ result?: { outcome: Outcome; scoreA: number; scoreB: number; hitOutcome: boolean }; }; const STORE_KEY = "tp_my_predictions_v1"; // { [emailLower]: MyPrediction[] } const EMAIL_KEY = "tp_email_v1"; const normEmail = (email: string) => email.trim().toLowerCase(); function readStore(): Record { if (typeof window === "undefined") return {}; try { return JSON.parse(window.localStorage.getItem(STORE_KEY) || "{}"); } catch { return {}; } } function writeStore(store: Record) { if (typeof window === "undefined") return; window.localStorage.setItem(STORE_KEY, JSON.stringify(store)); } /* ============================================================ * 백엔드 연동 지점 ① — 내 예측 목록 조회 * 개발자: 본문을 getMyPredictions 콜러블 호출로 교체. * const { fns } = getFirebase(); * const res = await httpsCallable(fns, "getMyPredictions")({ email }); * return res.data as MyPrediction[]; // 최신순 정렬 보장 * ============================================================ */ export async function fetchMyPredictions(email: string): Promise { if (!email) return []; const list = readStore()[normEmail(email)] ?? []; // 최신 제출순(내림차순) return [...list].sort((a, b) => b.submittedAt.localeCompare(a.submittedAt)); } /* ============================================================ * 백엔드 연동 지점 ② — 내 예측 제출/갱신 * 개발자: 본문을 submitPrediction 콜러블 호출로 교체(서버에서 crowd_stats * 원자적 증분 + PII 보호). 경기당 1건(matchId 기준 upsert). * ============================================================ */ export async function saveMyPrediction(email: string, rec: MyPrediction): Promise { if (!email) return; const key = normEmail(email); const store = readStore(); const list = store[key] ?? []; const next = list.filter((p) => p.matchId !== rec.matchId); // 같은 경기는 최신 픽으로 교체 next.push(rec); store[key] = next; writeStore(store); } /** 마지막 사용 이메일 기억 (재방문 시 자동 복원 — 로그인 대체) */ export function rememberEmail(email: string) { if (typeof window === "undefined" || !email) return; window.localStorage.setItem(EMAIL_KEY, normEmail(email)); } /** 기억된 이메일 불러오기 (없으면 "") */ export function recallEmail(): string { if (typeof window === "undefined") return ""; return window.localStorage.getItem(EMAIL_KEY) || ""; }