Skip to content

feat: add insights chat box with groq integration#28422

Draft
PeerRich wants to merge 4 commits intomainfrom
devin/1773486777-insights-chat-box
Draft

feat: add insights chat box with groq integration#28422
PeerRich wants to merge 4 commits intomainfrom
devin/1773486777-insights-chat-box

Conversation

@PeerRich
Copy link
Member

@PeerRich PeerRich commented Mar 14, 2026

What does this PR do?

Adds a fixed-position chat box at the bottom of the /insights page that accepts natural language queries about booking data. Uses Groq (llama-3.3-70b-versatile) to interpret queries, maps them to existing insights data endpoints server-side, and dynamically renders the best chart type (line, bar, pie, area) using recharts. Users can save generated charts to localStorage.

Architecture:

  • API route (apps/web/app/api/insights/chat/route.ts): Receives natural language query → calls Groq to determine endpoint + chart type → fetches data server-side via InsightsBookingBaseService → returns chart config + data
  • Client component (InsightsChatBox.tsx): Pure presentation layer — sends query to API, renders results
  • 14 insights endpoints are available for querying (event trends, KPIs, popular events, member rankings, CSAT, no-shows, bookings by hour, etc.)

⚠️ Key areas for reviewer attention

  1. No Zod validation on Groq LLM JSON outputFixed: Added GroqParsedSchema Zod schema with .safeParse(), plus try-catch around JSON.parse for malformed JSON
  2. Scope hardcoded to "user"Fixed: Chat box now receives scope and selectedTeamId from useInsightsOrgTeams hook via props
  3. weekStart now reads from user preferences (user.weekStart) instead of being hardcoded
  4. No authorization beyond session checkFixed: Added PermissionCheckService.checkPermission for insights.read and team membership verification matching the tRPC pattern
  5. GROQ_API_KEY environment variable required — runtime check only, no startup validation
  6. No tests — this is a new feature with ~1000 lines of new code and zero test coverage
  7. Record<string, unknown>[] is used throughout as the chart data type — loose typing

Suggested human review checklist

  • Verify the authorization logic in route.ts:484-523 correctly mirrors the tRPC userBelongsToTeamProcedure pattern — custom auth in API routes is inherently riskier than reusing middleware
  • Confirm PermissionCheckService.checkPermission API matches what's used in the codebase (params: userId, teamId, permission, fallbackRoles)
  • Check if any edge cases exist where scope from useInsightsOrgTeams could be undefined or mismatched with selectedTeamId
  • Verify localStorage getSavedCharts() handles corrupt/unexpected data gracefully (currently uses try-catch but no schema validation on the parsed array)

Updates since last revision

Addressed Cubic AI review feedback (9 issues with confidence ≥ 9/10 across 3 review rounds):

  • Authorization bypass (P0): Added PermissionCheckService checks for team membership and insights.read permission in the API route POST handler
  • Zod validation (P2): Created GroqParsedSchema with .safeParse() and wrapped JSON.parse in try-catch for malformed LLM output
  • Scope wiring (P1): InsightsChatBox now accepts scope and selectedTeamId props from useInsightsOrgTeams
  • Pie chart colors (P2): Cell fill now uses result.colors[index] with CHART_COLORS fallback
  • i18n localization (P3): Save/Saved buttons, "Saved Charts" heading, and Remove button all use t() with existing i18n keys (save, saved, remove, saved_charts)
  • useCallback deps (P1): Added scope and selectedTeamId to handleSubmit dependency array
  • Error message hygiene (P2): Removed raw Groq output from JSON parse error message to avoid leaking LLM responses into logs

Skipped: localStorage validation (confidence 8/10, below threshold).

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. N/A
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Set GROQ_API_KEY environment variable with a valid Groq API key
  2. Navigate to /insights page (logged in as a user with booking data)
  3. The chat box should appear fixed at the bottom of the page
  4. Try queries like:
    • "Show me booking trends over time" → should render a line chart
    • "Which team members have the most bookings?" → should render a bar chart
    • "What are my KPI stats?" → should render a bar chart with metrics
    • "Show popular event types" → should render a bar chart
  5. Click "Save" on a chart → should persist in localStorage and appear in saved charts section
  6. Click "Remove" on a saved chart → should remove it
  7. Verify the chat box collapses/expands properly
  8. Switch between user/team/org scope in the insights filters and verify the chat queries respect the selected scope

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • My PR is too large (>500 lines or >10 files) and should be split into smaller PRs

Link to Devin Session: https://app.devin.ai/sessions/bf30df83b3c34a5eb14d101f3f92eab9
Requested by: @PeerRich

- Add fixed-position chat box on /insights page
- Use Groq (llama-3.3-70b-versatile) to interpret natural language queries
- Server-side data fetching via InsightsBookingBaseService
- Dynamic chart rendering (line, bar, pie, area) with recharts
- Save/remove charts via localStorage persistence
- Support 14 different insights endpoints
- Add i18n translation keys

Co-Authored-By: peer@cal.com <peer@cal.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

