1+ import React , { useState , useEffect } from 'react' ;
2+ import { useQuery , useMutation , useQueryClient } from '@tanstack/react-query' ;
3+ import { base44 } from '@/api/base44Client' ;
4+ import { Card , CardContent , CardHeader , CardTitle , CardDescription } from '@/components/ui/card' ;
5+ import { Button } from '@/components/ui/button' ;
6+ import { Badge } from '@/components/ui/badge' ;
7+ import { Alert , AlertDescription } from '@/components/ui/alert' ;
8+ import {
9+ RefreshCw ,
10+ CheckCircle2 ,
11+ AlertCircle ,
12+ Clock ,
13+ FileText ,
14+ Activity ,
15+ Loader2 ,
16+ Zap
17+ } from 'lucide-react' ;
18+ import { toast } from 'sonner' ;
19+ import { formatDistanceToNow } from 'date-fns' ;
20+
21+ export default function DocsChangeDetector ( ) {
22+ const [ isRebuilding , setIsRebuilding ] = useState ( false ) ;
23+ const queryClient = useQueryClient ( ) ;
24+
25+ // Fetch the latest knowledge base metadata
26+ const { data : metadata , isLoading } = useQuery ( {
27+ queryKey : [ 'docs-metadata' ] ,
28+ queryFn : async ( ) => {
29+ try {
30+ const docs = await base44 . entities . ProjectDocumentation . filter ( { } ) ;
31+ return docs [ 0 ] || null ;
32+ } catch ( err ) {
33+ // Entity might not exist yet
34+ return null ;
35+ }
36+ } ,
37+ refetchInterval : 60000 , // Refetch every minute to detect changes
38+ staleTime : 30000
39+ } ) ;
40+
41+ // Rebuild mutation
42+ const rebuildMutation = useMutation ( {
43+ mutationFn : async ( ) => {
44+ setIsRebuilding ( true ) ;
45+ const response = await base44 . functions . invoke ( 'rebuildDocsKnowledgeBase' , { } ) ;
46+ return response . data ;
47+ } ,
48+ onSuccess : ( data ) => {
49+ queryClient . invalidateQueries ( [ 'docs-metadata' ] ) ;
50+ toast . success (
51+ `Knowledge base updated! Processed ${ data . metadata . document_count } documents.`
52+ ) ;
53+ setIsRebuilding ( false ) ;
54+ } ,
55+ onError : ( error ) => {
56+ toast . error ( 'Failed to rebuild: ' + error . message ) ;
57+ setIsRebuilding ( false ) ;
58+ }
59+ } ) ;
60+
61+ const handleRebuild = ( ) => {
62+ rebuildMutation . mutate ( ) ;
63+ } ;
64+
65+ const timeSinceLastBuild = metadata ?. rebuild_date
66+ ? formatDistanceToNow ( new Date ( metadata . rebuild_date ) , { addSuffix : true } )
67+ : 'Never' ;
68+
69+ const buildStatus = metadata ? 'up-to-date' : 'needs-build' ;
70+
71+ return (
72+ < Card className = "border-blue-200 bg-gradient-to-br from-blue-50 to-cyan-50" >
73+ < CardHeader >
74+ < div className = "flex items-start justify-between" >
75+ < div >
76+ < CardTitle className = "flex items-center gap-2" >
77+ < Activity className = "h-5 w-5 text-blue-600" />
78+ AI Knowledge Base Status
79+ </ CardTitle >
80+ < CardDescription >
81+ Automatic documentation monitoring and refresh system
82+ </ CardDescription >
83+ </ div >
84+ < Badge
85+ variant = { buildStatus === 'up-to-date' ? 'default' : 'secondary' }
86+ className = { buildStatus === 'up-to-date' ? 'bg-green-500' : 'bg-amber-500' }
87+ >
88+ { buildStatus === 'up-to-date' ? (
89+ < >
90+ < CheckCircle2 className = "h-3 w-3 mr-1" />
91+ Up to Date
92+ </ >
93+ ) : (
94+ < >
95+ < AlertCircle className = "h-3 w-3 mr-1" />
96+ Build Needed
97+ </ >
98+ ) }
99+ </ Badge >
100+ </ div >
101+ </ CardHeader >
102+
103+ < CardContent className = "space-y-4" >
104+ { /* Status Grid */ }
105+ < div className = "grid grid-cols-2 md:grid-cols-4 gap-4" >
106+ < div className = "bg-white rounded-lg p-3 border border-blue-100" >
107+ < div className = "flex items-center gap-2 text-xs text-slate-500 mb-1" >
108+ < Clock className = "h-3 w-3" />
109+ Last Build
110+ </ div >
111+ < div className = "text-sm font-semibold text-slate-900" >
112+ { timeSinceLastBuild }
113+ </ div >
114+ </ div >
115+
116+ < div className = "bg-white rounded-lg p-3 border border-blue-100" >
117+ < div className = "flex items-center gap-2 text-xs text-slate-500 mb-1" >
118+ < FileText className = "h-3 w-3" />
119+ Documents
120+ </ div >
121+ < div className = "text-sm font-semibold text-slate-900" >
122+ { metadata ?. document_count || 0 }
123+ </ div >
124+ </ div >
125+
126+ < div className = "bg-white rounded-lg p-3 border border-blue-100" >
127+ < div className = "flex items-center gap-2 text-xs text-slate-500 mb-1" >
128+ < Zap className = "h-3 w-3" />
129+ Size
130+ </ div >
131+ < div className = "text-sm font-semibold text-slate-900" >
132+ { metadata ?. total_size_kb || '0' } KB
133+ </ div >
134+ </ div >
135+
136+ < div className = "bg-white rounded-lg p-3 border border-blue-100" >
137+ < div className = "flex items-center gap-2 text-xs text-slate-500 mb-1" >
138+ < Activity className = "h-3 w-3" />
139+ Build Time
140+ </ div >
141+ < div className = "text-sm font-semibold text-slate-900" >
142+ { metadata ?. build_time_ms || '0' } ms
143+ </ div >
144+ </ div >
145+ </ div >
146+
147+ { /* Info Alert */ }
148+ < Alert >
149+ < AlertCircle className = "h-4 w-4" />
150+ < AlertDescription className = "text-sm" >
151+ The AI knowledge base is automatically rebuilt when documentation changes are detected.
152+ You can also manually trigger a rebuild if you've made recent updates.
153+ </ AlertDescription >
154+ </ Alert >
155+
156+ { /* Processed Files */ }
157+ { metadata ?. files_processed && metadata . files_processed . length > 0 && (
158+ < div className = "bg-white rounded-lg p-3 border border-slate-200" >
159+ < h4 className = "text-xs font-semibold text-slate-700 mb-2" >
160+ Included Documents ({ metadata . files_processed . length } )
161+ </ h4 >
162+ < div className = "flex flex-wrap gap-1" >
163+ { metadata . files_processed . map ( ( file , idx ) => (
164+ < Badge key = { idx } variant = "outline" className = "text-xs" >
165+ { file }
166+ </ Badge >
167+ ) ) }
168+ </ div >
169+ </ div >
170+ ) }
171+
172+ { /* Errors */ }
173+ { metadata ?. errors && metadata . errors . length > 0 && (
174+ < Alert variant = "destructive" >
175+ < AlertCircle className = "h-4 w-4" />
176+ < AlertDescription >
177+ < div className = "text-sm font-medium mb-1" >
178+ { metadata . errors . length } error(s) during last build:
179+ </ div >
180+ < ul className = "text-xs space-y-1" >
181+ { metadata . errors . map ( ( err , idx ) => (
182+ < li key = { idx } >
183+ { err . file } : { err . error }
184+ </ li >
185+ ) ) }
186+ </ ul >
187+ </ AlertDescription >
188+ </ Alert >
189+ ) }
190+
191+ { /* Manual Rebuild Button */ }
192+ < div className = "flex gap-3" >
193+ < Button
194+ onClick = { handleRebuild }
195+ disabled = { isRebuilding || rebuildMutation . isPending }
196+ className = "flex-1 bg-gradient-to-r from-blue-600 to-cyan-600 hover:from-blue-700 hover:to-cyan-700"
197+ >
198+ { isRebuilding || rebuildMutation . isPending ? (
199+ < >
200+ < Loader2 className = "h-4 w-4 mr-2 animate-spin" />
201+ Rebuilding...
202+ </ >
203+ ) : (
204+ < >
205+ < RefreshCw className = "h-4 w-4 mr-2" />
206+ Rebuild Knowledge Base
207+ </ >
208+ ) }
209+ </ Button >
210+ </ div >
211+
212+ { /* Last Build Info */ }
213+ { metadata ?. triggered_by && (
214+ < div className = "text-xs text-slate-500 text-center" >
215+ Last rebuilt by { metadata . triggered_by } on{ ' ' }
216+ { new Date ( metadata . rebuild_date ) . toLocaleString ( ) }
217+ </ div >
218+ ) }
219+ </ CardContent >
220+ </ Card >
221+ ) ;
222+ }
0 commit comments