{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mrdoge-match-card-adapter",
  "title": "Match Card Adapter (Mr. Doge SDK)",
  "description": "Maps a real matches.get()/odds.list() response onto Match Card's props.",
  "dependencies": [
    "@mrdoge/protocol"
  ],
  "registryDependencies": [
    "match-card"
  ],
  "files": [
    {
      "path": "lib/mrdoge-adapters/match-card.ts",
      "content": "import type { MatchDetail, Market } from \"@mrdoge/protocol\"\nimport type {\n  MatchCardDataProps,\n  MatchCardOdds,\n  MatchCardStatus,\n} from \"@/registry/mrdoge-ui/match-card/match-card\"\nimport type { OddsOption, OddsMovement } from \"@/registry/mrdoge-ui/odds-selector/odds-selector\"\n\n// match.status (upcoming/live/completed) is the authoritative field, always\n// kept fresh by the server. Clock.state is finer (adds paused/intermission/\n// interrupted) but comes from the live-stats feed, which can stop updating\n// once a match ends, e.g. frozen on \"live\" at \"90+5'\" instead of ever\n// transitioning to \"finished\". So match.status decides the coarse bucket\n// first; clock.state only refines *within* \"live\".\nfunction toMatchCardStatus(match: MatchDetail): MatchCardStatus {\n  if (match.status === \"upcoming\") return \"scheduled\"\n  if (match.status === \"completed\") return \"finished\"\n  return match.stats?.clock?.state ?? \"live\"\n}\n\n// Outcome order (1/X/2 left-to-right, Over before Under, ...) is decided\n// server-side: the SDK gateway orders every market's lines before they\n// ever reach here, so there's nothing to sort in this adapter.\n//\n// Odds Selector's own examples have room for real team names; Match\n// Card is a lot narrower, so it asks for \"code\" instead (1/X/2), short\n// and never needs truncating.\nexport function toOddsOptions(\n  market?: Market,\n  {\n    labelFrom = \"caption\",\n    movementById,\n  }: {\n    labelFrom?: \"caption\" | \"code\"\n    /** From useOddsMovement(market): id -> \"up\" | \"down\" since the previous snapshot. */\n    movementById?: Record<string, OddsMovement>\n  } = {}\n): OddsOption[] {\n  if (!market) return []\n  return market.lines.map((line) => ({\n    id: line.id,\n    label: labelFrom === \"code\" ? line.code : (line.caption ?? line.code),\n    price: line.price.toFixed(2),\n    suspended: !line.isAvailable,\n    movement: movementById?.[line.id],\n  }))\n}\n\nfunction toMatchCardOdds(\n  market?: Market,\n  movementById?: Record<string, OddsMovement>\n): MatchCardOdds | undefined {\n  if (!market) return undefined\n  return {\n    market: market.displayName,\n    options: toOddsOptions(market, { labelFrom: \"code\", movementById }),\n  }\n}\n\n// Public, unauthenticated, cached CDN. Not part of the Team shape itself.\n// See /docs/reference/images.\nexport function teamLogoUrl(teamId: number) {\n  return `https://api.mrdoge.co/images/teams/${teamId}.png`\n}\n\n/**\n * Maps a real `matches.get()` / `matches.subscribe()` response (plus an\n * optional market from `odds.list()`) to MatchCard's props. If the SDK's\n * shape changes, this fails to compile. That's the point. Returns\n * MatchCardDataProps (not the full MatchCardProps union) since this\n * always has real data to show; callers render `<MatchCard loading />`\n * separately while that data is still in flight.\n *\n * Whether the odds slot should show a skeleton while `market` is still\n * loading isn't this function's call: a caller with no odds feature at\n * all also has `market === undefined`, and this function can't tell the\n * difference. Pass `oddsLoading` as a sibling prop at the call site\n * instead, e.g. `oddsLoading={market === undefined}`, only when you're\n * actually using useOdds.\n *\n * `movementById` is optional. Pass `useOddsMovement(market)`'s result to\n * color options as odds change; omit it and options render without any\n * color change.\n *\n * Odds are dropped once the match is completed, regardless of what\n * `market` holds. Betting closes when a match ends, and a WS subscription\n * has no reason to push another update once there's nothing left to\n * change, so `market` would otherwise just sit frozen on its last live\n * value forever.\n */\nexport function matchToMatchCardProps(\n  match: MatchDetail,\n  market?: Market,\n  movementById?: Record<string, OddsMovement>,\n): MatchCardDataProps {\n  // Red cards are soccer-specific. MatchStats is a discriminated union\n  // per sport, so narrow before reading homeRedCards/awayRedCards.\n  const stats = match.stats?.sport === \"soccer\" ? match.stats : undefined\n\n  return {\n    status: toMatchCardStatus(match),\n    kickoff: match.startTime,\n    // Only meaningful while the match is actually in progress. Once\n    // completed, the clock display is a frozen artifact (e.g. \"90+5'\"),\n    // not a real \"FT\" transition, so drop it and let Match Card show its\n    // own \"FT\" label instead.\n    elapsed: match.status === \"live\" ? (match.stats?.clock?.display ?? undefined) : undefined,\n    home: {\n      name: match.homeTeam.name,\n      logoUrl: teamLogoUrl(match.homeTeam.id),\n      redCards: stats?.homeRedCards,\n    },\n    away: {\n      name: match.awayTeam.name,\n      logoUrl: teamLogoUrl(match.awayTeam.id),\n      redCards: stats?.awayRedCards,\n    },\n    homeScore: match.stats?.homeScore,\n    awayScore: match.stats?.awayScore,\n    odds: match.status === \"completed\" ? undefined : toMatchCardOdds(market, movementById),\n  }\n}\n\n/**\n * Same as matchToMatchCardProps, but keeps odds even once the match is\n * completed. matchToMatchCardProps drops them there deliberately: correct\n * for the live app, where a frozen line would be misleading once betting's\n * closed. Showcase/marketing usage (mrdoge.co's homepage and /ui gallery)\n * wants the opposite: proving the SDK still has the data regardless of\n * match status, suspended lines and all.\n */\nexport function matchToMatchCardPropsWithOdds(\n  match: MatchDetail,\n  market?: Market,\n  movementById?: Record<string, OddsMovement>,\n): MatchCardDataProps {\n  return {\n    ...matchToMatchCardProps(match, market, movementById),\n    odds: toMatchCardOdds(market, movementById),\n  }\n}\n",
      "type": "registry:lib",
      "target": "lib/mrdoge-adapters/match-card.ts"
    }
  ],
  "type": "registry:lib"
}