7 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/web/modules/insights/components/chat/InsightsChatChart.tsx">

<violation number="1" location="apps/web/modules/insights/components/chat/InsightsChatChart.tsx:105">
P2: Pie charts ignore the `colors` returned in `ChatChartResult`, so this chart type renders with a different palette than the API requested.</violation>

<violation number="2" location="apps/web/modules/insights/components/chat/InsightsChatChart.tsx:188">
P3: Localize the Save button label with `t(...)` instead of hard-coded English text.</violation>
</file>

<file name="apps/web/modules/insights/views/insights-view.tsx">

<violation number="1" location="apps/web/modules/insights/views/insights-view.tsx:162">
P1: Wire the chat box to the current insights scope before mounting it. Right now org/team views still query `scope: "user"`, so the chat can return data that doesn't match the visible insights filters.</violation>
</file>

<file name="apps/web/modules/insights/components/chat/SavedCharts.tsx">

<violation number="1" location="apps/web/modules/insights/components/chat/SavedCharts.tsx:14">
P2: Validate the parsed localStorage value before treating it as a chart array.</violation>

<violation number="2" location="apps/web/modules/insights/components/chat/SavedCharts.tsx:45">
P3: Localize these new UI labels instead of hard-coding English strings.</violation>
</file>

<file name="apps/web/app/api/insights/chat/route.ts">

<violation number="1" location="apps/web/app/api/insights/chat/route.ts:235">
P2: Unsafe `JSON.parse` on LLM output with no schema validation. If Groq returns malformed JSON, markdown-wrapped JSON, or a valid JSON object missing expected keys, this will either throw a parse error or silently produce `undefined` fields. Use a Zod schema with `.safeParse()` to validate the parsed shape and provide a clear fallback or retry.</violation>

<violation number="2" location="apps/web/app/api/insights/chat/route.ts:440">
P0: Authorization bypass: this route only checks for a valid session but never verifies `insights.read` permission or team membership. All tRPC insights endpoints use `userBelongsToTeamProcedure` (which calls `PermissionCheckService.checkPermission`) — this REST route bypasses that entirely. Any authenticated user can pass an arbitrary `selectedTeamId` and `scope` to read another team's booking data.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

try {
const stored = localStorage.getItem(STORAGE_KEY);
if (!stored) return [];
return JSON.parse(stored) as ChatChartResult[];
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 14, 2026

Choose a reason for hiding this comment

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

P2: Validate the parsed localStorage value before treating it as a chart array.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/modules/insights/components/chat/SavedCharts.tsx, line 14:

<comment>Validate the parsed localStorage value before treating it as a chart array.</comment>

<file context>
@@ -0,0 +1,61 @@
+  try {
+    const stored = localStorage.getItem(STORAGE_KEY);
+    if (!stored) return [];
+    return JSON.parse(stored) as ChatChartResult[];
+  } catch {
+    return [];
</file context>
Fix with Cubic

@github-actions
Copy link
Contributor

github-actions bot commented Mar 14, 2026

Devin AI is addressing Cubic AI's review feedback

New feedback has been sent to the existing Devin session.

View Devin Session


✅ Pushed commit eecbcc1

- Add authorization checks (team membership + insights.read permission)
- Add Zod schema validation for Groq LLM JSON output
- Wire chat box to current insights scope (user/team/org)
- Fix pie chart to use result.colors from API response
- Localize Save/Saved button labels with t()
- Localize Saved Charts heading and Remove button with t()
- Add saved_charts i18n key

Co-Authored-By: peer@cal.com <peer@cal.com>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/web/modules/insights/components/chat/InsightsChatBox.tsx">

<violation number="1" location="apps/web/modules/insights/components/chat/InsightsChatBox.tsx:59">
P1: Include `scope` and `selectedTeamId` in the submit callback dependencies; otherwise requests can be sent with stale filters after the parent props change.</violation>
</file>

<file name="apps/web/app/api/insights/chat/route.ts">

<violation number="1" location="apps/web/app/api/insights/chat/route.ts:244">
P2: `JSON.parse()` can throw a `SyntaxError` before `safeParse` runs, defeating the purpose of using Zod validation for the LLM response. Wrap the parse in a try-catch to produce a clear error when the LLM returns malformed JSON.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 14, 2026

Devin AI is addressing Cubic AI's review feedback

New feedback has been sent to the existing Devin session.

View Devin Session


✅ Pushed commit 58743d5

- Add scope and selectedTeamId to handleSubmit useCallback dependencies
- Wrap JSON.parse in try-catch before Zod safeParse for LLM output

Co-Authored-By: peer@cal.com <peer@cal.com>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/web/app/api/insights/chat/route.ts">

<violation number="1" location="apps/web/app/api/insights/chat/route.ts:248">
P2: Do not include raw Groq output in the parse error message; it gets logged by the route catch block.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 14, 2026

Devin AI is addressing Cubic AI's review feedback

New feedback has been sent to the existing Devin session.

View Devin Session


✅ Pushed commit bf35966

Co-Authored-By: peer@cal.com <peer@cal.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant