Revert "Implement V2 AI functions and enhance progress handling"
This reverts commit e2f2d79d4c.
This commit is contained in:
@@ -66,7 +66,7 @@ export default function AIProgressModal({
|
||||
|
||||
const modalInstanceId = modalInstanceIdRef.current || 'modal-01';
|
||||
|
||||
// Build full function ID with modal instance (only for debugging, not shown in UI)
|
||||
// Build full function ID with modal instance
|
||||
const fullFunctionId = functionId ? `${functionId}-${modalInstanceId}` : null;
|
||||
|
||||
// Determine color based on status
|
||||
@@ -201,10 +201,11 @@ export default function AIProgressModal({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Task ID (for debugging - Function ID not shown per requirements) */}
|
||||
{taskId && (
|
||||
{/* Function ID and Task ID (for debugging) */}
|
||||
{(fullFunctionId || taskId) && (
|
||||
<div className="mb-6 space-y-1 text-xs text-gray-400 dark:text-gray-600">
|
||||
<div>Task ID: {taskId}</div>
|
||||
{fullFunctionId && <div>Function ID: {fullFunctionId}</div>}
|
||||
{taskId && <div>Task ID: {taskId}</div>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -281,10 +282,11 @@ export default function AIProgressModal({
|
||||
{config.errorMessage || message}
|
||||
</p>
|
||||
|
||||
{/* Task ID (for debugging - Function ID not shown per requirements) */}
|
||||
{taskId && (
|
||||
{/* Function ID and Task ID (for debugging) */}
|
||||
{(fullFunctionId || taskId) && (
|
||||
<div className="mb-6 space-y-1 text-xs text-gray-400 dark:text-gray-600">
|
||||
<div>Task ID: {taskId}</div>
|
||||
{fullFunctionId && <div>Function ID: {fullFunctionId}</div>}
|
||||
{taskId && <div>Task ID: {taskId}</div>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -388,10 +390,15 @@ export default function AIProgressModal({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Task ID (for debugging - Function ID not shown per requirements) */}
|
||||
{taskId && (
|
||||
{/* Function ID and Task ID (for debugging) */}
|
||||
{(fullFunctionId || taskId) && (
|
||||
<div className="mb-4 space-y-1 text-xs text-gray-400 dark:text-gray-600">
|
||||
<div>Task ID: {taskId}</div>
|
||||
{fullFunctionId && (
|
||||
<div>Function ID: {fullFunctionId}</div>
|
||||
)}
|
||||
{taskId && (
|
||||
<div>Task ID: {taskId}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -144,12 +144,6 @@ const tableActionsConfigs: Record<string, TableActionsConfig> = {
|
||||
icon: <BoltIcon className="w-4 h-4 text-warning-500" />,
|
||||
variant: 'secondary',
|
||||
},
|
||||
{
|
||||
key: 'keywords_clustering',
|
||||
label: 'Keywords Clustering',
|
||||
icon: <BoltIcon className="w-4 h-4 text-brand-500" />,
|
||||
variant: 'secondary',
|
||||
},
|
||||
],
|
||||
},
|
||||
'/planner/clusters': {
|
||||
@@ -166,12 +160,6 @@ const tableActionsConfigs: Record<string, TableActionsConfig> = {
|
||||
icon: <BoltIcon className="w-5 h-5" />,
|
||||
variant: 'primary',
|
||||
},
|
||||
{
|
||||
key: 'generate_ideas_v2',
|
||||
label: 'Generate Ideas V2',
|
||||
icon: <BoltIcon className="w-5 h-5" />,
|
||||
variant: 'primary',
|
||||
},
|
||||
],
|
||||
bulkActions: [
|
||||
{
|
||||
|
||||
@@ -232,10 +232,8 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
`/v1/system/settings/task_progress/${taskId}/`
|
||||
);
|
||||
|
||||
// Helper function to start auto-increment progress
|
||||
// 0-50%: 300ms per 1%
|
||||
// 50-80%: 500ms per 1%
|
||||
// If stuck at 80%: 1% per 500ms
|
||||
// Helper function to start auto-increment progress (1% every 350ms until 80%)
|
||||
// Only runs when no backend updates are coming (smooth fill-in animation)
|
||||
const startAutoIncrement = () => {
|
||||
// Clear any existing auto-increment interval
|
||||
if (autoIncrementIntervalRef.current) {
|
||||
@@ -243,10 +241,11 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
autoIncrementIntervalRef.current = null;
|
||||
}
|
||||
|
||||
// Only start if we're below 100% and status is processing
|
||||
// Only start if we're below 80% and status is processing
|
||||
const current = displayedPercentageRef.current;
|
||||
if (current < 100) {
|
||||
const doIncrement = () => {
|
||||
if (current < 80) {
|
||||
// Use a slightly longer interval to avoid conflicts with backend updates
|
||||
autoIncrementIntervalRef.current = setInterval(() => {
|
||||
setProgress(prev => {
|
||||
// Check current status - stop if not processing
|
||||
if (prev.status !== 'processing') {
|
||||
@@ -258,30 +257,17 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
}
|
||||
|
||||
const currentPercent = displayedPercentageRef.current;
|
||||
let newPercentage = currentPercent;
|
||||
let nextInterval = 300; // Default 300ms
|
||||
|
||||
if (currentPercent < 50) {
|
||||
// 0-50%: 300ms per 1%
|
||||
newPercentage = Math.min(currentPercent + 1, 50);
|
||||
nextInterval = 300;
|
||||
} else if (currentPercent < 80) {
|
||||
// 50-80%: 500ms per 1%
|
||||
newPercentage = Math.min(currentPercent + 1, 80);
|
||||
nextInterval = 500;
|
||||
} else if (currentPercent < 100) {
|
||||
// Stuck at 80%+: 1% per 500ms
|
||||
newPercentage = Math.min(currentPercent + 1, 99);
|
||||
nextInterval = 500;
|
||||
}
|
||||
|
||||
if (newPercentage > currentPercent && newPercentage < 100) {
|
||||
// Only increment if still below 80%
|
||||
if (currentPercent < 80) {
|
||||
const newPercentage = Math.min(currentPercent + 1, 80);
|
||||
displayedPercentageRef.current = newPercentage;
|
||||
|
||||
// Restart interval with new speed if needed
|
||||
if (autoIncrementIntervalRef.current) {
|
||||
clearInterval(autoIncrementIntervalRef.current);
|
||||
autoIncrementIntervalRef.current = setInterval(doIncrement, nextInterval);
|
||||
// Stop if we've reached 80%
|
||||
if (newPercentage >= 80) {
|
||||
if (autoIncrementIntervalRef.current) {
|
||||
clearInterval(autoIncrementIntervalRef.current);
|
||||
autoIncrementIntervalRef.current = null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -289,7 +275,7 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
percentage: newPercentage,
|
||||
};
|
||||
} else {
|
||||
// Stop if we've reached 100% or can't increment
|
||||
// Stop if we've reached 80%
|
||||
if (autoIncrementIntervalRef.current) {
|
||||
clearInterval(autoIncrementIntervalRef.current);
|
||||
autoIncrementIntervalRef.current = null;
|
||||
@@ -297,11 +283,7 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
return prev;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Start with appropriate interval based on current percentage
|
||||
const initialInterval = current < 50 ? 300 : 500;
|
||||
autoIncrementIntervalRef.current = setInterval(doIncrement, initialInterval);
|
||||
}, 350); // Slightly longer interval to reduce conflicts
|
||||
}
|
||||
};
|
||||
|
||||
@@ -405,20 +387,19 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
const safeTargetPercentage = Math.max(targetPercentage, currentDisplayedPercentage);
|
||||
|
||||
// Smooth progress animation: increment gradually until reaching target
|
||||
// Speed: 300ms per 1% until 50%, then 500ms per 1%
|
||||
// Use smaller increments and faster updates for smoother animation
|
||||
if (safeTargetPercentage > currentDisplayedPercentage) {
|
||||
// Start smooth animation
|
||||
let animatedPercentage = currentDisplayedPercentage;
|
||||
const animateProgress = () => {
|
||||
if (animatedPercentage < safeTargetPercentage) {
|
||||
// Always increment by 1%
|
||||
const increment = 1;
|
||||
// Calculate increment based on distance for smooth animation
|
||||
const diff = safeTargetPercentage - animatedPercentage;
|
||||
// Use smaller increments for smoother feel
|
||||
// If close (< 5%), increment by 1, otherwise by 2
|
||||
const increment = diff <= 5 ? 1 : Math.min(2, Math.ceil(diff / 10));
|
||||
animatedPercentage = Math.min(animatedPercentage + increment, safeTargetPercentage);
|
||||
displayedPercentageRef.current = animatedPercentage;
|
||||
|
||||
// Determine speed based on current percentage
|
||||
const speed = animatedPercentage < 50 ? 300 : 500; // 300ms until 50%, then 500ms
|
||||
|
||||
setProgress({
|
||||
percentage: animatedPercentage,
|
||||
message: friendlyMessage,
|
||||
@@ -433,17 +414,13 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
});
|
||||
|
||||
if (animatedPercentage < safeTargetPercentage) {
|
||||
// Use appropriate speed based on current percentage
|
||||
const nextSpeed = animatedPercentage < 50 ? 300 : 500;
|
||||
stepTransitionTimeoutRef.current = setTimeout(animateProgress, nextSpeed);
|
||||
// Smooth updates: 150ms for better UX
|
||||
stepTransitionTimeoutRef.current = setTimeout(animateProgress, 150);
|
||||
} else {
|
||||
stepTransitionTimeoutRef.current = null;
|
||||
// After reaching target, start auto-increment if below 80% and no backend update pending
|
||||
if (safeTargetPercentage < 80) {
|
||||
startAutoIncrement();
|
||||
} else if (safeTargetPercentage >= 80 && safeTargetPercentage < 100) {
|
||||
// If at 80%+, start slow auto-increment (1% per 500ms)
|
||||
startAutoIncrement();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,8 +435,7 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
} else {
|
||||
// Same step or first step - start animation immediately
|
||||
currentStepRef.current = currentStep;
|
||||
const initialSpeed = currentDisplayedPercentage < 50 ? 300 : 500;
|
||||
stepTransitionTimeoutRef.current = setTimeout(animateProgress, initialSpeed);
|
||||
animateProgress();
|
||||
}
|
||||
} else {
|
||||
// Target is same or less than current - just update message and details
|
||||
@@ -479,9 +455,6 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
// Start auto-increment if below 80% and no backend update
|
||||
if (currentDisplayedPercentage < 80 && safeTargetPercentage === currentDisplayedPercentage) {
|
||||
startAutoIncrement();
|
||||
} else if (currentDisplayedPercentage >= 80 && currentDisplayedPercentage < 100) {
|
||||
// If at 80%+, start slow auto-increment (1% per 500ms)
|
||||
startAutoIncrement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,61 +527,26 @@ export function useProgressModal(): UseProgressModalReturn {
|
||||
} else if (response.state === 'SUCCESS') {
|
||||
const meta = response.meta || {};
|
||||
|
||||
// Clear any existing transition timeout and auto-increment
|
||||
// Clear any existing transition timeout
|
||||
if (stepTransitionTimeoutRef.current) {
|
||||
clearTimeout(stepTransitionTimeoutRef.current);
|
||||
stepTransitionTimeoutRef.current = null;
|
||||
}
|
||||
if (autoIncrementIntervalRef.current) {
|
||||
clearInterval(autoIncrementIntervalRef.current);
|
||||
autoIncrementIntervalRef.current = null;
|
||||
}
|
||||
|
||||
// Get completion message with extracted values
|
||||
const completionMessage = meta.message || '';
|
||||
const allSteps = [...(meta.request_steps || []), ...(meta.response_steps || [])];
|
||||
const stepInfo = getStepInfo('DONE', completionMessage, allSteps);
|
||||
|
||||
// Smooth completion animation: 5% per 500ms until 100%
|
||||
const currentPercent = displayedPercentageRef.current;
|
||||
if (currentPercent < 100) {
|
||||
const animateToCompletion = () => {
|
||||
const current = displayedPercentageRef.current;
|
||||
if (current < 100) {
|
||||
const increment = Math.min(5, 100 - current); // 5% per step, or remaining if less
|
||||
const newPercentage = current + increment;
|
||||
displayedPercentageRef.current = newPercentage;
|
||||
|
||||
setProgress({
|
||||
percentage: newPercentage,
|
||||
message: stepInfo.friendlyMessage,
|
||||
status: 'completed',
|
||||
details: meta.details,
|
||||
});
|
||||
|
||||
if (newPercentage < 100) {
|
||||
stepTransitionTimeoutRef.current = setTimeout(animateToCompletion, 500);
|
||||
} else {
|
||||
currentStepRef.current = 'DONE';
|
||||
}
|
||||
} else {
|
||||
currentStepRef.current = 'DONE';
|
||||
}
|
||||
};
|
||||
|
||||
currentStepRef.current = 'DONE';
|
||||
animateToCompletion();
|
||||
} else {
|
||||
// Already at 100%, just update message and status
|
||||
currentStepRef.current = 'DONE';
|
||||
displayedPercentageRef.current = 100;
|
||||
setProgress({
|
||||
percentage: 100,
|
||||
message: stepInfo.friendlyMessage,
|
||||
status: 'completed',
|
||||
details: meta.details,
|
||||
});
|
||||
}
|
||||
// Update to 100% with user-friendly completion message
|
||||
currentStepRef.current = 'DONE';
|
||||
displayedPercentageRef.current = 100;
|
||||
setProgress({
|
||||
percentage: 100,
|
||||
message: stepInfo.friendlyMessage,
|
||||
status: 'completed',
|
||||
details: meta.details,
|
||||
});
|
||||
|
||||
// Update final step logs
|
||||
if (meta.request_steps || meta.response_steps) {
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
bulkDeleteClusters,
|
||||
bulkUpdateClustersStatus,
|
||||
autoGenerateIdeas,
|
||||
generateIdeasV2,
|
||||
Cluster,
|
||||
ClusterFilters,
|
||||
ClusterCreateData,
|
||||
@@ -219,22 +218,6 @@ export default function Clusters() {
|
||||
} catch (error: any) {
|
||||
toast.error(`Failed to generate ideas: ${error.message}`);
|
||||
}
|
||||
} else if (action === 'generate_ideas_v2') {
|
||||
try {
|
||||
const result = await generateIdeasV2([row.id]);
|
||||
|
||||
if (result.success && result.task_id) {
|
||||
// Async task - show progress modal
|
||||
progressModal.openModal(result.task_id, 'Generate Ideas', 'ai_generate_ideas_v2');
|
||||
} else if (result.success) {
|
||||
toast.success(result.message || 'Ideas generated successfully');
|
||||
await loadClusters();
|
||||
} else {
|
||||
toast.error(result.error || 'Failed to generate ideas');
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(`Failed to generate ideas: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}, [toast, progressModal, loadClusters]);
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
Cluster,
|
||||
API_BASE_URL,
|
||||
autoClusterKeywords,
|
||||
autoClusterKeywordsV2,
|
||||
fetchSeedKeywords,
|
||||
SeedKeyword,
|
||||
} from '../../services/api';
|
||||
@@ -449,35 +448,6 @@ export default function Keywords() {
|
||||
}]);
|
||||
toast.error(errorMsg);
|
||||
}
|
||||
} else if (action === 'keywords_clustering') {
|
||||
if (ids.length === 0) {
|
||||
toast.error('Please select at least one keyword');
|
||||
return;
|
||||
}
|
||||
if (ids.length > 50) {
|
||||
toast.error('Maximum 50 keywords allowed for clustering');
|
||||
return;
|
||||
}
|
||||
|
||||
const numIds = ids.map(id => parseInt(id));
|
||||
const sectorId = activeSector?.id;
|
||||
|
||||
try {
|
||||
const result = await autoClusterKeywordsV2(numIds, sectorId);
|
||||
|
||||
if (result.success && result.task_id) {
|
||||
// Async task - open progress modal
|
||||
hasReloadedRef.current = false;
|
||||
progressModal.openModal(result.task_id, 'Keywords Clustering', 'ai_auto_cluster_v2');
|
||||
} else if (result.success) {
|
||||
toast.success(result.message || 'Clustering completed');
|
||||
await loadKeywords();
|
||||
} else {
|
||||
toast.error(result.error || 'Clustering failed');
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(`Clustering failed: ${error.message}`);
|
||||
}
|
||||
} else {
|
||||
toast.info(`Bulk action "${action}" for ${ids.length} items`);
|
||||
}
|
||||
|
||||
@@ -807,140 +807,6 @@ export async function autoGenerateIdeas(clusterIds: number[]): Promise<{ success
|
||||
}
|
||||
}
|
||||
|
||||
export async function autoClusterKeywordsV2(keywordIds: number[], sectorId?: number): Promise<{ success: boolean; task_id?: string; clusters_created?: number; keywords_updated?: number; message?: string; error?: string }> {
|
||||
const startTime = Date.now();
|
||||
const addLog = useAIRequestLogsStore.getState().addLog;
|
||||
|
||||
const endpoint = `/v1/planner/keywords/auto_cluster_v2/`;
|
||||
const requestBody = { ids: keywordIds, sector_id: sectorId };
|
||||
|
||||
const pendingLogId = addLog({
|
||||
function: 'autoClusterKeywordsV2',
|
||||
endpoint,
|
||||
request: {
|
||||
method: 'POST',
|
||||
body: requestBody,
|
||||
},
|
||||
status: 'pending',
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetchAPI(endpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
const updateLog = useAIRequestLogsStore.getState().updateLog;
|
||||
|
||||
if (pendingLogId && response) {
|
||||
updateLog(pendingLogId, {
|
||||
response: {
|
||||
status: 200,
|
||||
data: response,
|
||||
},
|
||||
status: response.success === false ? 'error' : 'success',
|
||||
duration,
|
||||
});
|
||||
}
|
||||
|
||||
if (response && response.success === false) {
|
||||
return response;
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
const duration = Date.now() - startTime;
|
||||
const updateLog = useAIRequestLogsStore.getState().updateLog;
|
||||
|
||||
let errorMessage = error.message || 'Unknown error';
|
||||
|
||||
if (pendingLogId) {
|
||||
updateLog(pendingLogId, {
|
||||
response: {
|
||||
status: 500,
|
||||
error: errorMessage,
|
||||
},
|
||||
status: 'error',
|
||||
duration,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateIdeasV2(clusterIds: number[]): Promise<{ success: boolean; task_id?: string; ideas_created?: number; message?: string; error?: string }> {
|
||||
const startTime = Date.now();
|
||||
const { useAIRequestLogsStore } = await import('../store/aiRequestLogsStore').catch(() => ({ useAIRequestLogsStore: null }));
|
||||
const addLog = useAIRequestLogsStore?.getState().addLog;
|
||||
|
||||
const endpoint = `/v1/planner/clusters/generate_ideas_v2/`;
|
||||
const requestBody = { ids: clusterIds };
|
||||
|
||||
addLog?.({
|
||||
function: 'generateIdeasV2',
|
||||
endpoint,
|
||||
request: {
|
||||
method: 'POST',
|
||||
body: requestBody,
|
||||
},
|
||||
status: 'pending',
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetchAPI(endpoint, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
addLog?.({
|
||||
function: 'generateIdeasV2',
|
||||
endpoint,
|
||||
request: {
|
||||
method: 'POST',
|
||||
body: requestBody,
|
||||
},
|
||||
response: {
|
||||
status: 200,
|
||||
data: response,
|
||||
},
|
||||
status: 'success',
|
||||
duration,
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
const duration = Date.now() - startTime;
|
||||
|
||||
let errorMessage = error.message || 'Unknown error';
|
||||
|
||||
addLog?.({
|
||||
function: 'generateIdeasV2',
|
||||
endpoint,
|
||||
request: {
|
||||
method: 'POST',
|
||||
body: requestBody,
|
||||
},
|
||||
response: {
|
||||
status: 500,
|
||||
error: errorMessage,
|
||||
},
|
||||
status: 'error',
|
||||
duration,
|
||||
});
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateSingleIdea(ideaId: string | number, clusterId: number): Promise<{ success: boolean; task_id?: string; idea_created?: number; message?: string; error?: string }> {
|
||||
const startTime = Date.now();
|
||||
const { useAIRequestLogsStore } = await import('../store/aiRequestLogsStore').catch(() => ({ useAIRequestLogsStore: null }));
|
||||
|
||||
Reference in New Issue
Block a user