{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "detailcard", "type": "registry:component", "title": "DetailCard", "description": "Componente Concorde DetailCard — un solo archivo, DetailCard.tsx.", "dependencies": [ "react" ], "registryDependencies": [], "files": [ { "path": "components/DetailCard.tsx", "type": "registry:file", "target": "components/DetailCard.tsx", "content": "\"use client\";\n\n/**\n * DetailCard — Generado por Concorde\n * Fuente: Figma VOYAGER · \"DetailCard\" (nodo 2007:18447 / Container 2007:16954)\n *\n * Tarjeta de detalle de oferta: header oscuro (Inicia / fecha / hora + LikeButton +\n * 3 stats) sobre cuerpo blanco (título + botón Participa + Precio Base con gema +\n * comisión). 2 variantes según el botón/acento:\n * · live → botón naranja→lila (#ED8936 → #8460E5), glow naranja\n * · negotiable → botón teal→lila (#00AEB1 → #8460E5), glow teal\n * 311 de ancho. Botón con hover/press, LikeButton clickeable.\n */\n\nimport { useId } from \"react\";\nimport type { JSX, MouseEventHandler } from \"react\";\nimport LikeButton from \"@/concorde/components/LikeButton\";\nimport PriceIcon from \"@/concorde/components/PriceIcon\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type DetailCardVariant = \"live\" | \"negotiable\";\n\nexport interface DetailCardProps {\n /** Variante de acento del botón (default \"live\") */\n variant?: DetailCardVariant;\n /** Etiqueta superior izquierda (default \"Inicia\" en live · \"Cierra\" en negotiable) */\n startLabel?: string;\n /** Fecha (default \"LUNES 04\") */\n day?: string;\n /** Hora (default \"11:11 pm\") */\n time?: string;\n /** Vistas / likes / participantes (default 11) */\n views?: number | string;\n likes?: number | string;\n participants?: number | string;\n /** Texto del banner teal (default solo en negotiable: \"¡Aprende a negociar con Subastin!\"). Pasa \"\" para ocultarlo. */\n bannerText?: string;\n /** Título del cuerpo */\n title?: string;\n /** Etiqueta del precio (default \"Precio Base:\") */\n priceLabel?: string;\n /** Precio formateado (default \"US$ 12,999\") */\n price?: string;\n /** Texto de comisión */\n commission?: string;\n /** Estado del like (controlado) */\n liked?: boolean;\n onLikeChange?: (active: boolean) => void;\n /** Texto del botón (default \"Participa\") */\n ctaLabel?: string;\n onParticipate?: MouseEventHandler;\n className?: string;\n}\n\n// ─── Self-contained CSS ───────────────────────────────────────────────────────\n\nconst STYLE_ID = \"concorde-detailcard-styles\";\n\nconst DETAILCARD_STYLES = `\n.pdetail {\n width: 311px;\n max-width: 100%;\n border-radius: 0 0 8px 8px;\n overflow: hidden;\n background: #ffffff;\n box-shadow: rgba(0,0,0,0.1) 0px 0px 16px 4px;\n font-family: var(--vmc-font-display, \"Plus Jakarta Sans\", -apple-system, sans-serif);\n}\n\n/* ── Header oscuro ── */\n.pdetail__head {\n position: relative;\n height: 130px;\n background: linear-gradient(157deg, #5F3ED8 0%, #340091 50%, #140046 100%);\n color: #ffffff;\n box-sizing: border-box;\n}\n.pdetail__head::before {\n content: \"\";\n position: absolute;\n top: 0; left: 0; right: 0;\n height: 38px;\n background: linear-gradient(180deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 100%);\n pointer-events: none;\n}\n.pdetail__start { position: absolute; top: 14px; left: 20px; z-index: 1; }\n.pdetail__start small { display: block; font-size: 14px; font-weight: 400; opacity: 1; line-height: 1.45; }\n.pdetail__start b { display: block; font-size: 17px; font-weight: 700; line-height: 1.2; letter-spacing: 0.01em; }\n.pdetail__time { position: absolute; top: 25px; right: 20px; font-size: 17px; font-weight: 600; z-index: 1; }\n.pdetail__vline { position: absolute; top: 8px; left: 50%; width: 1px; height: 19px; background: rgba(255,255,255,0.6); transform: translateX(-50%); }\n.pdetail__hline { position: absolute; top: 58px; height: 1px; background: rgba(255,255,255,0.6); }\n.pdetail__hline--l { left: 20px; width: 99.5px; }\n.pdetail__hline--r { right: 20px; width: 99.5px; }\n.pdetail__like { position: absolute; top: 38px; left: 50%; transform: translateX(-50%); z-index: 2; }\n.pdetail__stats {\n position: absolute;\n bottom: 16px; left: 53px; right: 58px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n z-index: 1;\n}\n.pdetail__stat { display: flex; align-items: center; gap: 12px; }\n.pdetail__stat b { font-size: 16px; font-weight: 700; }\n.pdetail__statico {\n width: 32px; height: 32px;\n border-radius: 50%;\n border: 1px solid rgba(255,255,255,0.3);\n display: flex; align-items: center; justify-content: center;\n color: #ffffff;\n}\n\n/* ── Banner teal (solo negotiable · 311×60) ── */\n.pdetail__banner {\n height: 60px;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n padding: 0 24px;\n background: linear-gradient(90deg, #00DAE0 0%, #008688 100%);\n color: #ffffff;\n font-size: 16px;\n font-weight: 700;\n line-height: 1.25;\n}\n\n/* ── Cuerpo blanco (311×260) ── */\n.pdetail__body { padding: 0 16px 40px; text-align: center; }\n/* text/VYbody-xsm · Plus Jakarta Sans · 400 · 12px · line-height space/200 (16px) */\n.pdetail__title { margin: 44px auto 0; max-width: 216px; font-size: 12px; font-weight: 400; letter-spacing: 0; color: #191C1C; line-height: 16px; }\n\n/* ── Botón Participa — misma interacción que Button (primary/negotiable) ── */\n@property --pdangle { syntax: \"\"; inherits: false; initial-value: 135deg; }\n@property --pda { syntax: \"\"; inherits: false; initial-value: #ed8936; }\n@property --pdb { syntax: \"\"; inherits: false; initial-value: #8460e5; }\n\n.pdetail__cta {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n margin-top: 12px;\n width: 186px;\n height: 48px;\n padding: 0 20px;\n border-radius: 9999px;\n border: 2px solid transparent;\n cursor: pointer;\n position: relative;\n overflow: hidden;\n font-family: inherit;\n font-size: 16px;\n line-height: 20px;\n font-weight: 600;\n color: #ffffff;\n text-shadow: rgba(0,0,0,0.25) 0 1px 3px;\n background-image:\n linear-gradient(var(--pdangle), var(--pda) 0%, var(--pda) 40%, var(--pdb) 100%),\n var(--pd-border);\n background-origin: padding-box, border-box;\n background-clip: padding-box, border-box;\n box-shadow: rgba(255,255,255,0.28) 0 1px 0 2px inset, var(--pd-glow) 0 2px 6px;\n transition:\n --pdangle 0.4s cubic-bezier(0.25, 0.8, 0.25, 1),\n --pda 0.35s,\n --pdb 0.35s,\n transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1),\n box-shadow 0.25s;\n transform: translateZ(0);\n}\n.pdetail__cta::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: 9999px;\n background: linear-gradient(rgba(255,255,255,0.17) 0%, transparent 55%);\n pointer-events: none;\n z-index: 1;\n}\n.pdetail__cta::after {\n content: \"\";\n position: absolute;\n inset: -4px;\n border-radius: 9999px;\n background: var(--pd-glowgrad);\n filter: blur(14px);\n opacity: 0;\n z-index: -1;\n transition: opacity 0.3s, filter 0.3s;\n}\n.pdetail__cta:hover {\n transform: translateY(-2px) scale(1.02);\n box-shadow: rgba(255,255,255,0.22) 0 1px 0 2px inset, rgba(132,96,229,0.35) 0 8px 24px, var(--pd-glow2) 0 4px 10px;\n}\n.pdetail__cta:hover::after { opacity: 0.45; filter: blur(18px); }\n.pdetail__cta:active {\n color: #d1d5dc;\n transform: scale(0.97) translateY(1px);\n box-shadow: rgba(0,0,0,0.22) 0 2px 5px 2px inset, rgba(0,0,0,0.12) 0 1px 3px;\n}\n.pdetail__cta:active::after { opacity: 0; }\n.pdetail__cta:focus-visible {\n outline: none;\n transform: scale(0.98);\n box-shadow: rgba(255,255,255,0.28) 0 1px 0 2px inset, 0 0 0 3px rgba(132,96,229,0.45);\n}\n\n.pdetail--live .pdetail__cta {\n --pda: #ed8936; --pdb: #8460e5;\n --pd-border: linear-gradient(135deg, #ffffff 0%, #fbc47d 25%, #ae8eff 75%, #ffffff 100%);\n --pd-glow: rgba(237,137,54,0.3);\n --pd-glow2: rgba(237,137,54,0.4);\n --pd-glowgrad: linear-gradient(135deg, #ed8936, #8460e5);\n}\n.pdetail--live .pdetail__cta:hover { --pdangle: 220deg; --pda: #fbc47d; --pdb: #ae8eff; }\n.pdetail--live .pdetail__cta:active { --pdangle: 135deg; --pda: #d46e20; --pdb: #5a35c2; }\n\n.pdetail--negotiable .pdetail__cta {\n --pda: #00aeb1; --pdb: #8460e5;\n --pd-border: linear-gradient(135deg, #ffffff 0%, #4ddcdc 25%, #6445df 75%, #ffffff 100%);\n --pd-glow: rgba(0,174,177,0.3);\n --pd-glow2: rgba(0,174,177,0.4);\n --pd-glowgrad: linear-gradient(135deg, #00aeb1, #8460e5);\n}\n.pdetail--negotiable .pdetail__cta:hover { --pdangle: 220deg; --pda: #4ddcdc; --pdb: #ae8eff; }\n.pdetail--negotiable .pdetail__cta:active { --pdangle: 135deg; --pda: #008688; --pdb: #5a35c2; }\n\n/* ── Precio + comisión ── */\n.pdetail__price {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n margin: 14px 0 0;\n font-size: 12px;\n font-weight: 400;\n letter-spacing: 0;\n color: #4C1EBC;\n}\n.pdetail__price b { font-size: 12px; font-weight: 400; }\n.pdetail__commission {\n margin: 14px auto 0;\n max-width: 215px;\n font-size: 12px;\n font-weight: 400;\n letter-spacing: 0;\n line-height: 16px;\n color: #191C1C;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .pdetail__cta { transition: none; }\n}\n`;\n\nlet _stylesInjected = false;\n\n// ─── Iconos de stats ──────────────────────────────────────────────────────────\n\nfunction EyeIcon(): JSX.Element {\n return (\n \n \n \n \n );\n}\n\nfunction GroupIcon(): JSX.Element {\n return (\n \n \n \n \n \n \n \n \n );\n}\n\nfunction MessageIcon(): JSX.Element {\n return (\n \n \n \n );\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport default function DetailCard({\n variant = \"live\",\n startLabel,\n bannerText,\n day = \"LUNES 04\",\n time = \"11:11 pm\",\n views = 11,\n likes = 11,\n participants = 11,\n title,\n priceLabel = \"Precio Base:\",\n price = \"US$ 12,999\",\n commission,\n liked,\n onLikeChange,\n ctaLabel,\n onParticipate,\n className = \"\",\n}: DetailCardProps): JSX.Element {\n useId();\n\n // Defaults por variante. En negotiable el cuerpo es más corto (sin fila de\n // precio) pero lleva banner teal → el alto total queda igual que en live.\n const isNeg = variant === \"negotiable\";\n const resolvedStartLabel = startLabel ?? (isNeg ? \"Cierra\" : \"Inicia\");\n const resolvedBanner = bannerText ?? (isNeg ? \"¡Aprende a negociar con Subastin!\" : \"\");\n const resolvedTitle = title ?? (isNeg ? \"Aprovecha esta oportunidad y haz una propuesta al vendedor.\" : \"¡Oportunidad para el que sabe!\");\n const resolvedCta = ctaLabel ?? (isNeg ? \"Negocia ahora\" : \"Participa\");\n const resolvedCommission = commission ?? (isNeg ? \"Comisión >S< 0\" : \"Comisión: 7.5% del valor de compra o mínimo >S< 50\");\n\n if (typeof document !== \"undefined\" && !_stylesInjected) {\n if (!document.getElementById(STYLE_ID)) {\n const el = document.createElement(\"style\");\n el.id = STYLE_ID;\n el.textContent = DETAILCARD_STYLES;\n document.head.appendChild(el);\n }\n _stylesInjected = true;\n }\n\n return (\n <>\n