import React, { useState, useEffect } from 'react'; import PersonaEditorModal from './PersonaEditorModal'; import { getUserPersonas, getPlatformPersona, updatePersona, updatePlatformPersona } from '../../../api/persona'; interface PersonaData { id?: number; user_id?: number; persona_name: string; archetype: string; core_belief: string; brand_voice_description: string; linguistic_fingerprint: any; platform_adaptations: any; confidence_score: number; ai_analysis_version: string; platform_type: string; sentence_metrics: any; lexical_features: any; rhetorical_devices: any; tonal_range: any; stylistic_constraints: any; content_format_rules: any; engagement_patterns: any; posting_frequency: any; content_types: any; platform_best_practices: any; algorithm_considerations: any; } interface PersonaChipProps { platform: string; onPersonaUpdate?: (personaData: PersonaData) => void; } const PersonaChip: React.FC = ({ platform, onPersonaUpdate }) => { const [personaData, setPersonaData] = useState(null); const [isLoading, setIsLoading] = useState(false); const [showEditor, setShowEditor] = useState(false); const [error, setError] = useState(null); // Fetch persona data const fetchPersonaData = async () => { setIsLoading(true); setError(null); try { // Fetch core persona list (take most recent active) and platform-specific details using authenticated API client const [coreList, platformData] = await Promise.all([ getUserPersonas(), getPlatformPersona(platform) ]); if (coreList && platformData) { // Extract core persona from the response const corePersona = platformData?.core_persona || {}; const platformPersona = platformData?.platform_persona || {}; const qualityMetrics = platformData?.quality_metrics || {}; if (!corePersona || Object.keys(corePersona).length === 0) { setError('No persona found for this platform'); return; } // Merge core + platform fields for editor convenience setPersonaData({ id: platformData?.id || 1, user_id: 1, // Placeholder, not used persona_name: corePersona.persona_name || 'Untitled Persona', archetype: corePersona.archetype || 'General', core_belief: corePersona.core_belief || '', brand_voice_description: corePersona.brand_voice_description || corePersona.core_belief || '', linguistic_fingerprint: corePersona.linguistic_fingerprint || {}, platform_adaptations: corePersona.platform_adaptations || {}, confidence_score: qualityMetrics.confidence_score || corePersona.confidence_score || 0, ai_analysis_version: platformData?.ai_analysis_version || '1.0', platform_type: platform, sentence_metrics: platformPersona?.sentence_metrics || {}, lexical_features: platformPersona?.lexical_features || {}, rhetorical_devices: platformPersona?.rhetorical_devices || {}, tonal_range: platformPersona?.tonal_range || {}, stylistic_constraints: platformPersona?.stylistic_constraints || {}, content_format_rules: platformPersona?.content_format_rules || {}, engagement_patterns: platformPersona?.engagement_patterns || {}, posting_frequency: platformPersona?.posting_frequency || {}, content_types: platformPersona?.content_types || {}, platform_best_practices: platformPersona?.platform_best_practices || {}, algorithm_considerations: platformPersona?.algorithm_considerations || {}, } as any); } } catch (err) { setError('Failed to load persona data'); console.error('Error fetching persona:', err); } finally { setIsLoading(false); } }; useEffect(() => { fetchPersonaData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [platform]); const handleSavePersona = async (data: PersonaData, saveToDatabase: boolean) => { try { if (saveToDatabase) { // Save core persona simple fields if (data.id) { const corePayload: any = { persona_name: data.persona_name, archetype: data.archetype, core_belief: data.core_belief, brand_voice_description: data.brand_voice_description, linguistic_fingerprint: data.linguistic_fingerprint, platform_adaptations: data.platform_adaptations, }; // Use authenticated API client, note that user ID is extracted from JWT await updatePersona(1, data.id, { core_persona: corePayload }); } // Save platform persona fields const platformPayload: any = { sentence_metrics: data.sentence_metrics, lexical_features: data.lexical_features, rhetorical_devices: data.rhetorical_devices, tonal_range: data.tonal_range, stylistic_constraints: data.stylistic_constraints, content_format_rules: data.content_format_rules, engagement_patterns: data.engagement_patterns, posting_frequency: data.posting_frequency, content_types: data.content_types, platform_best_practices: data.platform_best_practices, algorithm_considerations: data.algorithm_considerations, }; // Use authenticated API client, note that user ID is extracted from JWT await updatePlatformPersona(platform, platformPayload); } // Update local state setPersonaData(data); // Notify parent component if (onPersonaUpdate) { onPersonaUpdate(data); } console.log('Persona updated:', saveToDatabase ? 'saved to database' : 'session only'); } catch (err) { console.error('Error saving persona:', err); setError('Failed to save persona changes'); } }; const getPersonaColor = (confidence?: number) => { if (!confidence) return '#6b7280'; if (confidence >= 0.8) return '#10b981'; if (confidence >= 0.6) return '#f59e0b'; return '#ef4444'; }; const getPersonaIcon = (archetype?: string) => { if (!archetype) return '👤'; const archetypeIcons: Record = { 'pragmatic futurist': '🔮', 'thoughtful educator': '📚', 'innovative leader': '🚀', 'analytical expert': '🔍', 'creative storyteller': '✨', 'strategic advisor': '🎯', 'authentic connector': '🤝', 'data-driven optimist': '📊' }; const lowerArchetype = archetype.toLowerCase(); for (const [key, icon] of Object.entries(archetypeIcons)) { if (lowerArchetype.includes(key)) { return icon; } } return '👤'; }; if (isLoading) { return (
Loading Persona...
); } if (error || !personaData) { return (
fetchPersonaData()} title="Click to retry loading persona data" >
No Persona
); } const confidence = personaData.confidence_score || 0; const confidenceColor = getPersonaColor(confidence); // Debug: Log the confidence score to see what's being stored console.log('PersonaChip confidence_score:', personaData.confidence_score, 'processed:', confidence); const personaIcon = getPersonaIcon(personaData.archetype); return ( <>
{ e.currentTarget.style.transform = 'translateY(-2px) scale(1.05)'; e.currentTarget.style.boxShadow = `0 4px 16px ${confidenceColor}60`; }} onMouseOut={(e) => { e.currentTarget.style.transform = 'translateY(0) scale(1)'; e.currentTarget.style.boxShadow = `0 2px 8px ${confidenceColor}40`; }} onClick={() => setShowEditor(true)} >
{personaIcon}
{personaData.persona_name || 'Untitled Brand Voice'}
{Math.round(confidence * 100)}%
setShowEditor(false)} personaData={personaData} onSave={(data, saveToDatabase) => handleSavePersona(data, saveToDatabase)} platform={platform} /> ); }; export default PersonaChip;