ALwrity AI Blog Writer - Added Google Grounding UI Implementation
This commit is contained in:
198
frontend/src/hooks/useBlogWriterState.ts
Normal file
198
frontend/src/hooks/useBlogWriterState.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { BlogOutlineSection, BlogResearchResponse, BlogSEOMetadataResponse, BlogSEOAnalyzeResponse, SourceMappingStats, GroundingInsights, OptimizationResults, ResearchCoverage } from '../services/blogWriterApi';
|
||||
import { researchCache } from '../services/researchCache';
|
||||
|
||||
export const useBlogWriterState = () => {
|
||||
// Core state
|
||||
const [research, setResearch] = useState<BlogResearchResponse | null>(null);
|
||||
const [outline, setOutline] = useState<BlogOutlineSection[]>([]);
|
||||
const [titleOptions, setTitleOptions] = useState<string[]>([]);
|
||||
const [selectedTitle, setSelectedTitle] = useState<string>('');
|
||||
const [sections, setSections] = useState<Record<string, string>>({});
|
||||
const [seoAnalysis, setSeoAnalysis] = useState<BlogSEOAnalyzeResponse | null>(null);
|
||||
const [genMode, setGenMode] = useState<'draft' | 'polished'>('polished');
|
||||
const [seoMetadata, setSeoMetadata] = useState<BlogSEOMetadataResponse | null>(null);
|
||||
const [continuityRefresh, setContinuityRefresh] = useState<number>(0);
|
||||
const [outlineTaskId, setOutlineTaskId] = useState<string | null>(null);
|
||||
|
||||
// Enhanced metadata state
|
||||
const [sourceMappingStats, setSourceMappingStats] = useState<SourceMappingStats | null>(null);
|
||||
const [groundingInsights, setGroundingInsights] = useState<GroundingInsights | null>(null);
|
||||
const [optimizationResults, setOptimizationResults] = useState<OptimizationResults | null>(null);
|
||||
const [researchCoverage, setResearchCoverage] = useState<ResearchCoverage | null>(null);
|
||||
|
||||
// Separate research titles from AI-generated titles
|
||||
const [researchTitles, setResearchTitles] = useState<string[]>([]);
|
||||
const [aiGeneratedTitles, setAiGeneratedTitles] = useState<string[]>([]);
|
||||
|
||||
// Cache recovery - restore most recent research on page load
|
||||
useEffect(() => {
|
||||
const cachedEntries = researchCache.getAllCachedEntries();
|
||||
if (cachedEntries.length > 0) {
|
||||
// Get the most recent cached research
|
||||
const mostRecent = cachedEntries[0];
|
||||
console.log('Restoring cached research from page load:', mostRecent.keywords);
|
||||
setResearch(mostRecent.result);
|
||||
|
||||
// Also try to restore outline if it exists in localStorage
|
||||
try {
|
||||
const savedOutline = localStorage.getItem('blog_outline');
|
||||
const savedTitleOptions = localStorage.getItem('blog_title_options');
|
||||
const savedSelectedTitle = localStorage.getItem('blog_selected_title');
|
||||
|
||||
if (savedOutline) {
|
||||
setOutline(JSON.parse(savedOutline));
|
||||
}
|
||||
if (savedTitleOptions) {
|
||||
setTitleOptions(JSON.parse(savedTitleOptions));
|
||||
}
|
||||
if (savedSelectedTitle) {
|
||||
setSelectedTitle(savedSelectedTitle);
|
||||
}
|
||||
|
||||
console.log('Restored outline and title data from localStorage');
|
||||
} catch (error) {
|
||||
console.error('Error restoring outline data:', error);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Handle research completion
|
||||
const handleResearchComplete = useCallback((researchData: BlogResearchResponse) => {
|
||||
setResearch(researchData);
|
||||
}, []);
|
||||
|
||||
// Handle outline completion with enhanced metadata
|
||||
const handleOutlineComplete = useCallback((result: any) => {
|
||||
if (result?.outline) {
|
||||
setOutline(result.outline);
|
||||
setTitleOptions(result.title_options || []);
|
||||
|
||||
// Store enhanced metadata
|
||||
if (result.source_mapping_stats) {
|
||||
setSourceMappingStats(result.source_mapping_stats);
|
||||
}
|
||||
if (result.grounding_insights) {
|
||||
setGroundingInsights(result.grounding_insights);
|
||||
}
|
||||
if (result.optimization_results) {
|
||||
setOptimizationResults(result.optimization_results);
|
||||
}
|
||||
if (result.research_coverage) {
|
||||
setResearchCoverage(result.research_coverage);
|
||||
}
|
||||
|
||||
// Separate research titles from AI-generated titles
|
||||
if (result.title_options && research) {
|
||||
const researchAngles = research.suggested_angles || [];
|
||||
const researchTitlesList = result.title_options.filter((title: string) =>
|
||||
researchAngles.some((angle: string) => title.toLowerCase().includes(angle.toLowerCase().substring(0, 20)))
|
||||
);
|
||||
const aiTitlesList = result.title_options.filter((title: string) =>
|
||||
!researchTitlesList.includes(title)
|
||||
);
|
||||
|
||||
setResearchTitles(researchTitlesList);
|
||||
setAiGeneratedTitles(aiTitlesList);
|
||||
|
||||
// Auto-select first AI-generated title if available, otherwise first research title
|
||||
if (aiTitlesList.length > 0) {
|
||||
setSelectedTitle(aiTitlesList[0]);
|
||||
} else if (researchTitlesList.length > 0) {
|
||||
setSelectedTitle(researchTitlesList[0]);
|
||||
} else if (result.title_options.length > 0) {
|
||||
setSelectedTitle(result.title_options[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Save to localStorage for persistence
|
||||
try {
|
||||
localStorage.setItem('blog_outline', JSON.stringify(result.outline));
|
||||
localStorage.setItem('blog_title_options', JSON.stringify(result.title_options || []));
|
||||
localStorage.setItem('blog_selected_title', result.title_options?.[0] || '');
|
||||
console.log('Saved outline data to localStorage');
|
||||
} catch (error) {
|
||||
console.error('Error saving outline data:', error);
|
||||
}
|
||||
}
|
||||
setOutlineTaskId(null);
|
||||
}, [research]);
|
||||
|
||||
// Handle outline error
|
||||
const handleOutlineError = useCallback((error: any) => {
|
||||
console.error('Outline generation error:', error);
|
||||
setOutlineTaskId(null);
|
||||
}, []);
|
||||
|
||||
// Handle section generation
|
||||
const handleSectionGenerated = useCallback((sectionId: string, markdown: string) => {
|
||||
setSections(prev => ({ ...prev, [sectionId]: markdown }));
|
||||
}, []);
|
||||
|
||||
// Handle continuity refresh
|
||||
const handleContinuityRefresh = useCallback(() => {
|
||||
setContinuityRefresh(Date.now());
|
||||
}, []);
|
||||
|
||||
// Handle title selection
|
||||
const handleTitleSelect = useCallback((title: string) => {
|
||||
setSelectedTitle(title);
|
||||
localStorage.setItem('blog_selected_title', title);
|
||||
}, []);
|
||||
|
||||
// Handle custom title
|
||||
const handleCustomTitle = useCallback((title: string) => {
|
||||
const newTitleOptions = [...titleOptions, title];
|
||||
setTitleOptions(newTitleOptions);
|
||||
setSelectedTitle(title);
|
||||
localStorage.setItem('blog_title_options', JSON.stringify(newTitleOptions));
|
||||
localStorage.setItem('blog_selected_title', title);
|
||||
}, [titleOptions]);
|
||||
|
||||
return {
|
||||
// State
|
||||
research,
|
||||
outline,
|
||||
titleOptions,
|
||||
selectedTitle,
|
||||
sections,
|
||||
seoAnalysis,
|
||||
genMode,
|
||||
seoMetadata,
|
||||
continuityRefresh,
|
||||
outlineTaskId,
|
||||
sourceMappingStats,
|
||||
groundingInsights,
|
||||
optimizationResults,
|
||||
researchCoverage,
|
||||
researchTitles,
|
||||
aiGeneratedTitles,
|
||||
|
||||
// Setters
|
||||
setResearch,
|
||||
setOutline,
|
||||
setTitleOptions,
|
||||
setSelectedTitle,
|
||||
setSections,
|
||||
setSeoAnalysis,
|
||||
setGenMode,
|
||||
setSeoMetadata,
|
||||
setContinuityRefresh,
|
||||
setOutlineTaskId,
|
||||
setSourceMappingStats,
|
||||
setGroundingInsights,
|
||||
setOptimizationResults,
|
||||
setResearchCoverage,
|
||||
setResearchTitles,
|
||||
setAiGeneratedTitles,
|
||||
|
||||
// Handlers
|
||||
handleResearchComplete,
|
||||
handleOutlineComplete,
|
||||
handleOutlineError,
|
||||
handleSectionGenerated,
|
||||
handleContinuityRefresh,
|
||||
handleTitleSelect,
|
||||
handleCustomTitle
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user