ALwrity version 0.5.5
This commit is contained in:
282
frontend/src/services/navigationOrchestrator.ts
Normal file
282
frontend/src/services/navigationOrchestrator.ts
Normal file
@@ -0,0 +1,282 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export interface StrategyContext {
|
||||
strategyId: string;
|
||||
strategyData: any;
|
||||
activationStatus: 'pending' | 'confirmed' | 'active';
|
||||
activationTimestamp: string;
|
||||
userPreferences: any;
|
||||
strategicIntelligence: any;
|
||||
}
|
||||
|
||||
export interface CalendarContext {
|
||||
strategyContext: StrategyContext | null;
|
||||
autoPopulatedData: any;
|
||||
userModifications: any;
|
||||
generationProgress: number;
|
||||
}
|
||||
|
||||
export interface WorkflowProgress {
|
||||
currentStep: 'strategy' | 'activation' | 'calendar' | 'generation';
|
||||
completedSteps: string[];
|
||||
totalSteps: number;
|
||||
progressPercentage: number;
|
||||
}
|
||||
|
||||
export interface NavigationState {
|
||||
fromStrategy: boolean;
|
||||
preservedContext: StrategyContext | null;
|
||||
returnPath: string | null;
|
||||
}
|
||||
|
||||
class NavigationOrchestrator {
|
||||
private static instance: NavigationOrchestrator;
|
||||
private contextStorage: Map<string, any> = new Map();
|
||||
private progressTracking: WorkflowProgress = {
|
||||
currentStep: 'strategy',
|
||||
completedSteps: [],
|
||||
totalSteps: 4,
|
||||
progressPercentage: 0
|
||||
};
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static getInstance(): NavigationOrchestrator {
|
||||
if (!NavigationOrchestrator.instance) {
|
||||
NavigationOrchestrator.instance = new NavigationOrchestrator();
|
||||
}
|
||||
return NavigationOrchestrator.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate from strategy activation to calendar wizard
|
||||
*/
|
||||
navigateToCalendarWizard(strategyId: string, strategyContext: StrategyContext): void {
|
||||
console.log('🎯 Navigation Orchestrator: Navigating to calendar wizard', { strategyId });
|
||||
|
||||
// Preserve strategy context
|
||||
this.preserveContext('strategy', strategyContext);
|
||||
|
||||
// Update progress
|
||||
this.updateProgress('activation');
|
||||
|
||||
// Store navigation state
|
||||
this.contextStorage.set('navigationState', {
|
||||
fromStrategy: true,
|
||||
preservedContext: strategyContext,
|
||||
returnPath: '/content-planning'
|
||||
});
|
||||
|
||||
// Also store in session storage for context restoration
|
||||
sessionStorage.setItem('strategyCalendarContext', JSON.stringify({
|
||||
strategyContext,
|
||||
lastUpdated: new Date().toISOString()
|
||||
}));
|
||||
|
||||
// Navigate to calendar wizard with context
|
||||
const navigate = this.getNavigateFunction();
|
||||
if (navigate) {
|
||||
navigate('/content-planning', {
|
||||
state: {
|
||||
activeTab: 4, // Create tab (where Calendar Wizard is located)
|
||||
strategyContext,
|
||||
fromStrategyActivation: true
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Preserve context for later restoration
|
||||
*/
|
||||
preserveContext(key: string, context: any): void {
|
||||
console.log('💾 Navigation Orchestrator: Preserving context', { key });
|
||||
this.contextStorage.set(key, {
|
||||
data: context,
|
||||
timestamp: new Date().toISOString(),
|
||||
expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString() // 30 minutes
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore context by key
|
||||
*/
|
||||
restoreContext(key: string): any | null {
|
||||
const stored = this.contextStorage.get(key);
|
||||
if (!stored) {
|
||||
console.log('⚠️ Navigation Orchestrator: No context found for key', { key });
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if context has expired
|
||||
if (new Date() > new Date(stored.expiresAt)) {
|
||||
console.log('⏰ Navigation Orchestrator: Context expired for key', { key });
|
||||
this.contextStorage.delete(key);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log('🔄 Navigation Orchestrator: Restoring context', { key });
|
||||
return stored.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear context by key
|
||||
*/
|
||||
clearContext(key: string): void {
|
||||
console.log('🗑️ Navigation Orchestrator: Clearing context', { key });
|
||||
this.contextStorage.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all contexts
|
||||
*/
|
||||
clearAllContexts(): void {
|
||||
console.log('🗑️ Navigation Orchestrator: Clearing all contexts');
|
||||
this.contextStorage.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update workflow progress
|
||||
*/
|
||||
updateProgress(step: WorkflowProgress['currentStep']): void {
|
||||
const steps = ['strategy', 'activation', 'calendar', 'generation'];
|
||||
const currentIndex = steps.indexOf(step);
|
||||
|
||||
this.progressTracking = {
|
||||
currentStep: step,
|
||||
completedSteps: steps.slice(0, currentIndex),
|
||||
totalSteps: steps.length,
|
||||
progressPercentage: ((currentIndex + 1) / steps.length) * 100
|
||||
};
|
||||
|
||||
console.log('📊 Navigation Orchestrator: Progress updated', this.progressTracking);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current progress
|
||||
*/
|
||||
getProgress(): WorkflowProgress {
|
||||
return { ...this.progressTracking };
|
||||
}
|
||||
|
||||
/**
|
||||
* Track navigation event
|
||||
*/
|
||||
trackNavigation(from: string, to: string, context?: any): void {
|
||||
console.log('🧭 Navigation Orchestrator: Navigation tracked', { from, to, hasContext: !!context });
|
||||
|
||||
// Store navigation history
|
||||
const history = this.contextStorage.get('navigationHistory') || [];
|
||||
history.push({
|
||||
from,
|
||||
to,
|
||||
timestamp: new Date().toISOString(),
|
||||
context: context ? Object.keys(context) : []
|
||||
});
|
||||
|
||||
// Keep only last 10 navigation events
|
||||
if (history.length > 10) {
|
||||
history.splice(0, history.length - 10);
|
||||
}
|
||||
|
||||
this.contextStorage.set('navigationHistory', history);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get navigation state
|
||||
*/
|
||||
getNavigationState(): NavigationState | null {
|
||||
return this.contextStorage.get('navigationState') || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if navigation is from strategy activation
|
||||
*/
|
||||
isFromStrategyActivation(): boolean {
|
||||
const state = this.getNavigationState();
|
||||
return state?.fromStrategy || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get preserved strategy context
|
||||
*/
|
||||
getPreservedStrategyContext(): StrategyContext | null {
|
||||
return this.restoreContext('strategy');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle strategy activation success
|
||||
*/
|
||||
handleStrategyActivationSuccess(strategyId: string, strategyData: any): void {
|
||||
console.log('✅ Navigation Orchestrator: Strategy activation successful', { strategyId });
|
||||
|
||||
const strategyContext: StrategyContext = {
|
||||
strategyId,
|
||||
strategyData,
|
||||
activationStatus: 'active',
|
||||
activationTimestamp: new Date().toISOString(),
|
||||
userPreferences: strategyData.userPreferences || {},
|
||||
strategicIntelligence: strategyData.strategicIntelligence || {}
|
||||
};
|
||||
|
||||
// Navigate to calendar wizard
|
||||
this.navigateToCalendarWizard(strategyId, strategyContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle calendar generation completion
|
||||
*/
|
||||
handleCalendarGenerationComplete(calendarData: any): void {
|
||||
console.log('✅ Navigation Orchestrator: Calendar generation complete');
|
||||
|
||||
// Update progress
|
||||
this.updateProgress('generation');
|
||||
|
||||
// Clear strategy context as workflow is complete
|
||||
this.clearContext('strategy');
|
||||
|
||||
// Navigate to calendar view
|
||||
const navigate = this.getNavigateFunction();
|
||||
if (navigate) {
|
||||
navigate('/content-planning', {
|
||||
state: {
|
||||
activeTab: 1, // Calendar tab
|
||||
showGeneratedCalendar: true,
|
||||
calendarData
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get navigate function (to be set by components)
|
||||
*/
|
||||
private getNavigateFunction(): any {
|
||||
// Return the stored navigate function
|
||||
return (this as any).navigate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set navigate function (called by components)
|
||||
*/
|
||||
setNavigateFunction(navigate: any): void {
|
||||
// Store navigate function for internal use
|
||||
(this as any).navigate = navigate;
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const navigationOrchestrator = NavigationOrchestrator.getInstance();
|
||||
|
||||
// Hook for components to use the orchestrator
|
||||
export const useNavigationOrchestrator = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Set navigate function in orchestrator only once
|
||||
React.useEffect(() => {
|
||||
navigationOrchestrator.setNavigateFunction(navigate);
|
||||
}, [navigate]);
|
||||
|
||||
return navigationOrchestrator;
|
||||
};
|
||||
259
frontend/src/services/strategyMonitoringApi.ts
Normal file
259
frontend/src/services/strategyMonitoringApi.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
import { apiClient } from '../api/client';
|
||||
import { useState } from 'react';
|
||||
|
||||
export interface MonitoringTask {
|
||||
component: string;
|
||||
title: string;
|
||||
description: string;
|
||||
assignee: 'ALwrity' | 'Human';
|
||||
frequency: string;
|
||||
metric: string;
|
||||
measurementMethod: string;
|
||||
successCriteria: string;
|
||||
alertThreshold: string;
|
||||
actionableInsights: string;
|
||||
}
|
||||
|
||||
export interface MonitoringComponent {
|
||||
name: string;
|
||||
icon: string;
|
||||
tasks: MonitoringTask[];
|
||||
}
|
||||
|
||||
export interface MonitoringPlan {
|
||||
totalTasks: number;
|
||||
alwrityTasks: number;
|
||||
humanTasks: number;
|
||||
metricsCount: number;
|
||||
monitoringTasks: MonitoringTask[];
|
||||
monitoringSchedule?: any;
|
||||
successMetrics?: any;
|
||||
metadata?: any;
|
||||
}
|
||||
|
||||
export const strategyMonitoringApi = {
|
||||
/**
|
||||
* Generate monitoring plan for a strategy
|
||||
*/
|
||||
async generateMonitoringPlan(strategyId: number): Promise<{ success: boolean; data: MonitoringPlan; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.post(`/api/content-planning/strategy/${strategyId}/generate-monitoring-plan`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error generating monitoring plan:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to generate monitoring plan');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Activate strategy with monitoring plan
|
||||
*/
|
||||
async activateStrategyWithMonitoring(strategyId: number, monitoringPlan: MonitoringPlan): Promise<{ success: boolean; message: string; strategy_id: number }> {
|
||||
try {
|
||||
const response = await apiClient.post(`/api/content-planning/strategy/${strategyId}/activate-with-monitoring`, monitoringPlan);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error activating strategy with monitoring:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to activate strategy with monitoring');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get monitoring plan for a strategy
|
||||
*/
|
||||
async getMonitoringPlan(strategyId: number): Promise<{ success: boolean; data: any }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/content-planning/strategy/${strategyId}/monitoring-plan`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error getting monitoring plan:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to get monitoring plan');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update monitoring plan
|
||||
*/
|
||||
async updateMonitoringPlan(strategyId: number, monitoringPlan: Partial<MonitoringPlan>): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.put(`/api/content-planning/strategy/${strategyId}/monitoring-plan`, monitoringPlan);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error updating monitoring plan:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to update monitoring plan');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get performance history for a strategy
|
||||
*/
|
||||
async getPerformanceHistory(strategyId: number, days: number = 30): Promise<{ success: boolean; data: any }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/content-planning/strategy/${strategyId}/performance-history?days=${days}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error getting performance history:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to get performance history');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Deactivate a strategy
|
||||
*/
|
||||
async deactivateStrategy(strategyId: number, userId: number = 1): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.post(`/api/content-planning/strategy/${strategyId}/deactivate`, { user_id: userId });
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error deactivating strategy:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to deactivate strategy');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Pause a strategy
|
||||
*/
|
||||
async pauseStrategy(strategyId: number, userId: number = 1): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.post(`/api/content-planning/strategy/${strategyId}/pause`, { user_id: userId });
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error pausing strategy:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to pause strategy');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Resume a strategy
|
||||
*/
|
||||
async resumeStrategy(strategyId: number, userId: number = 1): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.post(`/api/content-planning/strategy/${strategyId}/resume`, { user_id: userId });
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error resuming strategy:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to resume strategy');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get performance metrics for a strategy
|
||||
*/
|
||||
async getPerformanceMetrics(strategyId: number): Promise<{ success: boolean; data: any; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/content-planning/strategy/${strategyId}/performance-metrics`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error getting performance metrics:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to get performance metrics');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get trend data for a strategy
|
||||
*/
|
||||
async getTrendData(strategyId: number, timeRange: string = '30d'): Promise<{ success: boolean; data: any; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/content-planning/strategy/${strategyId}/trend-data?time_range=${timeRange}`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error getting trend data:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to get trend data');
|
||||
}
|
||||
},
|
||||
|
||||
// New API calls for transparency data
|
||||
async getTransparencyData(strategyId: number): Promise<{ success: boolean; data: any; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/content-planning/strategy/${strategyId}/transparency-data`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching transparency data:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to fetch transparency data');
|
||||
}
|
||||
},
|
||||
|
||||
async getMonitoringTasks(strategyId: number): Promise<{ success: boolean; data: any; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/content-planning/strategy/${strategyId}/monitoring-tasks`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching monitoring tasks:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to fetch monitoring tasks');
|
||||
}
|
||||
},
|
||||
|
||||
async getDataFreshness(strategyId: number): Promise<{ success: boolean; data: any; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/content-planning/strategy/${strategyId}/data-freshness`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching data freshness:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to fetch data freshness');
|
||||
}
|
||||
},
|
||||
|
||||
// Quality Analysis API methods
|
||||
async getQualityAnalysis(strategyId: number): Promise<{ success: boolean; data: any; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.post(`/api/content-planning/quality-analysis/${strategyId}/analyze`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching quality analysis:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to fetch quality analysis');
|
||||
}
|
||||
},
|
||||
|
||||
async getQualityMetrics(strategyId: number): Promise<{ success: boolean; data: any; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/content-planning/quality-analysis/${strategyId}/metrics`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching quality metrics:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to fetch quality metrics');
|
||||
}
|
||||
},
|
||||
|
||||
async getQualityRecommendations(strategyId: number): Promise<{ success: boolean; data: any; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/content-planning/quality-analysis/${strategyId}/recommendations`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching quality recommendations:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to fetch quality recommendations');
|
||||
}
|
||||
},
|
||||
|
||||
async getQualityDashboard(strategyId: number): Promise<{ success: boolean; data: any; message: string }> {
|
||||
try {
|
||||
const response = await apiClient.get(`/api/content-planning/quality-analysis/${strategyId}/dashboard`);
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching quality dashboard:', error);
|
||||
throw new Error(error.response?.data?.detail || 'Failed to fetch quality dashboard');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Hook for monitoring plan generation
|
||||
export const useMonitoringPlanGeneration = () => {
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const generatePlan = async (strategyId: number): Promise<MonitoringPlan> => {
|
||||
setIsGenerating(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await strategyMonitoringApi.generateMonitoringPlan(strategyId);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
setError(err.message);
|
||||
throw err;
|
||||
} finally {
|
||||
setIsGenerating(false);
|
||||
}
|
||||
};
|
||||
|
||||
return { generatePlan, isGenerating, error };
|
||||
};
|
||||
Reference in New Issue
Block a user