Skip to content

Conversation

@ngoiyaeric
Copy link
Collaborator

@ngoiyaeric ngoiyaeric commented Feb 2, 2026

PR Type

Enhancement


Description

  • Integrate billing popup, usage sidebar, and credit preview toggle

  • Add history toggle provider and sidebar for chat history management

  • Improve type safety in streamable values with proper initialization

  • Refactor profile and usage views with mutual exclusivity logic

  • Update Stripe payment links and add credits preview in history panel


Diagram Walkthrough

flowchart LR
  A["Header Component"] -->|toggleUsage| B["UsageToggleProvider"]
  A -->|toggleHistory| C["HistoryToggleProvider"]
  A -->|open| D["PurchaseCreditsPopup"]
  B -->|provides context| E["UsageView"]
  C -->|provides context| F["HistorySidebar"]
  E -->|closeUsage| B
  F -->|credits preview| G["ChatHistoryClient"]
  H["Layout"] -->|wraps providers| B
  H -->|wraps providers| C
  I["ProfileToggle"] -->|closeUsage on open| B
  J["Chat Component"] -->|conditional render| E
  J -->|conditional render| K["MapProvider"]
Loading

File Walkthrough

Relevant files
Bug fix
2 files
actions.tsx
Improve type safety in streamable value initialization     
+9/-5     
chat-panel.tsx
Fix dependency array in useCallback hook                                 
+1/-1     
Enhancement
13 files
layout.tsx
Add history and usage toggle providers to layout                 
+30/-22 
chat.tsx
Integrate usage view with conditional rendering                   
+5/-2     
conditional-lottie.tsx
Add usage toggle check to visibility logic                             
+3/-1     
header.tsx
Add billing popup and usage toggle functionality                 
+37/-13 
history-sidebar.tsx
Create new history sidebar component with toggle                 
+35/-0   
history-toggle-context.tsx
Create history toggle context provider                                     
+30/-0   
history.tsx
Refactor to use history toggle context                                     
+14/-38 
profile-toggle-context.tsx
Add closeProfileView method to context                                     
+6/-1     
profile-toggle.tsx
Add mutual exclusivity between profile and usage views     
+12/-6   
purchase-credits-popup.tsx
Create billing popup with upgrade features                             
+73/-0   
chat-history-client.tsx
Add credits preview toggle and history fetch optimization
+37/-2   
usage-toggle-context.tsx
Create usage toggle context provider                                         
+30/-0   
usage-view.tsx
Create usage view component with credits display                 
+96/-0   
Configuration changes
1 files
mobile-icons-bar.tsx
Update Stripe payment link to new URL                                       
+1/-1     
Documentation
1 files
CC BY-NC 4.0.docx
Add license document file                                                               
[link]   

Summary by CodeRabbit

Release Notes

  • New Features

    • Added purchase credits upgrade popup for account tier upgrades
    • Added history sidebar to view and access previous conversations
    • Added usage dashboard displaying available credits and daily refresh allocation
  • UI/UX Improvements

    • Integrated usage view with dedicated toggle in header
    • Added history panel access via header button
    • Enhanced layout with streamlined panel state management

CJWTRUST and others added 18 commits January 20, 2026 01:20
Restored the branch to the expected head at commit 488b47c.
This recovers several missing features and architectural improvements:
- Integrated HistoryToggleProvider and HistorySidebar.
- Integrated UsageToggleProvider and the new UsageView component.
- Removed legacy usage-sidebar.tsx.
- Restored pricing and feature updates in PurchaseCreditsPopup.
- Fixed layout structure in app/layout.tsx to support these new global state providers.

Resolved previous merge conflicts and incorrect force-push state.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Reset branch HEAD to historical recovery point 488b47c.
- Applied latest code fixes from orphan commit 166aee9, including Mapbox rendering optimizations and GeoJSON streaming.
- Improved type safety in app/actions.tsx by removing 'as any' casts and properly typing message arrays.
- Refined getModel utility to support vision-aware model fallback.
- Documented non-blocking background task pattern in server actions.
- Updated feature/billing-integration branch with the restored state.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Reset branch HEAD to historical recovery point 488b47c.
- Applied latest code fixes from orphan commit 166aee9.
- Resolved TypeScript build error in app/actions.tsx by casting GeoJSON data to FeatureCollection.
- Improved type safety for AIState message filtering and content handling.
- Implemented background processing for resolution search with immediate UI streaming.
- Optimized chat history fetching to trigger only when the sidebar is open.
- Ensured Mapbox style loading is robust against re-renders.
- Cleaned up dead code and improved documentation in server actions.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Reset branch HEAD to historical recovery point 488b47c.
- Applied latest code fixes from orphan commit 166aee9.
- Resolved TypeScript build error in app/actions.tsx by casting GeoJSON data to FeatureCollection.
- Improved type safety for AIState message filtering and content handling.
- Implemented background processing for resolution search with immediate UI streaming.
- Re-enabled auto-opening of the pricing popup in components/header.tsx.
- Optimized chat history fetching to trigger only when the sidebar is open.
- Ensured Mapbox style loading is robust against re-renders.
- Cleaned up dead code and improved documentation in server actions.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Restored historical state from 488b47c and merged latest changes.
- Implemented streaming for resolution search in lib/agents/resolution-search.tsx and app/actions.tsx to improve response time.
- Re-enabled auto-opening pricing popup in components/header.tsx.
- Resolved all TypeScript build errors in app/actions.tsx.
- Restored Grok model support for vision tasks in lib/utils/index.ts.
- Optimized chat history loading in components/sidebar/chat-history-client.tsx.
- Improved Mapbox style loading robustness in components/map/mapbox-map.tsx.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Synchronized branch with origin/main, prioritizing main's code structure and latest features.
- Bridged the history with historical recovery point 488b47c.
- Implemented streaming for resolution search in lib/agents/resolution-search.tsx and app/actions.tsx to resolve performance issues.
- Restored the auto-opening Pricing Popup and Usage View in the Header component.
- Integrated the Timezone Clock and time context into the restored resolution search logic.
- Resolved TypeScript build errors with proper type casting and fixed a missing 'use client' directive in components/history.tsx.
- Ensured all required providers (History, Usage, etc.) are correctly wrapped in the root layout.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Synchronized branch with origin/main, prioritizing main's code structure and latest features.
- Bridged the history with historical recovery point 488b47c.
- Integrated UsageView and billing UI into the Chat component, toggled by the tent tree icon.
- Implemented streaming for resolution search in lib/agents/resolution-search.tsx and app/actions.tsx for better performance.
- Restored the auto-opening Pricing Popup in the Header component.
- Improved type safety across server actions and Mapbox components.
- Ensured mutual exclusion between Settings, Usage, and Map views.
- Fixed a missing 'use client' directive in components/history.tsx.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Integrated UsageView into the main Chat component for both mobile and desktop.
- Ensured mutual exclusion between Settings, Usage, and Map views in the UI panel.
- Updated ConditionalLottie to hide the loading animation when the Usage View is open.
- Synchronized with origin/main while prioritizing its code structure.
- Maintained historical recovery and performance optimizations for resolution search.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Synchronized with origin/main, prioritizing main branch code and features.
- Fully restored historical context and missing changes from commit 488b47c.
- Integrated Usage and Billing UI (UsageView) into the Chat component.
- Implemented streaming for resolution search analysis to improve perceived performance.
- Re-enabled auto-opening pricing popup in components/header.tsx.
- Refined ConditionalLottie visibility to avoid overlaps with the Usage UI.
- Ensured mutual exclusion between Settings, Usage, and Map views in the side panel.
- Improved type safety across server actions and Mapbox components.
- Resolved build failures related to missing client directives and type mismatches.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
…ability

