Skip to content

Comments

Enhancement/chatbot#140

Merged
jaylong255 merged 8 commits intomainfrom
enhancement/chatbot
Feb 18, 2026
Merged

Enhancement/chatbot#140
jaylong255 merged 8 commits intomainfrom
enhancement/chatbot

Conversation

@jaylong255
Copy link
Member

@jaylong255 jaylong255 commented Feb 17, 2026

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 + optional quickReplies), decides when to show an inline email/phone capture form, and upserts full conversation history plus extracted contact fields into a new chat_sessions Supabase table.

Introduces admin chat visibility and metrics: a new /admin/chats page and GET /api/admin/chats endpoint 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 AbortController and send cookies/disable caching.

Written by Cursor Bugbot for commit 5e28064. This will update automatically on new commits. Configure here.

- 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
@vercel
Copy link
Contributor

vercel bot commented Feb 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
legend Ready Ready Preview, Comment Feb 18, 2026 0:11am

Request Review

…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
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Fix in Cursor Fix in Web

message_count: messages.filter(m => m.role === 'user').length,
last_message_at: new Date().toISOString(),
page: page || null,
status: extracted.email ? 'converted' : 'active',
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web


return (
<tr>
<td colSpan={6} className="p-0">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Fix in Cursor Fix in Web

data: { user },
} = await supabase.auth.getUser();
return user;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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".

Additional Locations (1)

Fix in Cursor Fix in Web

@jaylong255 jaylong255 merged commit f1dbc41 into main Feb 18, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant