Advanced Content Hyper-Personalization Implementation
This commit is contained in:
@@ -5,8 +5,9 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { PlatformPersonaProvider, PlatformPersonaChat } from '../PersonaContext';
|
||||
import { PlatformType } from '../../types/PlatformPersonaTypes';
|
||||
import { PlatformPersonaProvider } from '../PersonaContext';
|
||||
import { PlatformPersonaChat } from './PlatformPersonaChat';
|
||||
import { PlatformType } from '../../../types/PlatformPersonaTypes';
|
||||
|
||||
// Example: LinkedIn Writer Integration
|
||||
export const LinkedInWriterWithPersonaChat: React.FC = () => {
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
* Provides intelligent, contextual assistance based on user's writing persona
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { CopilotChat, CopilotChatProps } from '@copilotkit/react-chat';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { CopilotSidebar } from '@copilotkit/react-ui';
|
||||
import { usePlatformPersonaContext } from '../PersonaContext';
|
||||
import { PlatformType, WritingPersona, PlatformAdaptation } from '../../types/PlatformPersonaTypes';
|
||||
import { PlatformType, WritingPersona, PlatformAdaptation } from '../../../types/PlatformPersonaTypes';
|
||||
|
||||
// Platform-specific chat configurations
|
||||
interface PlatformChatConfig {
|
||||
@@ -173,6 +173,7 @@ export const PlatformPersonaChat: React.FC<PlatformPersonaChatProps> = ({
|
||||
customSystemMessage
|
||||
}) => {
|
||||
const { corePersona, platformPersona, loading, error } = usePlatformPersonaContext();
|
||||
const [isChatOpen, setIsChatOpen] = useState(false);
|
||||
|
||||
// Generate platform-specific chat configuration
|
||||
const chatConfig = useMemo(() =>
|
||||
@@ -197,17 +198,6 @@ export const PlatformPersonaChat: React.FC<PlatformPersonaChatProps> = ({
|
||||
return `${systemMessage}\n\nCurrent Context: ${contextString}`;
|
||||
}, [systemMessage]);
|
||||
|
||||
// Custom CopilotChat props
|
||||
const copilotChatProps: CopilotChatProps = {
|
||||
makeSystemMessage,
|
||||
placeholder: chatConfig.placeholder,
|
||||
className: `platform-persona-chat ${className}`,
|
||||
showWelcomeMessage,
|
||||
showSuggestedPrompts,
|
||||
suggestedPrompts: showSuggestedPrompts ? chatConfig.suggestedPrompts : undefined,
|
||||
welcomeMessage: showWelcomeMessage ? chatConfig.welcomeMessage : undefined
|
||||
};
|
||||
|
||||
// Loading state
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -286,8 +276,39 @@ export const PlatformPersonaChat: React.FC<PlatformPersonaChatProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* CopilotKit Chat Component */}
|
||||
<CopilotChat {...copilotChatProps} />
|
||||
{/* Chat Toggle Button */}
|
||||
<div className="text-center">
|
||||
<button
|
||||
onClick={() => setIsChatOpen(!isChatOpen)}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
{isChatOpen ? 'Close Chat' : 'Open AI Assistant'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* CopilotKit Sidebar */}
|
||||
{isChatOpen && (
|
||||
<CopilotSidebar
|
||||
className="alwrity-copilot-sidebar platform-persona-chat"
|
||||
labels={{
|
||||
title: `${platform.charAt(0).toUpperCase() + platform.slice(1)} Content Assistant`,
|
||||
initial: chatConfig.welcomeMessage
|
||||
}}
|
||||
suggestions={chatConfig.suggestedPrompts}
|
||||
makeSystemMessage={makeSystemMessage}
|
||||
observabilityHooks={{
|
||||
onChatExpanded: () => {
|
||||
console.log(`[${platform}] Persona chat opened`);
|
||||
},
|
||||
onMessageSent: (message: any) => {
|
||||
const text = typeof message === 'string' ? message : (message?.content ?? '');
|
||||
if (text) {
|
||||
console.log(`[${platform}] User message:`, { content_length: text.length });
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
/**
|
||||
* Platform Persona Chat Test Component
|
||||
* Demonstrates and tests the PlatformPersonaChat component
|
||||
* Shows how to integrate persona-aware chat into different platforms
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { PlatformPersonaProvider } from '../PersonaContext';
|
||||
import { PlatformPersonaChat } from './index';
|
||||
import { PlatformType } from '../../types/PlatformPersonaTypes';
|
||||
|
||||
// Platform selection component
|
||||
const PlatformSelector: React.FC<{
|
||||
selectedPlatform: PlatformType;
|
||||
onPlatformChange: (platform: PlatformType) => void;
|
||||
}> = ({ selectedPlatform, onPlatformChange }) => {
|
||||
const platforms: { value: PlatformType; label: string; description: string }[] = [
|
||||
{ value: 'linkedin', label: 'LinkedIn', description: 'Professional networking & thought leadership' },
|
||||
{ value: 'facebook', label: 'Facebook', description: 'Community building & social engagement' },
|
||||
{ value: 'instagram', label: 'Instagram', description: 'Visual storytelling & aesthetic content' },
|
||||
{ value: 'twitter', label: 'Twitter', description: 'Concise messaging & viral potential' },
|
||||
{ value: 'blog', label: 'Blog', description: 'Long-form content & SEO optimization' },
|
||||
{ value: 'medium', label: 'Medium', description: 'Storytelling & publication strategy' },
|
||||
{ value: 'substack', label: 'Substack', description: 'Newsletter & subscription focus' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="mb-6 p-4 bg-gray-50 border border-gray-200 rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-3">Select Platform to Test</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{platforms.map((platform) => (
|
||||
<button
|
||||
key={platform.value}
|
||||
onClick={() => onPlatformChange(platform.value)}
|
||||
className={`p-3 text-left rounded-lg border transition-all ${
|
||||
selectedPlatform === platform.value
|
||||
? 'border-blue-500 bg-blue-50 text-blue-900'
|
||||
: 'border-gray-300 bg-white hover:border-gray-400 hover:bg-gray-50'
|
||||
}`}
|
||||
>
|
||||
<div className="font-medium">{platform.label}</div>
|
||||
<div className="text-sm text-gray-600 mt-1">{platform.description}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Chat configuration options
|
||||
const ChatConfigOptions: React.FC<{
|
||||
showWelcomeMessage: boolean;
|
||||
showSuggestedPrompts: boolean;
|
||||
onToggleWelcomeMessage: () => void;
|
||||
onToggleSuggestedPrompts: () => void;
|
||||
}> = ({ showWelcomeMessage, showSuggestedPrompts, onToggleWelcomeMessage, onToggleSuggestedPrompts }) => {
|
||||
return (
|
||||
<div className="mb-4 p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
|
||||
<h4 className="text-sm font-medium text-yellow-900 mb-2">Chat Configuration</h4>
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showWelcomeMessage}
|
||||
onChange={onToggleWelcomeMessage}
|
||||
className="mr-2"
|
||||
/>
|
||||
<span className="text-sm text-yellow-800">Show Welcome Message</span>
|
||||
</label>
|
||||
<label className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={showSuggestedPrompts}
|
||||
onChange={onToggleSuggestedPrompts}
|
||||
className="mr-2"
|
||||
/>
|
||||
<span className="text-sm text-yellow-800">Show Suggested Prompts</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Main test component
|
||||
export const PlatformPersonaChatTest: React.FC = () => {
|
||||
const [selectedPlatform, setSelectedPlatform] = useState<PlatformType>('linkedin');
|
||||
const [showWelcomeMessage, setShowWelcomeMessage] = useState(true);
|
||||
const [showSuggestedPrompts, setShowSuggestedPrompts] = useState(true);
|
||||
|
||||
const toggleWelcomeMessage = () => setShowWelcomeMessage(!showWelcomeMessage);
|
||||
const toggleSuggestedPrompts = () => setShowSuggestedPrompts(!showSuggestedPrompts);
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto p-6">
|
||||
<div className="mb-6">
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">
|
||||
Platform Persona Chat Test
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
Test the persona-aware CopilotKit integration across different platforms
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Platform Selector */}
|
||||
<PlatformSelector
|
||||
selectedPlatform={selectedPlatform}
|
||||
onPlatformChange={setSelectedPlatform}
|
||||
/>
|
||||
|
||||
{/* Chat Configuration */}
|
||||
<ChatConfigOptions
|
||||
showWelcomeMessage={showWelcomeMessage}
|
||||
showSuggestedPrompts={showSuggestedPrompts}
|
||||
onToggleWelcomeMessage={toggleWelcomeMessage}
|
||||
onToggleSuggestedPrompts={toggleSuggestedPrompts}
|
||||
/>
|
||||
|
||||
{/* Persona Chat Component */}
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
<div className="bg-gray-100 px-4 py-2 border-b">
|
||||
<h3 className="font-medium text-gray-900">
|
||||
{selectedPlatform.charAt(0).toUpperCase() + selectedPlatform.slice(1)} Persona Chat
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
AI-powered content assistance with your personal writing style
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<PlatformPersonaProvider platform={selectedPlatform}>
|
||||
<PlatformPersonaChat
|
||||
platform={selectedPlatform}
|
||||
showWelcomeMessage={showWelcomeMessage}
|
||||
showSuggestedPrompts={showSuggestedPrompts}
|
||||
className="p-4"
|
||||
/>
|
||||
</PlatformPersonaProvider>
|
||||
</div>
|
||||
|
||||
{/* Usage Instructions */}
|
||||
<div className="mt-6 p-4 bg-green-50 border border-green-200 rounded-lg">
|
||||
<h4 className="font-medium text-green-900 mb-2">How to Test</h4>
|
||||
<ul className="text-sm text-green-800 space-y-1">
|
||||
<li>• Select different platforms to see platform-specific chat configurations</li>
|
||||
<li>• Toggle welcome message and suggested prompts to test different chat modes</li>
|
||||
<li>• Ask questions about content strategy, writing style, or platform optimization</li>
|
||||
<li>• Notice how the AI adapts responses to your persona and platform constraints</li>
|
||||
<li>• Try the quick action buttons for platform-specific suggestions</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Technical Details */}
|
||||
<div className="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
||||
<h4 className="font-medium text-blue-900 mb-2">Technical Implementation</h4>
|
||||
<div className="text-sm text-blue-800 space-y-2">
|
||||
<p>
|
||||
<strong>Context Injection:</strong> The chat automatically receives your writing persona,
|
||||
linguistic fingerprint, and platform-specific constraints through CopilotKit's context system.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Dynamic System Messages:</strong> System messages are generated dynamically based on
|
||||
your persona data and selected platform, ensuring AI responses match your writing style.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Platform Optimization:</strong> Each platform has specific character limits,
|
||||
engagement patterns, and best practices that are automatically incorporated into AI responses.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlatformPersonaChatTest;
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
export { PlatformPersonaChat } from './PlatformPersonaChat';
|
||||
export { PlatformPersonaChatTest } from './PlatformPersonaChatTest';
|
||||
export { IntegrationExample } from './IntegrationExample';
|
||||
|
||||
// Re-export types for convenience
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/**
|
||||
* Persona Test Component
|
||||
* Simple component to test and demonstrate the PlatformPersonaProvider
|
||||
* This can be used to verify the implementation works correctly
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { PlatformPersonaProvider, usePlatformPersonaContext } from './index';
|
||||
import { PlatformType } from '../../../types/PlatformPersonaTypes';
|
||||
|
||||
// Test component that uses the context
|
||||
const PersonaDisplay: React.FC = () => {
|
||||
const {
|
||||
corePersona,
|
||||
platformPersona,
|
||||
platform,
|
||||
loading,
|
||||
error,
|
||||
refreshPersonas
|
||||
} = usePlatformPersonaContext();
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading persona data...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div>
|
||||
<p>Error: {error}</p>
|
||||
<button onClick={refreshPersonas}>Retry</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4 border rounded-lg">
|
||||
<h3 className="text-lg font-semibold mb-4">Persona Data for {platform}</h3>
|
||||
|
||||
{/* Core Persona Display */}
|
||||
{corePersona && (
|
||||
<div className="mb-4 p-3 bg-blue-50 rounded">
|
||||
<h4 className="font-medium text-blue-900">Core Persona</h4>
|
||||
<p><strong>Name:</strong> {corePersona.persona_name}</p>
|
||||
<p><strong>Archetype:</strong> {corePersona.archetype}</p>
|
||||
<p><strong>Core Belief:</strong> {corePersona.core_belief}</p>
|
||||
<p><strong>Confidence:</strong> {corePersona.confidence_score}%</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Platform Persona Display */}
|
||||
{platformPersona && (
|
||||
<div className="mb-4 p-3 bg-green-50 rounded">
|
||||
<h4 className="font-medium text-green-900">Platform Optimization</h4>
|
||||
<p><strong>Platform:</strong> {platformPersona.platform_type}</p>
|
||||
<p><strong>Character Limit:</strong> {platformPersona.content_format_rules?.character_limit || 'N/A'}</p>
|
||||
<p><strong>Optimal Length:</strong> {platformPersona.content_format_rules?.optimal_length || 'N/A'}</p>
|
||||
<p><strong>Posting Frequency:</strong> {platformPersona.engagement_patterns?.posting_frequency || 'N/A'}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Linguistic Fingerprint Display */}
|
||||
{corePersona?.linguistic_fingerprint && (
|
||||
<div className="mb-4 p-3 bg-purple-50 rounded">
|
||||
<h4 className="font-medium text-purple-900">Linguistic Fingerprint</h4>
|
||||
<p><strong>Avg Sentence Length:</strong> {corePersona.linguistic_fingerprint.sentence_metrics.average_sentence_length_words} words</p>
|
||||
<p><strong>Voice Ratio:</strong> {corePersona.linguistic_fingerprint.sentence_metrics.active_to_passive_ratio}</p>
|
||||
<p><strong>Go-to Words:</strong> {corePersona.linguistic_fingerprint.lexical_features.go_to_words?.join(', ') || 'N/A'}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Refresh Button */}
|
||||
<button
|
||||
onClick={refreshPersonas}
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
||||
>
|
||||
Refresh Personas
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Main test component with provider
|
||||
interface PersonaTestComponentProps {
|
||||
platform: PlatformType;
|
||||
userId?: number;
|
||||
}
|
||||
|
||||
export const PersonaTestComponent: React.FC<PersonaTestComponentProps> = ({
|
||||
platform,
|
||||
userId = 1
|
||||
}) => {
|
||||
return (
|
||||
<PlatformPersonaProvider platform={platform} userId={userId}>
|
||||
<PersonaDisplay />
|
||||
</PlatformPersonaProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default PersonaTestComponent;
|
||||
@@ -4,14 +4,13 @@
|
||||
* Integrates with existing persona API client and injects data into CopilotKit
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||
import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback, useRef } from 'react';
|
||||
import { useCopilotReadable } from '@copilotkit/react-core';
|
||||
import {
|
||||
WritingPersona,
|
||||
PlatformAdaptation,
|
||||
PlatformType,
|
||||
UserPersonasResponse,
|
||||
PlatformPersonaResponse
|
||||
UserPersonasResponse
|
||||
} from '../../../types/PlatformPersonaTypes';
|
||||
import {
|
||||
getUserPersonas,
|
||||
@@ -50,8 +49,36 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Add request throttling
|
||||
const lastRequestTime = useRef<number>(0);
|
||||
const requestInProgress = useRef<boolean>(false);
|
||||
const dataCacheTime = useRef<number>(0);
|
||||
|
||||
// Cache duration: 5 minutes
|
||||
const CACHE_DURATION = 5 * 60 * 1000;
|
||||
|
||||
// Fetch persona data function
|
||||
const fetchPersonas = async () => {
|
||||
const fetchPersonas = useCallback(async () => {
|
||||
const now = Date.now();
|
||||
|
||||
// Prevent multiple simultaneous requests
|
||||
if (requestInProgress.current) {
|
||||
console.log('🔄 Request already in progress, skipping...');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check cache validity
|
||||
if (corePersona && platformPersona && (now - dataCacheTime.current) < CACHE_DURATION) {
|
||||
console.log('✅ Using cached persona data');
|
||||
return;
|
||||
}
|
||||
|
||||
// Rate limiting: minimum 2 seconds between requests
|
||||
if (now - lastRequestTime.current < 2000) {
|
||||
console.log('⏱️ Rate limit: waiting before next request...');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
@@ -65,12 +92,58 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
|
||||
// Handle core persona data
|
||||
if (userPersonasResponse.personas && userPersonasResponse.personas.length > 0) {
|
||||
const primaryPersona = userPersonasResponse.personas[0];
|
||||
setCorePersona(primaryPersona);
|
||||
|
||||
// Convert API response to WritingPersona format
|
||||
const convertedPersona: WritingPersona = {
|
||||
id: primaryPersona.persona_id,
|
||||
user_id: userId,
|
||||
persona_name: primaryPersona.persona_name,
|
||||
archetype: primaryPersona.archetype,
|
||||
core_belief: primaryPersona.core_belief,
|
||||
brand_voice_description: primaryPersona.core_belief, // Use core_belief as fallback
|
||||
linguistic_fingerprint: {
|
||||
sentence_metrics: {
|
||||
average_sentence_length_words: 15,
|
||||
preferred_sentence_type: "compound",
|
||||
active_to_passive_ratio: "80:20",
|
||||
sentence_complexity: "moderate",
|
||||
paragraph_structure: "standard"
|
||||
},
|
||||
lexical_features: {
|
||||
go_to_words: ["leverage", "optimize", "strategic"],
|
||||
go_to_phrases: ["Let's explore", "Here's the thing"],
|
||||
avoid_words: ["utilize", "synergize"],
|
||||
contractions: "moderate",
|
||||
vocabulary_level: "professional",
|
||||
industry_terminology: [],
|
||||
emotional_tone_words: []
|
||||
},
|
||||
rhetorical_devices: {
|
||||
metaphors: "tech_mechanics",
|
||||
analogies: "everyday_to_tech",
|
||||
rhetorical_questions: "occasional",
|
||||
storytelling_approach: "case_study",
|
||||
persuasion_techniques: ["logic", "credibility"]
|
||||
}
|
||||
},
|
||||
platform_adaptations: [],
|
||||
onboarding_session_id: 1,
|
||||
source_website_analysis: {},
|
||||
source_research_preferences: {},
|
||||
ai_analysis_version: "1.0",
|
||||
confidence_score: primaryPersona.confidence_score,
|
||||
analysis_date: primaryPersona.created_at,
|
||||
created_at: primaryPersona.created_at,
|
||||
updated_at: primaryPersona.created_at,
|
||||
is_active: true
|
||||
};
|
||||
|
||||
setCorePersona(convertedPersona);
|
||||
|
||||
console.log('✅ Core persona loaded:', {
|
||||
name: primaryPersona.persona_name,
|
||||
archetype: primaryPersona.archetype,
|
||||
confidence: primaryPersona.confidence_score
|
||||
name: convertedPersona.persona_name,
|
||||
archetype: convertedPersona.archetype,
|
||||
confidence: convertedPersona.confidence_score
|
||||
});
|
||||
} else {
|
||||
console.warn('⚠️ No core personas found for user');
|
||||
@@ -79,12 +152,87 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
|
||||
|
||||
// Handle platform-specific persona data
|
||||
if (platformPersonaResponse) {
|
||||
setPlatformPersona(platformPersonaResponse);
|
||||
// Convert API response to PlatformAdaptation format
|
||||
const convertedPlatformPersona: PlatformAdaptation = {
|
||||
id: 1,
|
||||
writing_persona_id: corePersona?.id || 1,
|
||||
platform_type: platform,
|
||||
sentence_metrics: {
|
||||
optimal_length: "150-300 words",
|
||||
character_limit: platform === 'linkedin' ? 3000 : 280,
|
||||
sentence_structure: "varied",
|
||||
paragraph_breaks: "frequent",
|
||||
readability_score: 8.5
|
||||
},
|
||||
lexical_features: {
|
||||
hashtag_strategy: "3-5 relevant hashtags",
|
||||
platform_specific_terms: [],
|
||||
engagement_phrases: ["What do you think?", "Share your thoughts"],
|
||||
call_to_action_style: "gentle"
|
||||
},
|
||||
rhetorical_devices: {
|
||||
question_frequency: "occasional",
|
||||
story_elements: "personal_anecdotes",
|
||||
visual_descriptions: "minimal",
|
||||
interactive_elements: "questions"
|
||||
},
|
||||
tonal_range: {
|
||||
default_tone: "professional_friendly",
|
||||
permissible_tones: ["inspiring", "thoughtful"],
|
||||
forbidden_tones: ["salesy", "academic"],
|
||||
emotional_range: "moderate",
|
||||
formality_level: "semi_formal"
|
||||
},
|
||||
stylistic_constraints: {
|
||||
punctuation_preferences: "standard",
|
||||
formatting_rules: "clean",
|
||||
emoji_usage: "minimal",
|
||||
link_placement: "end",
|
||||
media_integration: "encouraged"
|
||||
},
|
||||
content_format_rules: {
|
||||
character_limit: platform === 'linkedin' ? 3000 : 280,
|
||||
optimal_length: platform === 'linkedin' ? "150-300 words" : "120-150 characters",
|
||||
word_count: platform === 'linkedin' ? "150-300" : "20-25",
|
||||
hashtag_limit: platform === 'instagram' ? 30 : 3,
|
||||
media_requirements: "optional",
|
||||
link_restrictions: "unlimited"
|
||||
},
|
||||
engagement_patterns: {
|
||||
posting_frequency: "2-3 times per week",
|
||||
best_timing: "9 AM - 11 AM, 1 PM - 3 PM",
|
||||
interaction_style: "conversational",
|
||||
response_strategy: "within 2 hours",
|
||||
community_approach: "collaborative"
|
||||
},
|
||||
posting_frequency: {
|
||||
frequency: "2-3 times per week",
|
||||
optimal_days: ["Tuesday", "Wednesday", "Thursday"],
|
||||
optimal_times: ["9:00 AM", "1:00 PM"],
|
||||
seasonal_adjustments: "moderate"
|
||||
},
|
||||
content_types: {
|
||||
primary_content: ["thought_leadership", "industry_insights"],
|
||||
secondary_content: ["personal_stories", "tips"],
|
||||
content_mix: "70% professional, 30% personal",
|
||||
seasonal_content: ["trending_topics", "industry_events"]
|
||||
},
|
||||
platform_best_practices: {
|
||||
algorithm_tips: ["post_consistently", "engage_with_community"],
|
||||
engagement_tactics: ["ask_questions", "share_stories"],
|
||||
content_strategies: ["value_first", "authentic_voice"],
|
||||
growth_hacks: ["cross_promotion", "collaboration"]
|
||||
},
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
setPlatformPersona(convertedPlatformPersona);
|
||||
|
||||
console.log('✅ Platform persona loaded:', {
|
||||
platform: platformPersonaResponse.platform_type,
|
||||
characterLimit: platformPersonaResponse.content_format_rules?.character_limit,
|
||||
optimalLength: platformPersonaResponse.content_format_rules?.optimal_length
|
||||
platform: convertedPlatformPersona.platform_type,
|
||||
characterLimit: convertedPlatformPersona.content_format_rules?.character_limit,
|
||||
optimalLength: convertedPlatformPersona.content_format_rules?.optimal_length
|
||||
});
|
||||
} else {
|
||||
console.warn(`⚠️ No platform-specific persona found for ${platform}`);
|
||||
@@ -101,13 +249,16 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
lastRequestTime.current = Date.now();
|
||||
dataCacheTime.current = Date.now();
|
||||
requestInProgress.current = false;
|
||||
}
|
||||
};
|
||||
}, [userId, platform, corePersona]);
|
||||
|
||||
// Initial data fetch
|
||||
useEffect(() => {
|
||||
fetchPersonas();
|
||||
}, [platform, userId]);
|
||||
}, [fetchPersonas]);
|
||||
|
||||
// Refresh function for manual updates
|
||||
const refreshPersonas = async () => {
|
||||
@@ -200,7 +351,6 @@ export const PlatformPersonaProvider: React.FC<PlatformPersonaProviderProps> = (
|
||||
</PlatformPersonaContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// Custom hook to use the context
|
||||
export const usePlatformPersonaContext = () => {
|
||||
const context = useContext(PlatformPersonaContext);
|
||||
@@ -212,3 +362,4 @@ export const usePlatformPersonaContext = () => {
|
||||
|
||||
// Export the context for direct access if needed
|
||||
export { PlatformPersonaContext };
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export {
|
||||
usePlatformPersonaContext
|
||||
} from './PlatformPersonaProvider';
|
||||
|
||||
export { PersonaTestComponent } from './PersonaTestComponent';
|
||||
// PersonaTestComponent removed - functionality integrated into main components
|
||||
|
||||
// Re-export types for convenience
|
||||
export type {
|
||||
|
||||
Reference in New Issue
Block a user