- Recovered historical state from commit 488b47c and synchronized with main.
- Optimized resolution search by refactoring to stream partial summaries.
- Fixed Mapbox memory leaks by ensuring all event listeners are removed on cleanup.
- Improved StreamableValue stability by initializing with default values.
- Integrated Usage View with mutual exclusion logic and Lottie player visibility fixes.
- Refined model selection for Grok vision and Gemini 1.5 Pro.
- Integrated timezone-aware analysis using tz-lookup.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Fixed Type error in `app/actions.tsx` by casting `content` to `string` in `createStreamableValue`.
- Addressed ESLint warnings in `components/map/mapbox-map.tsx` and `components/chat-panel.tsx` by adding missing dependencies to `useEffect` and `useCallback` hooks.
- Ensured `relatedQueries` streamable value is initialized with an empty state for stability.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
- Optimized resolution search with streaming.
- Improved timezone handling and AI context.
- Fixed UI mutual exclusion and loading state logic.
- Stabilized Mapbox cleanup and event listeners.
- Resolved build errors and ESLint warnings.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
…head features

- Reverted branch to commit 6acfbe5.
- Merged changes from main (e95a25b).
- Preserved Tentree Icon (UsageView logic), History layout (HistoryContainer), and PurchaseCreditsPopup from 6acfbe5 as requested.
- Updated Stripe checkout URL in PurchaseCreditsPopup to the official one (14A3cv7K72TR3go14Nasg02).
- Re-applied bug fixes from 6acfbe5 (type casting in app/actions.tsx, ESLint dependencies in components/map/mapbox-map.tsx and components/chat-panel.tsx, and relatedQueries initialization).
- Verified build and synchronization with main branch features (e.g., resolution search enhancements).

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
…ranch head features

- Reverted branch to commit 6acfbe5 and merged with main (e95a25b).
- Preserved Tentree Icon (Usage View), history layout, and PurchaseCreditsPopup from 6acfbe5.
- Restored branch-specific UI components: Header, History button, Profile toggle, and Usage integration in Chat.
- Synchronized all other logic with main, including resolution search enhancements.
- Fixed an infinite re-render loop in Mapbox map by optimizing useEffect dependencies (removed cameraState from initialization effect).
- Re-applied critical bug fixes from 6acfbe5:
  - Type casting in app/actions.tsx (getUIStateFromAIState).
  - Streamable value initialization for stability.
  - setSuggestions dependency in ChatPanel.
- Updated Stripe checkout URL in PurchaseCreditsPopup to the official link.
- Verified successful build with 'bun run build'.

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
@charliecreates charliecreates bot requested a review from CharlieHelps February 2, 2026 09:54
@vercel
Copy link
Contributor

vercel bot commented Feb 2, 2026

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

Project Deployment Actions Updated (UTC)
qcx Ready Ready Preview, Comment Feb 2, 2026 10:32am

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 3 committers have signed the CLA.

✅ ngoiyaeric
❌ CJWTRUST
❌ google-labs-jules[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 2, 2026

Walkthrough

This PR introduces a new purchase flow and usage tracking dashboard. It adds context providers for history and usage state management, creates new UI components for purchase credits and usage display, refactors server-side streamable value initialization, and integrates toggle controls throughout the header and layout.

Changes

Cohort / File(s) Summary
State Management Infrastructure
components/history-toggle-context.tsx, components/usage-toggle-context.tsx, components/profile-toggle-context.tsx
Introduced new context providers for history and usage toggles with corresponding custom hooks. Extended profile toggle context with closeProfileView function to programmatically dismiss profile views.
Streamable Initialization
app/actions.tsx
Updated UI state streamable values to initialize with actual content (assistant messages, related queries, tool state, search results) instead of empty defaults, improving initial state handling.
Layout & Provider Hierarchy
app/layout.tsx
Wrapped existing provider tree with new HistoryToggleProvider and UsageToggleProvider; added HistorySidebar component alongside existing layout elements within MapLoadingProvider.
New Purchase & Usage Components
components/purchase-credits-popup.tsx, components/usage-view.tsx, components/history-sidebar.tsx
Added new components: PurchaseCreditsPopup (Dialog-based purchase flow), UsageView (usage dashboard with credits display), and HistorySidebar (Sheet-based history panel with lazy-loaded chat history).
Header & Control Integration
components/header.tsx, components/history.tsx, components/profile-toggle.tsx
Replaced external Stripe link with local usage toggle button; refactored history panel into context-driven toggle; integrated usage closure logic before profile view switches. Added PurchaseCreditsPopup management with lifecycle hooks.
Chat & Rendering Updates
components/chat.tsx, components/conditional-lottie.tsx, components/chat-panel.tsx
Integrated usage toggle state into conditional rendering; prioritized SettingsView over UsageView over MapProvider. Updated dependency arrays and visibility logic to account for usage state. Minor callback dependency adjustment.
History Client Enhancement
components/sidebar/chat-history-client.tsx
Implemented lazy chat history fetching based on isHistoryOpen state; added collapsible Credits Preview section with UI indicators and toggle visibility control.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Header
    participant UsageToggle as UsageToggle<br/>Context
    participant ProfileToggle as ProfileToggle<br/>Context
    participant Chat as Chat<br/>Component
    participant UsageView
    participant PurchasePopup as PurchaseCredits<br/>Popup

    User->>Header: Click Usage Button
    activate Header
    Header->>UsageToggle: handleUsageToggle()
    activate UsageToggle
    UsageToggle->>ProfileToggle: closeProfileView()
    activate ProfileToggle
    ProfileToggle->>ProfileToggle: Set activeView = null
    deactivate ProfileToggle
    UsageToggle->>UsageToggle: toggleUsage()
    UsageToggle->>Chat: isUsageOpen = true
    deactivate UsageToggle
    deactivate Header

    activate Chat
    Chat->>Chat: Render UsageView<br/>(instead of MapProvider)
    deactivate Chat

    activate UsageView
    UsageView->>User: Display usage dashboard
    User->>UsageView: Click Upgrade
    deactivate UsageView

    activate Header
    Header->>PurchasePopup: Open on mount
    activate PurchasePopup
    PurchasePopup->>User: Show purchase dialog
    User->>PurchasePopup: Click "Pay Now"
    PurchasePopup->>PurchasePopup: Open Stripe URL
    PurchasePopup->>Header: onClose()
    deactivate PurchasePopup
    deactivate Header
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly related PRs

Poem

🐰 With toggles that toggle and history's delight,
A purchase flow springs into sight!
Usage tracking gleams, credits shine so bright,
The sidebar hops in—everything's right! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title is vague, non-descriptive, and appears to be an internal ID or hash rather than a meaningful summary of the changes. Replace with a clear, descriptive title such as 'Add billing/purchase credits flow with usage sidebar and history management' that summarizes the main changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jules-8488824498232079115-26d4e4cd

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b5ed27 and f45f687.

📒 Files selected for processing (1)
  • components/chat-panel.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: ngoiyaeric
Repo: QueueLab/QCX PR: 0
File: :0-0
Timestamp: 2026-01-20T10:04:04.963Z
Learning: The application has a comprehensive credits and payment UI system with: (1) PurchaseCreditsPopup that shows after user authentication with a 7-day cooldown, (2) History panel on the left side containing CreditsDisplay at the top and ChatHistoryClient below, (3) TentTree icon in header that opens UsageSidebar on the right side showing usage history and billing options. All these components are coordinated through RootLayout and only render for authenticated users.
🧬 Code graph analysis (1)
components/chat-panel.tsx (3)
lib/actions/suggest.ts (1)
  • getSuggestions (9-46)
components/chat.tsx (2)
  • setSuggestions (204-204)
  • setSuggestions (149-149)
components/suggestions-dropdown.tsx (3)
  • document (76-78)
  • suggestionItems (31-61)
  • suggestionItems (64-79)
🔇 Additional comments (1)
components/chat-panel.tsx (1)

133-157: Dependency update looks correct.

Including setSuggestions in the dependency array avoids stale closures and matches React hook best practices.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Feb 2, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Reverse tabnabbing

Description: window.open(..., "_blank") is used without noopener,noreferrer (and without setting
newWindow.opener = null), which can enable reverse-tabnabbing where the newly opened page
can navigate the originating tab via window.opener.
purchase-credits-popup.tsx [20-24]

Referred Code
export function PurchaseCreditsPopup({ isOpen, onClose }: PurchaseCreditsPopupProps) {
  const handlePurchase = () => {
    window.open('https://buy.stripe.com/3cIaEX3tRcur9EM7tbasg00', '_blank');
    onClose();
  };
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Unguarded JSON parse: New code calls JSON.parse(content as string) without a local try/catch, so malformed or
unexpected content can throw and break UI state generation rather than degrading
gracefully.

Referred Code
const relatedQueries = createStreamableValue<RelatedQueries>({
  items: []
})
relatedQueries.done(JSON.parse(content as string))
return {

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
User-facing raw errors: New code surfaces backend-provided error strings directly to end users via
toast.error(...) and rendering {error}, which can leak internal details depending on API
responses.

Referred Code
      } else {
        setError('An unknown error occurred.');
        toast.error('Error fetching chats: An unknown error occurred.');
      }
    } finally {
      setIsLoading(false);
    }
  }

  if (isHistoryOpen) {
    fetchChats();
  }
}, [isHistoryOpen]);

