1- import { useState } from 'react'
1+ import { useState , useEffect } from 'react'
22import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
33import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
44import { API_URL } from '../config/api'
@@ -29,28 +29,38 @@ export function Playground({ onSignupClick }: PlaygroundProps) {
2929 const [ results , setResults ] = useState < SearchResult [ ] > ( [ ] )
3030 const [ loading , setLoading ] = useState ( false )
3131 const [ searchTime , setSearchTime ] = useState < number | null > ( null )
32- const [ searchCount , setSearchCount ] = useState ( 0 )
32+ const [ remaining , setRemaining ] = useState ( 50 ) // Will be updated from backend
33+ const [ limit , setLimit ] = useState ( 50 ) // Total limit from backend
3334 const [ hasSearched , setHasSearched ] = useState ( false )
35+ const [ rateLimitError , setRateLimitError ] = useState < string | null > ( null )
3436
35- const FREE_SEARCH_LIMIT = 5
37+ // Fetch rate limit status on mount (backend is source of truth)
38+ useEffect ( ( ) => {
39+ fetch ( `${ API_URL } /playground/limits` , {
40+ credentials : 'include' , // Send cookies for session tracking
41+ } )
42+ . then ( res => res . json ( ) )
43+ . then ( data => {
44+ setRemaining ( data . remaining ?? 50 )
45+ setLimit ( data . limit ?? 50 )
46+ } )
47+ . catch ( console . error )
48+ } , [ ] )
3649
3750 const handleSearch = async ( searchQuery ?: string ) => {
3851 const q = searchQuery || query
39- if ( ! q . trim ( ) ) return
40-
41- if ( searchCount >= FREE_SEARCH_LIMIT ) {
42- // Show signup prompt
43- return
44- }
52+ if ( ! q . trim ( ) || loading || remaining <= 0 ) return
4553
4654 setLoading ( true )
4755 setHasSearched ( true )
56+ setRateLimitError ( null )
4857 const startTime = Date . now ( )
4958
5059 try {
5160 const response = await fetch ( `${ API_URL } /playground/search` , {
5261 method : 'POST' ,
5362 headers : { 'Content-Type' : 'application/json' } ,
63+ credentials : 'include' , // Send cookies for session tracking
5464 body : JSON . stringify ( {
5565 query : q ,
5666 demo_repo : selectedRepo ,
@@ -59,18 +69,29 @@ export function Playground({ onSignupClick }: PlaygroundProps) {
5969 } )
6070
6171 const data = await response . json ( )
62- setResults ( data . results || [ ] )
63- setSearchTime ( Date . now ( ) - startTime )
64- setSearchCount ( prev => prev + 1 )
72+
73+ if ( response . ok ) {
74+ setResults ( data . results || [ ] )
75+ setSearchTime ( data . search_time_ms || ( Date . now ( ) - startTime ) )
76+ // Update remaining from backend (source of truth)
77+ if ( typeof data . remaining_searches === 'number' ) {
78+ setRemaining ( data . remaining_searches )
79+ }
80+ if ( typeof data . limit === 'number' ) {
81+ setLimit ( data . limit )
82+ }
83+ } else if ( response . status === 429 ) {
84+ // Rate limit exceeded
85+ setRateLimitError ( data . detail ?. message || 'Daily limit reached. Sign up for unlimited searches!' )
86+ setRemaining ( 0 )
87+ }
6588 } catch ( error ) {
6689 console . error ( 'Search error:' , error )
6790 } finally {
6891 setLoading ( false )
6992 }
7093 }
7194
72- const remainingSearches = FREE_SEARCH_LIMIT - searchCount
73-
7495 return (
7596 < div className = "min-h-screen bg-gradient-to-b from-gray-50 to-white" >
7697 { /* Minimal Nav */ }
@@ -171,9 +192,9 @@ export function Playground({ onSignupClick }: PlaygroundProps) {
171192 ) }
172193
173194 { /* Remaining searches indicator */ }
174- { searchCount > 0 && remainingSearches > 0 && (
195+ { hasSearched && remaining > 0 && remaining < limit && (
175196 < div className = "text-center mt-4 text-sm text-gray-500" >
176- { remainingSearches } free { remainingSearches === 1 ? 'search' : 'searches' } remaining •{ ' ' }
197+ { remaining } free { remaining === 1 ? 'search' : 'searches' } remaining •{ ' ' }
177198 < button onClick = { onSignupClick } className = "text-blue-600 hover:underline" >
178199 Sign up for unlimited
179200 </ button >
@@ -194,11 +215,11 @@ export function Playground({ onSignupClick }: PlaygroundProps) {
194215 ) }
195216
196217 { /* Limit Reached Banner */ }
197- { searchCount >= FREE_SEARCH_LIMIT && (
218+ { ( remaining <= 0 || rateLimitError ) && (
198219 < div className = "bg-gradient-to-r from-blue-600 to-indigo-600 rounded-2xl p-6 mb-6 text-white" >
199- < h3 className = "text-lg font-semibold mb-2" > You've used all free searches </ h3 >
220+ < h3 className = "text-lg font-semibold mb-2" > You've reached today's limit </ h3 >
200221 < p className = "text-blue-100 mb-4" >
201- Sign up to get unlimited searches, index your own repos, and more.
222+ { rateLimitError || ' Sign up to get unlimited searches, index your own repos, and more.' }
202223 </ p >
203224 < button
204225 onClick = { onSignupClick }
0 commit comments