1+ import React , { useState } from 'react' ;
2+ import { useMutation } 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 { Select , SelectContent , SelectItem , SelectTrigger , SelectValue } from '@/components/ui/select' ;
7+ import { Badge } from '@/components/ui/badge' ;
8+ import { Sparkles , FileText , Loader2 , ChevronDown , ChevronUp , Copy , Check } from 'lucide-react' ;
9+ import { toast } from 'sonner' ;
10+ import ReactMarkdown from 'react-markdown' ;
11+
12+ const AVAILABLE_DOCS = [
13+ { value : 'ARCHITECTURE.md' , label : 'Architecture Overview' , category : 'Technical' } ,
14+ { value : 'PRD_MASTER.md' , label : 'Product Requirements' , category : 'Product' } ,
15+ { value : 'API_REFERENCE.md' , label : 'API Reference' , category : 'Technical' } ,
16+ { value : 'SECURITY.md' , label : 'Security Documentation' , category : 'Technical' } ,
17+ { value : 'FEATURE_SPECS.md' , label : 'Feature Specifications' , category : 'Product' } ,
18+ { value : 'ONBOARDING_SPEC.md' , label : 'Onboarding System' , category : 'Product' } ,
19+ { value : 'GAMIFICATION_ADMIN_GUIDE.md' , label : 'Gamification Guide' , category : 'Admin' } ,
20+ { value : 'AI_FEATURES_DOCUMENTATION.md' , label : 'AI Features' , category : 'Technical' } ,
21+ { value : 'DATABASE_SCHEMA_TECHNICAL_SPEC.md' , label : 'Database Schema' , category : 'Technical' } ,
22+ { value : 'DEPLOYMENT_GUIDE.md' , label : 'Deployment Guide' , category : 'Operations' } ,
23+ { value : 'QUICK_START_GUIDE.md' , label : 'Quick Start Guide' , category : 'Getting Started' } ,
24+ { value : 'USER_FLOWS.md' , label : 'User Flows' , category : 'Product' } ,
25+ ] ;
26+
27+ export default function DocSummarizer ( ) {
28+ const [ selectedDoc , setSelectedDoc ] = useState ( '' ) ;
29+ const [ summary , setSummary ] = useState ( null ) ;
30+ const [ fullContent , setFullContent ] = useState ( '' ) ;
31+ const [ showFull , setShowFull ] = useState ( false ) ;
32+ const [ copied , setCopied ] = useState ( false ) ;
33+
34+ const summarizeMutation = useMutation ( {
35+ mutationFn : async ( docName ) => {
36+ // Fetch the documentation file content
37+ const response = await fetch ( `/components/docs/${ docName } ` ) ;
38+ if ( ! response . ok ) {
39+ throw new Error ( 'Failed to fetch documentation' ) ;
40+ }
41+ const content = await response . text ( ) ;
42+ setFullContent ( content ) ;
43+
44+ // Generate AI summary
45+ const aiResponse = await base44 . integrations . Core . InvokeLLM ( {
46+ prompt : `Summarize the following documentation in a concise, structured format. Include:
47+ 1. **Purpose** (1-2 sentences)
48+ 2. **Key Points** (3-5 bullet points)
49+ 3. **Target Audience** (who should read this)
50+ 4. **Quick Takeaways** (2-3 actionable insights)
51+
52+ Keep the summary under 200 words and make it scannable.
53+
54+ Documentation Content:
55+ ${ content } `,
56+ response_json_schema : {
57+ type : 'object' ,
58+ properties : {
59+ purpose : { type : 'string' } ,
60+ key_points : { type : 'array' , items : { type : 'string' } } ,
61+ target_audience : { type : 'string' } ,
62+ quick_takeaways : { type : 'array' , items : { type : 'string' } } ,
63+ estimated_read_time : { type : 'string' }
64+ }
65+ }
66+ } ) ;
67+
68+ return aiResponse ;
69+ } ,
70+ onSuccess : ( data ) => {
71+ setSummary ( data ) ;
72+ toast . success ( 'Summary generated!' ) ;
73+ } ,
74+ onError : ( error ) => {
75+ toast . error ( 'Failed to generate summary: ' + error . message ) ;
76+ }
77+ } ) ;
78+
79+ const handleCopy = ( text ) => {
80+ navigator . clipboard . writeText ( text ) ;
81+ setCopied ( true ) ;
82+ toast . success ( 'Copied to clipboard' ) ;
83+ setTimeout ( ( ) => setCopied ( false ) , 2000 ) ;
84+ } ;
85+
86+ const selectedDocInfo = AVAILABLE_DOCS . find ( doc => doc . value === selectedDoc ) ;
87+
88+ return (
89+ < div className = "space-y-6" >
90+ < Card className = "border-purple-200 bg-gradient-to-br from-purple-50 to-blue-50" >
91+ < CardHeader >
92+ < CardTitle className = "flex items-center gap-2" >
93+ < Sparkles className = "h-6 w-6 text-purple-600" />
94+ AI Documentation Summarizer
95+ </ CardTitle >
96+ < CardDescription >
97+ Get concise, AI-generated overviews of lengthy documentation
98+ </ CardDescription >
99+ </ CardHeader >
100+ < CardContent className = "space-y-4" >
101+ < div className = "flex gap-3" >
102+ < Select value = { selectedDoc } onValueChange = { setSelectedDoc } >
103+ < SelectTrigger className = "flex-1" >
104+ < SelectValue placeholder = "Select a document to summarize..." />
105+ </ SelectTrigger >
106+ < SelectContent >
107+ { [ 'Technical' , 'Product' , 'Admin' , 'Operations' , 'Getting Started' ] . map ( category => (
108+ < React . Fragment key = { category } >
109+ < div className = "px-2 py-1.5 text-xs font-semibold text-slate-500" >
110+ { category }
111+ </ div >
112+ { AVAILABLE_DOCS
113+ . filter ( doc => doc . category === category )
114+ . map ( doc => (
115+ < SelectItem key = { doc . value } value = { doc . value } >
116+ { doc . label }
117+ </ SelectItem >
118+ ) ) }
119+ </ React . Fragment >
120+ ) ) }
121+ </ SelectContent >
122+ </ Select >
123+
124+ < Button
125+ onClick = { ( ) => summarizeMutation . mutate ( selectedDoc ) }
126+ disabled = { ! selectedDoc || summarizeMutation . isPending }
127+ className = "bg-gradient-to-r from-purple-600 to-blue-600 hover:from-purple-700 hover:to-blue-700"
128+ >
129+ { summarizeMutation . isPending ? (
130+ < >
131+ < Loader2 className = "h-4 w-4 mr-2 animate-spin" />
132+ Summarizing...
133+ </ >
134+ ) : (
135+ < >
136+ < Sparkles className = "h-4 w-4 mr-2" />
137+ Summarize
138+ </ >
139+ ) }
140+ </ Button >
141+ </ div >
142+
143+ { selectedDocInfo && (
144+ < div className = "flex gap-2" >
145+ < Badge variant = "outline" > { selectedDocInfo . category } </ Badge >
146+ < Badge variant = "outline" >
147+ < FileText className = "h-3 w-3 mr-1" />
148+ { selectedDocInfo . value }
149+ </ Badge >
150+ </ div >
151+ ) }
152+ </ CardContent >
153+ </ Card >
154+
155+ { summary && (
156+ < Card >
157+ < CardHeader >
158+ < div className = "flex items-start justify-between" >
159+ < div >
160+ < CardTitle className = "text-lg" >
161+ Summary: { selectedDocInfo ?. label }
162+ </ CardTitle >
163+ { summary . estimated_read_time && (
164+ < CardDescription >
165+ Full doc read time: { summary . estimated_read_time }
166+ </ CardDescription >
167+ ) }
168+ </ div >
169+ < Button
170+ variant = "ghost"
171+ size = "sm"
172+ onClick = { ( ) => handleCopy ( JSON . stringify ( summary , null , 2 ) ) }
173+ >
174+ { copied ? (
175+ < Check className = "h-4 w-4 text-green-600" />
176+ ) : (
177+ < Copy className = "h-4 w-4" />
178+ ) }
179+ </ Button >
180+ </ div >
181+ </ CardHeader >
182+ < CardContent className = "space-y-4" >
183+ { /* Purpose */ }
184+ < div >
185+ < h3 className = "text-sm font-semibold text-slate-700 mb-2" > 📋 Purpose</ h3 >
186+ < p className = "text-sm text-slate-600 leading-relaxed" > { summary . purpose } </ p >
187+ </ div >
188+
189+ { /* Key Points */ }
190+ < div >
191+ < h3 className = "text-sm font-semibold text-slate-700 mb-2" > 🔑 Key Points</ h3 >
192+ < ul className = "space-y-1.5" >
193+ { summary . key_points ?. map ( ( point , idx ) => (
194+ < li key = { idx } className = "text-sm text-slate-600 flex items-start gap-2" >
195+ < span className = "text-purple-600 mt-1" > •</ span >
196+ < span > { point } </ span >
197+ </ li >
198+ ) ) }
199+ </ ul >
200+ </ div >
201+
202+ { /* Target Audience */ }
203+ < div >
204+ < h3 className = "text-sm font-semibold text-slate-700 mb-2" > 👥 Target Audience</ h3 >
205+ < p className = "text-sm text-slate-600" > { summary . target_audience } </ p >
206+ </ div >
207+
208+ { /* Quick Takeaways */ }
209+ < div >
210+ < h3 className = "text-sm font-semibold text-slate-700 mb-2" > 💡 Quick Takeaways</ h3 >
211+ < div className = "space-y-2" >
212+ { summary . quick_takeaways ?. map ( ( takeaway , idx ) => (
213+ < div key = { idx } className = "bg-purple-50 border border-purple-200 rounded-lg p-3" >
214+ < p className = "text-sm text-purple-900" > { takeaway } </ p >
215+ </ div >
216+ ) ) }
217+ </ div >
218+ </ div >
219+
220+ { /* Toggle Full Content */ }
221+ { fullContent && (
222+ < div className = "pt-4 border-t" >
223+ < Button
224+ variant = "outline"
225+ onClick = { ( ) => setShowFull ( ! showFull ) }
226+ className = "w-full"
227+ >
228+ { showFull ? (
229+ < >
230+ < ChevronUp className = "h-4 w-4 mr-2" />
231+ Hide Full Documentation
232+ </ >
233+ ) : (
234+ < >
235+ < ChevronDown className = "h-4 w-4 mr-2" />
236+ Show Full Documentation
237+ </ >
238+ ) }
239+ </ Button >
240+
241+ { showFull && (
242+ < div className = "mt-4 p-4 bg-slate-50 rounded-lg border border-slate-200 max-h-[600px] overflow-y-auto" >
243+ < ReactMarkdown className = "prose prose-sm max-w-none" >
244+ { fullContent }
245+ </ ReactMarkdown >
246+ </ div >
247+ ) }
248+ </ div >
249+ ) }
250+ </ CardContent >
251+ </ Card >
252+ ) }
253+
254+ { ! summary && ! summarizeMutation . isPending && (
255+ < Card className = "border-dashed" >
256+ < CardContent className = "py-12 text-center" >
257+ < FileText className = "h-12 w-12 text-slate-300 mx-auto mb-3" />
258+ < p className = "text-sm text-slate-500" >
259+ Select a document and click "Summarize" to generate an AI-powered overview
260+ </ p >
261+ </ CardContent >
262+ </ Card >
263+ ) }
264+ </ div >
265+ ) ;
266+ }
0 commit comments