Slide-out drawer showing full store intel, AI talk track, and refresh capability for StackRecon
This skill triggers when editing these files:
app/(app)/components/CallCardDrawer.tsxapp/api/store/[domain]/talk-track/route.tsapp/api/store/[domain]/refresh/route.tsapp/api/store/[domain]/detection/route.tsKeywords: CallCardDrawer, talk-track, talkTrack, onRefreshComplete, ANGLE_TAGS
You are working on the call-card — a slide-out drawer (50vw, 480px–960px) that shows full store intel and an AI-generated sales talk track.
app/(app)/components/CallCardDrawer.tsx — client component; all drawer UI, state, nav, refresh confirmapp/api/store/[domain]/talk-track/route.ts — GET; returns cached or freshly generated talk track (Claude Haiku)app/api/store/[domain]/refresh/route.ts — POST; re-scrapes the store via processSingleUrl using the shared browser pool, nulls talk_trackapp/api/store/[domain]/detection/route.ts — GET; returns SSEResultEvent-compatible shape for buildTableRowTalkTrackResult shape: { talk_track: string, why_pitch: { their_stack, the_gap, your_angle } }. Stored as JSON string in stores.talk_track.TALK_TRACK_VERSION constant gates cache hits — if talk_track_version < TALK_TRACK_VERSION the route regenerates. Bump this constant to invalidate all cached talk tracks.COMPETITIVE_REPLACE / FIRST_SMS_PITCH / EXISTING_CUSTOMER / NON_SHOPIFY) is derived in the talk-track route and written to ai_logs via upsertAiLog. The drawer displays angle tags from ANGLE_TAGS keyed by SmsCategory.btn-secondary) → close icon button (btn-icon-circle). No separate close header row.Stack | Gap | Angle) with 1px dividers between columns. Only shows when talkTrack is non-null. Uses transparent background with border-top/bottom — not the old purple card.getSharedBrowser() from lib/browser-pool (returns the 'primary' named instance by default) and passes it to processSingleUrl(url, { primary: browser }) — it does not launch a temporary browser per refresh.INSERT … ON CONFLICT always sets talk_track=NULL, talk_track_version=1. After POST resolves, the drawer calls onRefreshComplete(domain) to re-fetch the row from the parent./api/store/${domain}/contacts (separate endpoint, not in this domain) on drawer open.fetchTalkTrack and contacts fetch when open changes or row changes — always clean up.platform, smsProviders, emailPlatform, popupTool) render in 'JetBrains Mono', monospace — never DM Sans. Use capitalize() helper for platform; use '\u2014' (em dash) for null values.<a> tag — the entire domain + chain icon is the clickable link. Do not add a separate external-link icon.btn-secondary for "Reanalyze", btn-icon-circle for close — defined in globals.css. Do not inline those styles.platform === 'shopify' AND products.length > 0. Show <ProductsUnavailableNotice> otherwise — do not hide the section.@next/next/no-img-element on product images (<img>) — intentional, products use raw URLs from the store.error category rows skip talk track entirely (smsCategory === 'error' guard in useEffect).detection/route.ts must return smsProviders as an array (split sms_tool on comma) — buildTableRow depends on this shape.DESIGN-SYSTEM.mdLast Updated: 2026-04-02