11'use client' ;
22import { useCompany } from '@/components/interactive/useUser' ;
3+ import { useTrainingData } from '@/components/interactive/useTrainingData' ;
34import { SidebarPage } from '@/components/layout/SidebarPage' ;
45import { Alert , AlertDescription } from '@/components/ui/alert' ;
56import { Button } from '@/components/ui/button' ;
@@ -168,103 +169,58 @@ export const AutoResizeTextarea: React.FC<AutoResizeTextareaProps> = ({ value, o
168169
169170export default function Training ( ) {
170171 const searchParams = useSearchParams ( ) ;
171- const { data : company } = useCompany ( ) ;
172- const [ userPersona , setUserPersona ] = useState < string > ( '' ) ;
173- const [ companyPersona , setCompanyPersona ] = useState < string > ( '' ) ;
172+ const { data : activeCompany } = useCompany ( ) ;
173+
174+ const mode = searchParams . get ( 'mode' ) ;
175+ const isCompanyMode = mode === 'company' ;
176+
177+ // Use optimized training data hook instead of separate state and API calls
178+ const {
179+ data : trainingData ,
180+ error : trainingError ,
181+ isLoading,
182+ updatePersona,
183+ refreshSources,
184+ } = useTrainingData ( {
185+ isCompanyMode,
186+ agentName : getCookie ( 'agixt-agent' ) || process . env . NEXT_PUBLIC_AGIXT_AGENT || DEFAULT_AGENT ,
187+ companyId : activeCompany ?. id ,
188+ } ) ;
189+
174190 const [ uploadingDocument , setUploadingDocument ] = useState ( false ) ;
175191 const [ uploadProgress , setUploadProgress ] = useState ( 0 ) ;
176192 const [ error , setError ] = useState < string | null > ( null ) ;
177193 const [ success , setSuccess ] = useState < string | null > ( null ) ;
178- const [ userExternalSources , setUserExternalSources ] = useState < string [ ] > ( [ ] ) ;
179- const [ companyExternalSources , setCompanyExternalSources ] = useState < string [ ] > ( [ ] ) ;
180- const [ loading , setLoading ] = useState ( true ) ;
181- const { data : activeCompany } = useCompany ( ) ;
182194
183195 // New state for URL learning
184196 const [ learnUrl , setLearnUrl ] = useState < string > ( '' ) ;
185197 const [ isLearningUrl , setIsLearningUrl ] = useState ( false ) ;
186198 const [ urlProgress , setUrlProgress ] = useState ( 0 ) ;
187199
200+ // Local state for editing persona (controlled inputs)
201+ const [ userPersona , setUserPersona ] = useState < string > ( '' ) ;
202+ const [ companyPersona , setCompanyPersona ] = useState < string > ( '' ) ;
203+
188204 const apiKey = getCookie ( 'jwt' ) || '' ;
189205 const apiServer = process . env . NEXT_PUBLIC_AGIXT_SERVER as string ;
190206 const agentName = getCookie ( 'agixt-agent' ) || process . env . NEXT_PUBLIC_AGIXT_AGENT || DEFAULT_AGENT ;
191- useEffect ( ( ) => {
192- if ( activeCompany ?. id || searchParams . get ( 'mode' ) !== 'company' ) {
193- fetchCompanyData ( ) ;
194- }
195- } , [ activeCompany ?. id , searchParams . get ( 'mode' ) !== 'company' ] ) ;
196-
197- const fetchCompanyData = async ( ) => {
198- setLoading ( true ) ;
199- try {
200- const url =
201- searchParams . get ( 'mode' ) === 'company'
202- ? `${ apiServer } /api/agent/${ agentName } /persona/${ activeCompany ?. id } `
203- : `${ apiServer } /api/agent/${ agentName } /persona` ;
204-
205- const personaResponse = await fetch ( url , {
206- headers : { Authorization : apiKey } ,
207- } ) ;
208-
209- if ( personaResponse . ok ) {
210- const personaData = await personaResponse . json ( ) ;
211- if ( searchParams . get ( 'mode' ) === 'company' ) {
212- setCompanyPersona ( personaData . message === 'None' ? '' : personaData . message || '' ) ;
213- } else {
214- setUserPersona ( personaData . message === 'None' ? '' : personaData . message || '' ) ;
215- }
216- }
217-
218- const sourcesUrl =
219- searchParams . get ( 'mode' ) === 'company'
220- ? `${ apiServer } /api/agent/${ agentName } /memory/external_sources/${ COLLECTION_NUMBER } /${ activeCompany ?. id } `
221- : `${ apiServer } /api/agent/${ agentName } /memory/external_sources/${ COLLECTION_NUMBER } ` ;
222-
223- const sourcesResponse = await fetch ( sourcesUrl , {
224- headers : { Authorization : apiKey } ,
225- } ) ;
226207
227- if ( sourcesResponse . ok ) {
228- const sourcesData = await sourcesResponse . json ( ) ;
229- const sources = sourcesData [ 'external_sources' ] || [ ] ;
230- if ( searchParams . get ( 'mode' ) === 'company' ) {
231- setCompanyExternalSources ( Array . isArray ( sources ) ? sources : [ ] ) ;
232- } else {
233- setUserExternalSources ( Array . isArray ( sources ) ? sources : [ ] ) ;
234- }
235- }
236- } catch ( err ) {
237- setError ( 'Failed to fetch training data' ) ;
238- } finally {
239- setLoading ( false ) ;
208+ // Sync training data to local state when loaded
209+ useEffect ( ( ) => {
210+ if ( trainingData ) {
211+ setUserPersona ( trainingData . userPersona ) ;
212+ setCompanyPersona ( trainingData . companyPersona ) ;
240213 }
241- } ;
214+ } , [ trainingData ] ) ;
242215
243216 const handlePersonaUpdate = async ( ) => {
244217 try {
245- const response = await fetch (
246- searchParams . get ( 'mode' ) === 'company'
247- ? `${ apiServer } /api/agent/${ agentName } /persona/${ activeCompany ?. id } `
248- : `${ apiServer } /api/agent/${ agentName } /persona` ,
249- {
250- method : 'PUT' ,
251- headers : {
252- Authorization : apiKey ,
253- 'Content-Type' : 'application/json' ,
254- } ,
255- body : JSON . stringify ( {
256- persona : searchParams . get ( 'mode' ) === 'company' ? companyPersona : userPersona ,
257- company_id : searchParams . get ( 'mode' ) === 'company' ? activeCompany ?. id : null ,
258- // user: searchParams.get('mode') === 'company',
259- } ) ,
260- } ,
261- ) ;
262-
263- if ( ! response . ok ) {
264- throw new Error ( 'Failed to update persona' ) ;
265- }
266- setSuccess ( `Successfully updated ${ searchParams . get ( 'mode' ) === 'company' ? 'company' : 'user' } mandatory context` ) ;
267- await fetchCompanyData ( ) ;
218+ const persona = isCompanyMode ? companyPersona : userPersona ;
219+ await updatePersona ( persona ) ;
220+
221+ // Update hook data optimistically
222+ setSuccess ( `Successfully updated ${ isCompanyMode ? 'company' : 'user' } mandatory context` ) ;
223+ setError ( null ) ;
268224 } catch ( err ) {
269225 setError ( 'Failed to update persona' ) ;
270226 }
@@ -316,7 +272,7 @@ export default function Training() {
316272
317273 // Reset upload state and re-enable interactions
318274 await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) ) ; // Short delay for UX
319- await fetchCompanyData ( ) ; // Refresh sources
275+ await refreshSources ( ) ; // Refresh sources
320276
321277 // Reset upload-related states
322278 setUploadingDocument ( false ) ;
@@ -383,7 +339,7 @@ export default function Training() {
383339
384340 // Short delay for UX
385341 await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) ) ;
386- await fetchCompanyData ( ) ; // Refresh sources
342+ await refreshSources ( ) ; // Refresh sources
387343
388344 // Reset URL learning states
389345 setLearnUrl ( '' ) ; // Clear the URL input after success
@@ -426,7 +382,7 @@ export default function Training() {
426382 await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) ) ;
427383
428384 // Refresh the sources list
429- await fetchCompanyData ( ) ;
385+ await refreshSources ( ) ;
430386 } catch ( error ) {
431387 setError ( 'Failed to delete document' ) ;
432388 }
@@ -439,7 +395,7 @@ export default function Training() {
439395 < CardHeader >
440396 < CardTitle className = 'flex items-center gap-2' >
441397 < Brain className = 'w-5 h-5' />
442- { searchParams . get ( 'mode' ) === 'company' ? ( company ?. name ?? 'Company' ) + ' Agent Training' : 'Agent Training' }
398+ { isCompanyMode ? ( activeCompany ?. name ?? 'Company' ) + ' Agent Training' : 'Agent Training' }
443399 </ CardTitle >
444400 </ CardHeader >
445401 < CardContent className = 'space-y-6' >
@@ -583,13 +539,13 @@ export default function Training() {
583539 { /* Documents List */ }
584540 < div className = 'space-y-4 border-t pt-6' >
585541 < h3 className = 'text-lg font-medium' > Learned Sources</ h3 >
586- { loading ? (
542+ { isLoading ? (
587543 < div className = 'text-center text-muted-foreground' > Loading documents...</ div >
588- ) : ( searchParams . get ( 'mode' ) === 'company' ? companyExternalSources : userExternalSources ) . length === 0 ? (
544+ ) : ! trainingData || ( isCompanyMode ? trainingData . companyExternalSources : trainingData . userExternalSources ) . length === 0 ? (
589545 < div className = 'text-center text-muted-foreground' > No documents uploaded yet</ div >
590546 ) : (
591547 < div className = 'grid gap-2' >
592- { ( searchParams . get ( 'mode' ) === 'company' ? companyExternalSources : userExternalSources ) . map ( ( source ) => (
548+ { ( isCompanyMode ? trainingData . companyExternalSources : trainingData . userExternalSources ) . map ( ( source : string ) => (
593549 < SourceDisplay key = { source } source = { source } onDelete = { handleDeleteDocument } />
594550 ) ) }
595551 </ div >
0 commit comments