const handleClearHistory = async () => {
  startClearTransition(async () => {
    try {
      // We need a new API endpoint for clearing history
      // Example: DELETE /api/chats (or POST /api/clear-history)
      // This endpoint will call clearHistory(userId) from chat-db.ts
      const response = await fetch('/api/chats/all', { // Placeholder for the actual clear endpoint


 ... (clipped 40 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated JSON content: New code parses unvalidated content with JSON.parse(content as string) and then propagates
the result into UI streamables, so it requires verification that content is always
trusted/validated and cannot be attacker-controlled.

Referred Code
const relatedQueries = createStreamableValue<RelatedQueries>({
  items: []
})
relatedQueries.done(JSON.parse(content as string))
return {

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Feb 2, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Fix dialog open/close handler

Correct the PurchaseCreditsPopup component's props to properly handle the
dialog's state. Rename the onClose prop to onOpenChange and update its type to
(open: boolean) => void to align with the underlying Dialog component's API.

components/purchase-credits-popup.tsx [15-36]

 interface PurchaseCreditsPopupProps {
   isOpen: boolean;
-  onClose: () => void;
+  onOpenChange: (open: boolean) => void;
 }
 ...
-<Dialog open={isOpen} onOpenChange={onClose}>
+<Dialog open={isOpen} onOpenChange={onOpenChange}>

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a bug where the dialog would not function as expected due to the incorrect use of the onOpenChange handler. Applying this fix is crucial for the PurchaseCreditsPopup component to work correctly.

Medium
Conditionally display the purchase popup

Conditionally display the PurchaseCreditsPopup only when the user's credits are
depleted. This involves fetching the user's credit status and updating the
isPurchaseOpen state accordingly, rather than showing it on every app start.

components/header.tsx [39-42]

 useEffect(() => {
-  // Open payment popup as soon as application opens
-  setIsPurchaseOpen(true)
+  // Example of how to conditionally open the popup.
+  // You would need to implement the logic to fetch and check credits.
+  const checkCreditsAndShowPopup = async () => {
+    // const userCredits = await fetchUserCredits(); // Fetch user credits
+    // if (userCredits <= 0) {
+    //   setIsPurchaseOpen(true);
+    // }
+  };
+
+  checkCreditsAndShowPopup();
 }, [])
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that showing a "credit limit reached" popup on every application start is poor UX and logically flawed. Implementing a conditional check improves the application's logic and user experience.

Medium
Avoid re-fetching history on every open

Optimize chat history fetching by removing isHistoryOpen from the useEffect
dependency array. This prevents re-fetching the history every time the panel is
opened, reducing unnecessary network requests.

components/sidebar/chat-history-client.tsx [40-69]

 useEffect(() => {
   async function fetchChats() {
     setIsLoading(true);
     setError(null);
     try {
       // API route /api/chats uses getCurrentUserId internally
       const response = await fetch('/api/chats?limit=50&offset=0'); // Example limit/offset
       if (!response.ok) {
         const errorData = await response.json();
         throw new Error(errorData.error || `Failed to fetch chats: ${response.statusText}`);
       }
       const data: { chats: DrizzleChat[], nextOffset: number | null } = await response.json();
       setChats(data.chats);
     } catch (err) {
       if (err instanceof Error) {
         setError(err.message);
         toast.error(`Error fetching chats: ${err.message}`);
       } else {
         setError('An unknown error occurred.');
         toast.error('Error fetching chats: An unknown error occurred.');
       }
     } finally {
       setIsLoading(false);
     }
   }
 
-  if (isHistoryOpen) {
-    fetchChats();
-  }
-}, [isHistoryOpen]);
+  fetchChats();
+  // To refetch when a new chat is added, you might need a global state or context
+  // that triggers a refetch.
+}, []); // Removed isHistoryOpen from dependencies
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: This is a valid performance optimization. The PR introduces a change that fetches chat history on every panel open, and this suggestion correctly points out the inefficiency and proposes a better approach to avoid redundant network calls.

Low
Pass parsed object not JSON string

Pass the toolOutput object directly to createStreamableValue and
searchResults.done() instead of stringifying it. This avoids redundant
JSON.stringify calls and provides a typed object to consumers.

app/actions.tsx [740-743]

-const searchResults = createStreamableValue(
-  JSON.stringify(toolOutput)
-)
-searchResults.done(JSON.stringify(toolOutput))
+const searchResults = createStreamableValue(toolOutput)
+searchResults.done(toolOutput)
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out that double-stringifying the toolOutput is redundant. Passing the object directly improves code clarity and avoids unnecessary serialization and deserialization downstream.

Low
  • Update

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/actions.tsx (1)

671-696: ⚠️ Potential issue | 🟡 Minor

Wrap switch case declarations in blocks to prevent scope leakage.

The static analyzer correctly identifies that const answer (line 672) and const relatedQueries (line 685) are declared without block scope. These declarations can be erroneously accessed from other switch clauses if fall-through occurs.

🛡️ Proposed fix to wrap declarations in blocks
         case 'response':
+          {
+            const answer = createStreamableValue(content as string)
+            answer.done(content as string)
             return {
               id,
               component: (
                 <Section title="response">
                   <BotMessage content={answer.value} />
                 </Section>
               )
             }
-            case 'related':
-              const relatedQueries = createStreamableValue<RelatedQueries>({
-                items: []
-              })
+          }
+        case 'related':
+          {
+            const relatedQueries = createStreamableValue<RelatedQueries>({
+              items: []
+            })
             relatedQueries.done(JSON.parse(content as string))
             return {
               id,
               component: (
                 <Section title="Related" separator={true}>
                   <SearchRelated relatedQueries={relatedQueries.value} />
                 </Section>
               )
             }
+          }
components/profile-toggle.tsx (1)

15-35: ⚠️ Potential issue | 🟡 Minor

Redundant conditional branches — both set the same value.

Lines 19-23 set alignValue to "start" in both the if and else branches, making the conditional pointless. Additionally, the comments say "Right align" but "start" typically aligns to the left.

🔧 Proposed fix

If the intent is always "start", simplify:

   const handleResize = () => {
     const mobile = window.innerWidth < 768
     setIsMobile(mobile)
-    if (mobile) {
-      setAlignValue("start") // Right align on mobile too
-    } else {
-      setAlignValue("start") // Right align on desktop
-    }
+    setAlignValue("start")
   }

Or if different alignment was intended, clarify the logic and update comments.

🤖 Fix all issues with AI agents
In `@components/chat.tsx`:
- Line 113: Duplicate ternary rendering of activeView ? <SettingsView /> :
isUsageOpen ? <UsageView /> : <MapProvider /> should be extracted into a small
helper component (e.g., MainContentView) that accepts props activeView and
isUsageOpen (and optional position) and returns SettingsView if activeView,
UsageView if isUsageOpen, otherwise MapProvider; replace the inline ternaries in
both the mobile and desktop layouts with <MainContentView
activeView={activeView} isUsageOpen={isUsageOpen} position={...} /> to remove
duplication and improve readability while keeping existing component names
(SettingsView, UsageView, MapProvider).

In `@components/header.tsx`:
- Around line 81-83: The usage toggle Button(s) (the desktop one rendering
TentTree and the mobile variant rendered later) are missing accessible labels;
update the Button elements that call handleUsageToggle to include a descriptive
title (e.g., title="Open Usage") and an aria-label (e.g., aria-label="Open
Usage") so screen readers and tooltips match other header toggles; locate the
Button components wrapping TentTree (and the corresponding mobile Button at the
later occurrence) and add both attributes.
- Around line 39-42: The current useEffect unconditionally calls
setIsPurchaseOpen(true) causing the PurchaseCreditsPopup to show on every mount;
change it to only open after the user is authenticated and when a 7-day cooldown
has expired: read the authenticated state from your auth context (or a new hook
like useAuth) and check a persisted timestamp (e.g. localStorage key like
purchase_popup_last_shown) before calling setIsPurchaseOpen(true), updating that
timestamp when the popup is shown; consider extracting the logic into a
dedicated hook (e.g. usePurchasePopup) that returns whether to show and
encapsulates the auth check and 7-day cooldown calculation.

In `@components/purchase-credits-popup.tsx`:
- Around line 35-36: The Dialog currently passes onOpenChange={onClose} but
onOpenChange receives a boolean; update the handler to explicitly handle the
boolean state: use the Dialog's onOpenChange prop (e.g., onOpenChange={(open) =>
{ if (!open) onClose(); }}) or replace onClose with a new function (e.g.,
handleDialogOpenChange) that accepts the boolean and invokes onClose only when
open is false; locate the Dialog usage and the isOpen/onClose props in
purchase-credits-popup.tsx to implement this controlled open-state handler.

In `@components/sidebar/chat-history-client.tsx`:
- Around line 137-148: The current credits panel rendered when isCreditsVisible
shows hardcoded values; replace this static markup with the app's CreditsDisplay
component and feed it real state/props (e.g., userCredits, maxCredits,
creditsPercentage) or call the credits API/store selector to obtain those values
before render; locate the conditional using isCreditsVisible and swap the static
div for <CreditsDisplay credits={userCredits} maxCredits={maxCredits}
percent={creditsPercentage} /> (or equivalent prop names used in your codebase),
ensuring you import CreditsDisplay and derive userCredits/creditsPercentage from
your auth/user store or the existing credits-fetching hook.
- Around line 65-69: The effect watching isHistoryOpen always calls fetchChats()
on open, causing redundant network calls; modify the useEffect that depends on
isHistoryOpen so it checks whether chats (the state holding loaded chat list) is
already populated before calling fetchChats(), e.g., only call fetchChats() when
isHistoryOpen is true AND chats is empty (or when a separate forceRefresh flag
is set), or implement a stale-while-revalidate approach by returning cached
chats and triggering a background refresh via a refreshChats/forceRefresh
mechanism tied to a button or pull-to-refresh; update references to
isHistoryOpen, fetchChats, chats, and any refresh flag accordingly.

In `@components/usage-view.tsx`:
- Around line 10-15: The usage data in the usage-view component is hardcoded and
list items are rendered using the array index as a key; update the usage state
to either fetch real data or mark it as a placeholder with a clear TODO (replace
the const [usage] initialization) and add a stable unique id to each usage item
(e.g., include an id property for entries currently in the usage array and for
future API responses). Then change the list rendering to use that unique id (not
key={i}) wherever usage is mapped (referencing the usage state and the map that
currently uses key={i} around lines ~83-89) and leave credits state as-is if
it’s intentional. Ensure new ids are stable across reorders/filters so React
keys remain consistent.
- Line 3: The import statement in components/usage-view.tsx currently imports
useEffect but it is unused; remove useEffect from the import list so the line
reads import React, { useState } from 'react' (or drop the named imports
entirely if useState is also unused), ensuring only used symbols (React,
useState) remain to eliminate the unused import.
- Line 35: The Stripe checkout URL is hardcoded in the Button onClick handler;
replace the literal URL by reading process.env.NEXT_PUBLIC_STRIPE_CHECKOUT_URL
(or use a safe fallback/guard) and update the onClick in the Button component to
call window.open(checkoutUrl, '_blank') where checkoutUrl is the env var, and
add NEXT_PUBLIC_STRIPE_CHECKOUT_URL to your .env files per environment; ensure
the code uses the client-exposed NEXT_PUBLIC_ prefix so the value is available
in the browser.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3984b9b and 3b5ed27.

⛔ Files ignored due to path filters (1)
  • CC BY-NC 4.0.docx is excluded by !**/*.docx
📒 Files selected for processing (16)
  • app/actions.tsx
  • app/layout.tsx
  • components/chat-panel.tsx
  • components/chat.tsx
  • components/conditional-lottie.tsx
  • components/header.tsx
  • components/history-sidebar.tsx
  • components/history-toggle-context.tsx
  • components/history.tsx
  • components/mobile-icons-bar.tsx
  • components/profile-toggle-context.tsx
  • components/profile-toggle.tsx
  • components/purchase-credits-popup.tsx
  • components/sidebar/chat-history-client.tsx
  • components/usage-toggle-context.tsx
  • components/usage-view.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: ngoiyaeric
Repo: QueueLab/QCX PR: 0
File: :0-0
Timestamp: 2026-01-20T10:04:04.963Z
Learning: The application has a comprehensive credits and payment UI system with: (1) PurchaseCreditsPopup that shows after user authentication with a 7-day cooldown, (2) History panel on the left side containing CreditsDisplay at the top and ChatHistoryClient below, (3) TentTree icon in header that opens UsageSidebar on the right side showing usage history and billing options. All these components are coordinated through RootLayout and only render for authenticated users.
📚 Learning: 2026-01-20T10:04:04.963Z
Learnt from: ngoiyaeric
Repo: QueueLab/QCX PR: 0
File: :0-0
Timestamp: 2026-01-20T10:04:04.963Z
Learning: The application has a comprehensive credits and payment UI system with: (1) PurchaseCreditsPopup that shows after user authentication with a 7-day cooldown, (2) History panel on the left side containing CreditsDisplay at the top and ChatHistoryClient below, (3) TentTree icon in header that opens UsageSidebar on the right side showing usage history and billing options. All these components are coordinated through RootLayout and only render for authenticated users.

Applied to files:

  • components/usage-view.tsx
  • components/sidebar/chat-history-client.tsx
  • components/header.tsx
  • components/purchase-credits-popup.tsx
🧬 Code graph analysis (12)
components/usage-view.tsx (1)
components/usage-toggle-context.tsx (1)
  • useUsageToggle (26-30)
components/history.tsx (1)
components/history-toggle-context.tsx (1)
  • useHistoryToggle (26-30)
components/conditional-lottie.tsx (4)
components/map-loading-context.tsx (1)
  • useMapLoading (20-26)
components/profile-toggle-context.tsx (1)
  • useProfileToggle (43-49)
components/usage-toggle-context.tsx (1)
  • useUsageToggle (26-30)
components/ui/lottie-player.tsx (2)
  • isVisible (10-20)
  • LottiePlayerProps (6-8)
components/chat-panel.tsx (2)
lib/actions/suggest.ts (1)
  • getSuggestions (9-46)
components/suggestions-dropdown.tsx (3)
  • suggestionItems (31-61)
  • suggestionItems (64-79)
  • suggestions (26-26)
components/history-sidebar.tsx (4)
components/history-toggle-context.tsx (1)
  • useHistoryToggle (26-30)
components/ui/sheet.tsx (4)
  • Sheet (130-130)
  • SheetContent (135-135)
  • SheetHeader (136-136)
  • SheetTitle (138-138)
components/history-skelton.tsx (1)
  • HistorySkeleton (4-12)
components/sidebar/chat-history-client.tsx (1)
  • ChatHistoryClient (30-197)
components/history-toggle-context.tsx (2)
components/calendar-toggle-context.tsx (3)
  • children (20-35)
  • context (12-18)
  • CalendarToggleContextType (5-8)
components/history-container.tsx (1)
  • div (8-14)
components/chat.tsx (6)
components/usage-toggle-context.tsx (1)
  • useUsageToggle (26-30)
components/usage-view.tsx (1)
  • UsageView (9-96)
components/map/map-provider.tsx (1)
  • MapProvider (16-28)
lib/store/settings.ts (1)
  • MapProvider (3-3)
components/map/map-context.tsx (1)
  • MapProvider (14-22)
components/map/google-map.tsx (2)
  • apiKey (20-29)
  • GoogleMapComponent (12-86)
components/profile-toggle-context.tsx (1)
components/settings/settings-view.tsx (1)
  • activeView (12-16)
app/actions.tsx (2)
lib/agents/inquire.tsx (1)
  • inquire (12-54)
lib/agents/query-suggestor.tsx (1)
  • querySuggestor (8-50)
components/header.tsx (6)
components/calendar-toggle-context.tsx (1)
  • useCalendarToggle (12-18)
components/usage-toggle-context.tsx (1)
  • useUsageToggle (26-30)
components/profile-toggle-context.tsx (1)
  • useProfileToggle (43-49)
components/history-toggle-context.tsx (1)
  • useHistoryToggle (26-30)
components/purchase-credits-popup.tsx (1)
  • PurchaseCreditsPopup (20-73)
components/settings/settings-view.tsx (1)
  • SettingsView (8-35)
components/profile-toggle.tsx (2)
components/profile-toggle-context.tsx (1)
  • useProfileToggle (43-49)
components/usage-toggle-context.tsx (1)
  • useUsageToggle (26-30)
components/purchase-credits-popup.tsx (1)
components/ui/dialog.tsx (6)
  • Dialog (112-112)
  • props (35-53)
  • props (20-29)
  • props (70-81)
  • props (87-96)
  • props (102-108)
🪛 Biome (2.3.13)
app/actions.tsx

[error] 672-672: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 685-687: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (17)
components/mobile-icons-bar.tsx (1)

51-55: LGTM - Stripe URL updated consistently.

The Stripe checkout URL update aligns with the same URL used in PurchaseCreditsPopup and other components in this PR.

components/chat-panel.tsx (1)

133-157: LGTM - Correct dependency array fix.

Adding setSuggestions to the dependency array is the right fix. The debouncedGetSuggestions callback references setSuggestions, so it should be listed as a dependency to ensure the callback always captures the current reference.

components/conditional-lottie.tsx (1)

6-14: LGTM - Usage toggle integration is correct.

The visibility logic now correctly hides the Lottie loading animation when the usage panel is open, consistent with the existing pattern for activeView. This ensures the animation doesn't overlay on the usage panel.

components/purchase-credits-popup.tsx (1)

20-72: Overall implementation looks good.

The component structure, feature list, and Stripe integration are well-implemented. The dialog UI follows the established patterns from the codebase's Dialog primitives.

app/actions.tsx (2)

672-673: Streamable value initialization pattern looks correct.

Initializing with the content value and immediately marking as done() ensures consumers receive the final value synchronously when hydrating from AI state. This is the correct pattern for static/completed content.


740-743: Streamable value initialization for tool results is correct.

Initializing searchResults with JSON.stringify(toolOutput) and marking done with the same value ensures the component receives the data immediately during hydration.

components/history-toggle-context.tsx (1)

1-30: LGTM - Clean context implementation.

The context provider follows established patterns in the codebase (similar to CalendarToggleContext). Exposing both toggleHistory and setHistoryOpen provides flexibility for consumers that need explicit control vs. simple toggling.

components/history.tsx (1)

11-25: LGTM - Clean refactor to context-based toggle.

The component is now a focused trigger button that delegates state management to the context. The Sheet content has been moved to HistorySidebar, maintaining separation of concerns. This aligns well with the coordinated UI system described in the learnings where History panel components work together through shared state.

components/history-sidebar.tsx (2)

15-34: LGTM!

Clean implementation of the history sidebar using Sheet with Suspense boundary. The component correctly integrates with the history toggle context and provides a proper loading state via HistorySkeleton fallback.


12-12: The import path is correct. The file history-skelton.tsx exists in the components directory and matches the import statement, so no build error will occur.

Likely an incorrect or invalid review comment.

app/layout.tsx (1)

75-103: LGTM!

The provider hierarchy is well-structured with toggle providers at outer levels and theme/map contexts closer to the content. HistorySidebar is appropriately placed alongside other global UI elements.

components/profile-toggle-context.tsx (1)

32-37: LGTM!

The closeProfileView addition provides a cleaner API for closing the profile view without requiring knowledge of the current active section. This aligns well with the closeUsage pattern in usage-toggle-context.tsx for consistency across toggle contexts.

components/usage-toggle-context.tsx (1)

1-30: LGTM!

Clean implementation of the usage toggle context following established patterns in the codebase. The API is consistent with other toggle contexts (history, profile) and includes proper error handling for usage outside the provider.

components/chat.tsx (1)

17-19: LGTM!

The usage toggle integration is clean and follows the established pattern for other toggles. The mutual exclusion logic (Settings > Usage > Map) aligns with the PR objectives for UI panel coordination.

Also applies to: 36-36

components/profile-toggle.tsx (1)

37-43: LGTM — Mutual exclusion logic is correctly implemented.

The handleSectionToggle function properly closes the usage panel before opening a profile section when both conditions are met (activeView !== section ensures we're about to open/switch, not close). This integrates well with the coordinated toggle system.

components/header.tsx (2)

31-37: LGTM — Mutual exclusion between usage and profile views.

The handleUsageToggle correctly closes the profile view before opening usage when both would otherwise be visible simultaneously. This complements the inverse logic in profile-toggle.tsx.


55-63: LGTM — History toggle on logo aligns with the UI system.

The logo button now triggers the history panel, which is consistent with the coordinated toggle architecture where the history panel appears on the left side.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

<div className="mobile-layout-container">
<div className="mobile-map-section">
{activeView ? <SettingsView /> : <MapProvider />}
{activeView ? <SettingsView /> : isUsageOpen ? <UsageView /> : <MapProvider />}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider extracting conditional view logic for readability.

The ternary chain activeView ? <SettingsView /> : isUsageOpen ? <UsageView /> : <MapProvider /> is duplicated in both mobile (line 113) and desktop (line 224) layouts. Consider extracting this to a helper component or function to reduce duplication and improve maintainability.

♻️ Suggested extraction
// Add above the Chat component or as a separate file
function MainContentView({ activeView, isUsageOpen, position }: { 
  activeView: ProfileToggleEnum | null; 
  isUsageOpen: boolean;
  position?: { latitude: number; longitude: number; };
}) {
  if (activeView) return <SettingsView />;
  if (isUsageOpen) return <UsageView />;
  return <MapProvider position={position} />;
}

// Then in both layouts:
<MainContentView activeView={activeView} isUsageOpen={isUsageOpen} />

Also applies to: 224-224

🤖 Prompt for AI Agents
In `@components/chat.tsx` at line 113, Duplicate ternary rendering of activeView ?
<SettingsView /> : isUsageOpen ? <UsageView /> : <MapProvider /> should be
extracted into a small helper component (e.g., MainContentView) that accepts
props activeView and isUsageOpen (and optional position) and returns
SettingsView if activeView, UsageView if isUsageOpen, otherwise MapProvider;
replace the inline ternaries in both the mobile and desktop layouts with
<MainContentView activeView={activeView} isUsageOpen={isUsageOpen}
position={...} /> to remove duplication and improve readability while keeping
existing component names (SettingsView, UsageView, MapProvider).

Comment on lines +39 to +42
useEffect(() => {
// Open payment popup as soon as application opens
setIsPurchaseOpen(true)
}, [])
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing authentication check and 7-day cooldown for purchase popup.

The popup opens unconditionally on every component mount, which will annoy users with repeated prompts on each page load. Based on learnings, the PurchaseCreditsPopup should only appear after user authentication and respect a 7-day cooldown period.

🛠️ Suggested approach
  useEffect(() => {
-   // Open payment popup as soon as application opens
-   setIsPurchaseOpen(true)
+   // Only show popup for authenticated users who haven't seen it in 7 days
+   const lastShown = localStorage.getItem('purchasePopupLastShown')
+   const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000)
+   
+   // TODO: Add authentication check (e.g., from session/auth context)
+   const isAuthenticated = true // Replace with actual auth check
+   
+   if (isAuthenticated && (!lastShown || parseInt(lastShown) < sevenDaysAgo)) {
+     setIsPurchaseOpen(true)
+     localStorage.setItem('purchasePopupLastShown', Date.now().toString())
+   }
  }, [])

Consider moving this logic to a dedicated hook or integrating with your auth context.

Based on learnings: "PurchaseCreditsPopup that shows after user authentication with a 7-day cooldown."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
// Open payment popup as soon as application opens
setIsPurchaseOpen(true)
}, [])
useEffect(() => {
// Only show popup for authenticated users who haven't seen it in 7 days
const lastShown = localStorage.getItem('purchasePopupLastShown')
const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000)
// TODO: Add authentication check (e.g., from session/auth context)
const isAuthenticated = true // Replace with actual auth check
if (isAuthenticated && (!lastShown || parseInt(lastShown) < sevenDaysAgo)) {
setIsPurchaseOpen(true)
localStorage.setItem('purchasePopupLastShown', Date.now().toString())
}
}, [])
🤖 Prompt for AI Agents
In `@components/header.tsx` around lines 39 - 42, The current useEffect
unconditionally calls setIsPurchaseOpen(true) causing the PurchaseCreditsPopup
to show on every mount; change it to only open after the user is authenticated
and when a 7-day cooldown has expired: read the authenticated state from your
auth context (or a new hook like useAuth) and check a persisted timestamp (e.g.
localStorage key like purchase_popup_last_shown) before calling
setIsPurchaseOpen(true), updating that timestamp when the popup is shown;
consider extracting the logic into a dedicated hook (e.g. usePurchasePopup) that
returns whether to show and encapsulates the auth check and 7-day cooldown
calculation.

Comment on lines +81 to +83
<Button variant="ghost" size="icon" onClick={handleUsageToggle}>
<TentTree className="h-[1.2rem] w-[1.2rem]" />
</Button>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add accessibility attributes to usage toggle buttons.

Other header buttons include title attributes (e.g., calendar toggle has title="Open Calendar"), but the usage toggle buttons lack them. Add title and consider aria-label for screen reader support.

♿ Proposed fix
-        <Button variant="ghost" size="icon" onClick={handleUsageToggle}>
+        <Button variant="ghost" size="icon" onClick={handleUsageToggle} title="Toggle Usage" aria-label="Toggle usage view">
           <TentTree className="h-[1.2rem] w-[1.2rem]" />
         </Button>

Apply the same fix to the mobile version at line 95.

Also applies to: 95-97

🤖 Prompt for AI Agents
In `@components/header.tsx` around lines 81 - 83, The usage toggle Button(s) (the
desktop one rendering TentTree and the mobile variant rendered later) are
missing accessible labels; update the Button elements that call
handleUsageToggle to include a descriptive title (e.g., title="Open Usage") and
an aria-label (e.g., aria-label="Open Usage") so screen readers and tooltips
match other header toggles; locate the Button components wrapping TentTree (and
the corresponding mobile Button at the later occurrence) and add both
attributes.

Comment on lines +35 to +36
return (
<Dialog open={isOpen} onOpenChange={onClose}>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider using a controlled open state handler.

onOpenChange receives a boolean argument indicating the new open state, but onClose is a void function. While this works when the dialog closes (since onClose() ignores the false argument), it may cause unexpected behavior if the Dialog ever triggers onOpenChange(true).

♻️ Suggested fix for explicit open state handling
-    <Dialog open={isOpen} onOpenChange={onClose}>
+    <Dialog open={isOpen} onOpenChange={(open) => { if (!open) onClose(); }}>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<Dialog open={isOpen} onOpenChange={onClose}>
return (
<Dialog open={isOpen} onOpenChange={(open) => { if (!open) onClose(); }}>
🤖 Prompt for AI Agents
In `@components/purchase-credits-popup.tsx` around lines 35 - 36, The Dialog
currently passes onOpenChange={onClose} but onOpenChange receives a boolean;
update the handler to explicitly handle the boolean state: use the Dialog's
onOpenChange prop (e.g., onOpenChange={(open) => { if (!open) onClose(); }}) or
replace onClose with a new function (e.g., handleDialogOpenChange) that accepts
the boolean and invokes onClose only when open is false; locate the Dialog usage
and the isOpen/onClose props in purchase-credits-popup.tsx to implement this
controlled open-state handler.

Comment on lines +65 to +69

if (isHistoryOpen) {
fetchChats();
}
}, [isHistoryOpen]);
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider caching to avoid redundant fetches on toggle.

The effect re-fetches chats every time isHistoryOpen transitions to true, even if data was already loaded. Consider adding a flag to skip re-fetching when chats is already populated, or use a stale-while-revalidate pattern.

♻️ Proposed optimization
   useEffect(() => {
+    // Skip if already loaded and not stale
+    if (chats.length > 0) return;
+
     if (isHistoryOpen) {
       fetchChats();
     }
-  }, [isHistoryOpen]);
+  }, [isHistoryOpen, chats.length]);

Alternatively, add a dedicated refresh mechanism (e.g., pull-to-refresh or a refresh button) for explicit reloads.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (isHistoryOpen) {
fetchChats();
}
}, [isHistoryOpen]);
// Skip if already loaded and not stale
if (chats.length > 0) return;
if (isHistoryOpen) {
fetchChats();
}
}, [isHistoryOpen, chats.length]);
🤖 Prompt for AI Agents
In `@components/sidebar/chat-history-client.tsx` around lines 65 - 69, The effect
watching isHistoryOpen always calls fetchChats() on open, causing redundant
network calls; modify the useEffect that depends on isHistoryOpen so it checks
whether chats (the state holding loaded chat list) is already populated before
calling fetchChats(), e.g., only call fetchChats() when isHistoryOpen is true
AND chats is empty (or when a separate forceRefresh flag is set), or implement a
stale-while-revalidate approach by returning cached chats and triggering a
background refresh via a refreshChats/forceRefresh mechanism tied to a button or
pull-to-refresh; update references to isHistoryOpen, fetchChats, chats, and any
refresh flag accordingly.

Comment on lines +137 to +148
{isCreditsVisible && (
<div className="mt-2 p-3 rounded-lg bg-muted/50 border border-border/50 space-y-2">
<div className="flex justify-between items-center text-xs">
<span>Available Credits</span>
<span className="font-bold">0</span>
</div>
<div className="w-full bg-secondary h-1.5 rounded-full overflow-hidden">
<div className="bg-yellow-500 h-full w-[0%]" />
</div>
<p className="text-[10px] text-muted-foreground">Upgrade to get more credits</p>
</div>
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Credits Preview displays static placeholder data.

The credits display shows hardcoded values (0 credits, 0% progress bar) rather than actual user data. Per the retrieved learnings, the application has a comprehensive credits UI system with CreditsDisplay component. Consider wiring this to actual user credits state or API data.

Do you want me to help implement the integration with actual credits data, or is this intentionally a placeholder for a future iteration?

🤖 Prompt for AI Agents
In `@components/sidebar/chat-history-client.tsx` around lines 137 - 148, The
current credits panel rendered when isCreditsVisible shows hardcoded values;
replace this static markup with the app's CreditsDisplay component and feed it
real state/props (e.g., userCredits, maxCredits, creditsPercentage) or call the
credits API/store selector to obtain those values before render; locate the
conditional using isCreditsVisible and swap the static div for <CreditsDisplay
credits={userCredits} maxCredits={maxCredits} percent={creditsPercentage} /> (or
equivalent prop names used in your codebase), ensuring you import CreditsDisplay
and derive userCredits/creditsPercentage from your auth/user store or the
existing credits-fetching hook.

@@ -0,0 +1,96 @@
'use client'

import React, { useEffect, useState } from 'react'
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Remove unused import.

useEffect is imported but not used in this component.

🧹 Remove unused import
-import React, { useEffect, useState } from 'react'
+import React, { useState } from 'react'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import React, { useEffect, useState } from 'react'
import React, { useState } from 'react'
🤖 Prompt for AI Agents
In `@components/usage-view.tsx` at line 3, The import statement in
components/usage-view.tsx currently imports useEffect but it is unused; remove
useEffect from the import list so the line reads import React, { useState } from
'react' (or drop the named imports entirely if useState is also unused),
ensuring only used symbols (React, useState) remain to eliminate the unused
import.

Comment on lines +10 to +15
const [usage] = useState([
{ details: 'Efficiently Fix Pull Request ...', date: '2026-01-17 08:05', change: -418 },
{ details: 'Fix Build and Add Parallel S...', date: '2026-01-16 06:10', change: -482 },
{ details: 'How to Add a Feature to a ...', date: '2026-01-14 10:42', change: -300 },
])
const [credits] = useState(0)
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Usage data is static and uses array index as key.

Two observations:

  1. The usage data is hardcoded with sample entries. If this is a placeholder, consider adding a TODO comment or connecting to actual usage API.
  2. Using array index as React key (key={i}) can cause rendering issues if the list is reordered or filtered. Consider using a unique identifier from the data.
♻️ Suggested improvement for key
-{usage.map((item, i) => (
-  <TableRow key={i}>
+{usage.map((item) => (
+  <TableRow key={`${item.date}-${item.details}`}>

Also applies to: 83-89

🤖 Prompt for AI Agents
In `@components/usage-view.tsx` around lines 10 - 15, The usage data in the
usage-view component is hardcoded and list items are rendered using the array
index as a key; update the usage state to either fetch real data or mark it as a
placeholder with a clear TODO (replace the const [usage] initialization) and add
a stable unique id to each usage item (e.g., include an id property for entries
currently in the usage array and for future API responses). Then change the list
rendering to use that unique id (not key={i}) wherever usage is mapped
(referencing the usage state and the map that currently uses key={i} around
lines ~83-89) and leave credits state as-is if it’s intentional. Ensure new ids
are stable across reorders/filters so React keys remain consistent.

<div className="p-4 border rounded-xl space-y-4">
<div className="flex justify-between items-center">
<span className="italic font-medium text-lg">Free</span>
<Button size="sm" className="rounded-full px-4" onClick={() => window.open('https://buy.stripe.com/3cIaEX3tRcur9EM7tbasg00', '_blank')}>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Extract Stripe URL to environment variable.

Hardcoding the Stripe checkout URL directly in the component makes it difficult to manage across environments (dev/staging/prod) and poses a maintenance risk. Consider moving this to an environment variable.

🔧 Suggested approach
-<Button size="sm" className="rounded-full px-4" onClick={() => window.open('https://buy.stripe.com/3cIaEX3tRcur9EM7tbasg00', '_blank')}>
+<Button size="sm" className="rounded-full px-4" onClick={() => window.open(process.env.NEXT_PUBLIC_STRIPE_CHECKOUT_URL, '_blank')}>

Then add to .env:

NEXT_PUBLIC_STRIPE_CHECKOUT_URL=https://buy.stripe.com/3cIaEX3tRcur9EM7tbasg00
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Button size="sm" className="rounded-full px-4" onClick={() => window.open('https://buy.stripe.com/3cIaEX3tRcur9EM7tbasg00', '_blank')}>
<Button size="sm" className="rounded-full px-4" onClick={() => window.open(process.env.NEXT_PUBLIC_STRIPE_CHECKOUT_URL, '_blank')}>
🤖 Prompt for AI Agents
In `@components/usage-view.tsx` at line 35, The Stripe checkout URL is hardcoded
in the Button onClick handler; replace the literal URL by reading
process.env.NEXT_PUBLIC_STRIPE_CHECKOUT_URL (or use a safe fallback/guard) and
update the onClick in the Button component to call window.open(checkoutUrl,
'_blank') where checkoutUrl is the env var, and add
NEXT_PUBLIC_STRIPE_CHECKOUT_URL to your .env files per environment; ensure the
code uses the client-exposed NEXT_PUBLIC_ prefix so the value is available in
the browser.

Copy link

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

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

The biggest correctness issue is in PurchaseCreditsPopup: onOpenChange={onClose} can cause the dialog to close immediately or behave unpredictably. Auto-opening the purchase popup on every app load in components/header.tsx is an intrusive UX and should be gated by real credit state and/or persisted dismissal. Mobile/desktop behavior for the TentTree action is inconsistent (Stripe link vs usage toggle), and ChatHistoryClient would benefit from abort/cancel handling to avoid stale updates when the history sheet closes.

Additional notes (3)
  • Maintainability | components/mobile-icons-bar.tsx:48-54
    The mobile TentTree button still links directly to Stripe, while desktop header now uses TentTree to toggle the Usage panel. This creates inconsistent behavior across breakpoints (and conflicts with the new “UsageView” concept). Users will see different actions for the same icon.

Given the new UsageToggleProvider, the mobile icon should likely call toggleUsage() (or open the same purchase popup) instead of navigating away.

  • Maintainability | components/mobile-icons-bar.tsx:48-54
    The mobile “TentTree” button still links directly to Stripe (<a href=...>) instead of toggling the in-app UsageView, while desktop header now toggles usage. This creates inconsistent behavior across breakpoints (mobile opens Stripe, desktop opens usage panel). If UsageView is the intended destination, mobile should trigger the same toggle.

Separately, there are now two different Stripe URLs in the codebase (here vs PurchaseCreditsPopup / UsageView), which is a maintainability risk and easy to regress.

  • Readability | components/sidebar/chat-history-client.tsx:36-36
    ChatHistoryClient now only fetches when isHistoryOpen is true, but it doesn’t reset state on close. If the user opens history, sees an error, closes, then reopens, the component may flash stale error/loading state depending on timing.

Also, this pattern should guard against state updates after close (fetch in-flight) to avoid showing results for a panel that is no longer open.

Summary of changes

What changed

  • Added global UI state providers in app/layout.tsx:

    • Wrapped the app with new HistoryToggleProvider and UsageToggleProvider.
    • Rendered a new HistorySidebar alongside the existing Sidebar.
  • Introduced History side panel as a global sheet:

    • New components/history-toggle-context.tsx manages open/close state.
    • New components/history-sidebar.tsx renders the Sheet + ChatHistoryClient.
    • Updated components/history.tsx to be a simple toggle button (onClick={toggleHistory}), delegating the actual sheet to HistorySidebar.
  • Integrated Usage UI:

    • New components/usage-toggle-context.tsx for toggling usage view.
    • New components/usage-view.tsx (static mock data) and integrated it into components/chat.tsx (desktop and mobile panel selection).
    • Updated components/conditional-lottie.tsx to hide the loading Lottie when usage is open.
  • Billing / purchase flow updates:

    • New components/purchase-credits-popup.tsx and header now auto-opens it on initial mount.
    • Header tent-tree icon now toggles Usage instead of linking directly to Stripe.
    • Mobile icons bar Stripe link updated to a new checkout URL.
  • Server/UI streaming stability tweaks in app/actions.tsx:

    • Initialized createStreamableValue(...) with default values for several streams (answer, relatedQueries, isCollapsed, searchResults).
  • Chat history fetch behavior change:

    • ChatHistoryClient now fetches only when the history sheet is open and adds a “Credits Preview” collapsible UI section.
  • Profile/usage mutual exclusion:

    • ProfileToggleContext adds closeProfileView().
    • ProfileToggle now closes usage before opening a profile section.
    • Header closes profile before opening usage.

Comment on lines +35 to +38
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
Copy link

Choose a reason for hiding this comment

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

PurchaseCreditsPopup is configured with onOpenChange={onClose} but Dialog’s onOpenChange typically passes the next open boolean. With the current wiring, any open-change (including opening) will call onClose() and can cause the dialog to immediately close or behave inconsistently (depending on the dialog implementation).

Suggestion

Update the handler to accept the boolean and only call onClose() when it transitions to false.

export function PurchaseCreditsPopup({ isOpen, onClose }: PurchaseCreditsPopupProps) {
  return (
    <Dialog
      open={isOpen}
      onOpenChange={(open) => {
        if (!open) onClose()
      }}
    >
      {/* ... */}
    </Dialog>
  )
}

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.

Comment on lines +39 to +43
useEffect(() => {
// Open payment popup as soon as application opens
setIsPurchaseOpen(true)
}, [])

Copy link

Choose a reason for hiding this comment

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

Auto-opening the purchase dialog on every app load (setIsPurchaseOpen(true) in a mount effect) is a high-friction UX and will likely break flows where the user is not actually out of credits. It can also cause repeated popups across navigations in SPA mode (layout persists) and makes automated tests flaky unless they always account for the dialog.

This should be driven by a real condition (e.g., credits === 0, a server flag, or a query param), and ideally persisted so users who click “Later” aren’t repeatedly interrupted.

Suggestion

Gate the auto-open behind a real signal and persist dismissal.

Example (client-only, minimal):

useEffect(() => {
  const dismissed = localStorage.getItem('purchase-popup-dismissed') === '1'
  const shouldOpen = !dismissed /* && credits <= 0 */
  if (shouldOpen) setIsPurchaseOpen(true)
}, [])

const handleClose = () => {
  localStorage.setItem('purchase-popup-dismissed', '1')
  setIsPurchaseOpen(false)
}

<PurchaseCreditsPopup isOpen={isPurchaseOpen} onClose={handleClose} />

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.

Comment on lines +9 to +38
export function UsageView() {
const [usage] = useState([
{ details: 'Efficiently Fix Pull Request ...', date: '2026-01-17 08:05', change: -418 },
{ details: 'Fix Build and Add Parallel S...', date: '2026-01-16 06:10', change: -482 },
{ details: 'How to Add a Feature to a ...', date: '2026-01-14 10:42', change: -300 },
])
const [credits] = useState(0)
const { toggleUsage } = useUsageToggle()

return (
<div className="container py-8 h-full overflow-y-auto">
<div className="flex justify-between items-center mb-8">
<div>
<h1 className="text-3xl font-bold tracking-tight">Usage</h1>
<p className="text-muted-foreground">Track your credits and usage history</p>
</div>
<Button variant="ghost" size="icon" onClick={toggleUsage}>
<Minus className="h-6 w-6" />
<span className="sr-only">Close usage</span>
</Button>
</div>

<div className="space-y-6">
<div className="p-4 border rounded-xl space-y-4">
<div className="flex justify-between items-center">
<span className="italic font-medium text-lg">Free</span>
<Button size="sm" className="rounded-full px-4" onClick={() => window.open('https://buy.stripe.com/3cIaEX3tRcur9EM7tbasg00', '_blank')}>
Upgrade
</Button>
</div>
Copy link

Choose a reason for hiding this comment

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

UsageView is hard-coded with mock usage rows and credits = 0, plus it opens Stripe via window.open inline. Shipping this as-is will mislead users and makes it harder to evolve (no data contract).

If this is intended as a placeholder, it should be clearly isolated (e.g., behind a feature flag) or wired to real data sources (same credits value used for gating the purchase popup / history preview).

Suggestion

Replace mock state with props or a real fetch hook, and centralize the Stripe URL.

Example shape:

type UsageEntry = { details: string; date: string; change: number }

export function UsageView({ credits, usage }: { credits: number; usage: UsageEntry[] }) {
  // render only
}

And move the checkout URL to a single constant (e.g., lib/constants/billing.ts). Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.

Comment on lines +685 to 689
const relatedQueries = createStreamableValue<RelatedQueries>({
items: []
})
relatedQueries.done(JSON.parse(content as string))
return {
Copy link

Choose a reason for hiding this comment

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

getUIStateFromAIState now initializes streamables with defaults, which is good, but relatedQueries.done(JSON.parse(content as string)) still assumes content is valid JSON for the related type. If malformed data arrives, this will throw and break rendering of the whole UI state mapping.

Because this is in a UI-state conversion function, a single bad message should degrade gracefully (e.g., show an error section or skip the related block) rather than crash the whole mapping.

Suggestion

Wrap the JSON parse for related in a try/catch and fall back to an empty { items: [] }.

const relatedQueries = createStreamableValue<RelatedQueries>({ items: [] })
try {
  relatedQueries.done(JSON.parse(content as string))
} catch {
  relatedQueries.done({ items: [] })
}

Reply with "@CharlieHelps yes please" if you’d like me to add a commit with this guard.

@charliecreates charliecreates bot removed the request for review from CharlieHelps February 2, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants