Conversation
- Update app/admin/page.tsx to include chat session data and display in stats cards and recent activity - Add dynamic export to app/api/admin/chats/route.ts to ensure server-side rendering is up-to-date - Enhance components/admin/ChatsTable.tsx for better handling of chat sessions - Adjust components/admin/LeadsTable.tsx for consistency with other tables - Refactor components/admin/TranscriptsTable.tsx for improved performance and readability
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…rd parameter. Use signal in fetch – Passed signal into the fetch options so the request can be aborted. AbortController in useEffect – Created an AbortController, passed controller.signal to fetchChats, and returned a cleanup that calls controller.abort() when the effect re-runs or the component unmounts. AbortError handling – Adjusted the catch to ignore AbortError when the request is aborted, and only treat other errors as real failures.
extractContactFields*() – Generator that yields each extracted field with its message index
extractContactInfo() – Returns { email, phone, name } for the chat API and Supabase
hasContactInfo() – Used for contact-capture gating
computeExtractionOrder() – Returns ordered results with message indices for the admin UI
app/api/chat/route.ts – Uses extractContactInfo and hasContactInfo from @/lib/chat-extraction; removed the local implementation and regexes.
lib/chat-extraction-order.ts – Re-exports computeExtractionOrder from lib/chat-extraction.ts, so ChatsTable keeps working without changes.
The input updates immediately as the user types. Search requests run only after 400 ms without input. When the debounced search runs, the page resets to 1 before the fetch
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 4 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| message_count: messages.filter(m => m.role === 'user').length, | ||
| last_message_at: new Date().toISOString(), | ||
| page: page || null, | ||
| status: extracted.email ? 'converted' : 'active', |
There was a problem hiding this comment.
Phone-only leads incorrectly marked as active status
Medium Severity
The status field in upsertChatSession only checks extracted.email to determine 'converted' vs 'active', but hasContactInfo (used for contactCaptured) returns true for phone-only leads too. This means a user who submits only their phone number will no longer see the email capture form, yet their session stays 'active' instead of 'converted'. The status check needs to also consider extracted.phone.
Additional Locations (1)
| message_count: messages.filter(m => m.role === 'user').length, | ||
| last_message_at: new Date().toISOString(), | ||
| page: page || null, | ||
| status: extracted.email ? 'converted' : 'active', |
There was a problem hiding this comment.
Upsert can downgrade converted sessions to active
Medium Severity
The status field is unconditionally set in the upsert (extracted.email ? 'converted' : 'active'), but the extracted_* fields use conditional spreads to avoid overwriting with null. This inconsistency means a page reload (which resets the chat widget's in-memory messages while sessionStorage preserves the same session_id) causes the next message to upsert status: 'active', downgrading a previously 'converted' session. The status field needs the same protective conditional pattern used for extracted_email and friends.
|
|
||
| return ( | ||
| <tr> | ||
| <td colSpan={6} className="p-0"> |
There was a problem hiding this comment.
Table colSpan exceeds actual column count
Low Severity
colSpan={6} is used in the ChatDetailPanel, loading, and empty-state rows, but the table only defines 5 columns (expand arrow, Status, Msgs, Email/Name, Created). The value needs to be 5 to match the actual column count.
Additional Locations (2)
| data: { user }, | ||
| } = await supabase.auth.getUser(); | ||
| return user; | ||
| } |
There was a problem hiding this comment.
Duplicated verifyAuth function across admin route files
Low Severity
The verifyAuth function in the new chats route is an identical copy of the same function already present in four other admin route files. This is now duplicated in five places, increasing maintenance burden and the risk of inconsistent auth changes.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| * The extraction logic lives in lib/chat-extraction.ts to avoid duplication | ||
| * with app/api/chat/route.ts extractContactInfo(). | ||
| */ | ||
| export { computeExtractionOrder } from '@/lib/chat-extraction'; |
There was a problem hiding this comment.
Unnecessary re-export wrapper file adds indirection
Low Severity
lib/chat-extraction-order.ts is a 6-line file that only re-exports computeExtractionOrder from @/lib/chat-extraction. Since chat-extraction.ts uses only type imports and pure computation (no server-only dependencies), the sole consumer (ChatsTable.tsx) can import directly from @/lib/chat-extraction, making this wrapper unnecessary indirection.
| const PHONE_REGEX = /(?:\+?1[-.\s]?)?(?:\(?\d{3}\)?[-.\s]?)?\d{3}[-.\s]?\d{4}/; | ||
|
|
||
| /** Require explicit name-intro phrase (e.g. "I'm John") to avoid misclassifying quick replies like "Automation". */ | ||
| const NAME_INTRO_REGEX = /^(i'm|my name is|it's|call me|i am|hey,?\s*i'm)\s+/i; |
There was a problem hiding this comment.
Name extraction regex produces false positives from common phrases
Medium Severity
The i am and it's alternatives in NAME_INTRO_REGEX match extremely common English phrases (e.g., "I am interested in SEO", "I am a dentist", "I am good", "It's been great"). These pass all the guard checks (word count ≤ 4, alpha-only, length ≥ 2) and get stored as extracted_name. Because extraction processes messages from the start and the first match sets nameFound = true, a false positive on an early message permanently blocks correct name extraction from a later "I'm John" response — polluting admin data with values like "interested in SEO" or "a dentist".


Note
Medium Risk
Adds new persistence and extraction of user-supplied contact info plus a new admin API surface over
chat_sessions, which increases data-handling and authorization/caching sensitivity despite being scoped to admin tooling.Overview
Adds chat session persistence + lead capture: the chat API now rate-limits requests, requires the LLM to return JSON (
message+ optionalquickReplies), decides when to show an inline email/phone capture form, and upserts full conversation history plus extracted contact fields into a newchat_sessionsSupabase table.Introduces admin chat visibility and metrics: a new
/admin/chatspage andGET /api/admin/chatsendpoint provide searchable/filterable/paginated chat session browsing with transcript/extraction-order detail panels, while the admin dashboard and analytics page add chat counts and chatbot performance metrics based on new tracked events.Tightens admin data fetching behavior by forcing dynamic API routes, adding no-store caching headers, and hardening lead status filtering; also updates admin UI fetches to use
AbortControllerand send cookies/disable caching.Written by Cursor Bugbot for commit 5e28064. This will update automatically on new commits. Configure here.