Add SEO fields to Tasks model, improve content generation response handling, and enhance progress bar animation

- Added primary_keyword, secondary_keywords, tags, and categories fields to Tasks model
- Updated generate_content function to handle full JSON response with all SEO fields
- Improved progress bar animation: smooth 1% increments every 300ms
- Enhanced step detection for content generation vs clustering vs ideas
- Fixed progress modal to show correct messages for each function type
- Added comprehensive logging to Keywords and Tasks pages for AI functions
- Fixed error handling to show meaningful error messages instead of generic failures
This commit is contained in:
Gitea Deploy
2025-11-09 21:22:34 +00:00
parent 09d22ab0e2
commit 961362e088
17340 changed files with 10636 additions and 2248776 deletions

View File

@@ -0,0 +1,76 @@
import { ReactNode, useEffect, useState } from 'react';
import { useLocation } from 'react-router';
interface PageTransitionProps {
children: ReactNode;
}
/**
* Smooth page transition wrapper with modern loading indicator
* Provides seamless transitions between pages without feeling like a page load
* Uses subtle fade effects and minimal loading indicator
*/
export default function PageTransition({ children }: PageTransitionProps) {
const location = useLocation();
const [isTransitioning, setIsTransitioning] = useState(false);
const [displayChildren, setDisplayChildren] = useState(children);
const [currentPath, setCurrentPath] = useState(location.pathname);
useEffect(() => {
// Only show transition if pathname actually changed
if (location.pathname === currentPath) {
setDisplayChildren(children);
return;
}
// Start transition with minimal delay
setIsTransitioning(true);
setCurrentPath(location.pathname);
// Quick fade-out, then swap content
const fadeOutTimer = setTimeout(() => {
setDisplayChildren(children);
}, 100);
// Complete transition quickly for smooth feel
const fadeInTimer = setTimeout(() => {
setIsTransitioning(false);
}, 200);
return () => {
clearTimeout(fadeOutTimer);
clearTimeout(fadeInTimer);
};
}, [location.pathname, children, currentPath]);
return (
<div className="relative min-h-screen">
{/* Subtle fade overlay - very light */}
<div
className={`absolute inset-0 bg-white/50 dark:bg-gray-900/50 backdrop-blur-sm transition-opacity duration-200 z-40 pointer-events-none ${
isTransitioning ? 'opacity-100' : 'opacity-0'
}`}
/>
{/* Minimal loading indicator - only shows briefly */}
{isTransitioning && (
<div className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50 pointer-events-none">
<div className="relative w-10 h-10">
<div className="absolute inset-0 border-2 border-gray-200/50 dark:border-gray-700/50 rounded-full"></div>
<div className="absolute inset-0 border-2 border-transparent border-t-brand-500 rounded-full animate-spin"></div>
</div>
</div>
)}
{/* Page content with smooth fade */}
<div
className={`transition-opacity duration-200 ease-in-out ${
isTransitioning ? 'opacity-0' : 'opacity-100'
}`}
>
{displayChildren}
</div>
</div>
);
}

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useRef } from 'react';
import { Modal } from '../ui/modal';
import { ProgressBar } from '../ui/progress';
import Button from '../ui/button/Button';
@@ -34,13 +34,19 @@ export default function ProgressModal({
}: ProgressModalProps) {
// Auto-close on completion after 2 seconds
// Don't auto-close on error - let user manually close to see error details
const hasAutoClosedRef = React.useRef(false);
useEffect(() => {
if (status === 'completed' && onClose) {
if (status === 'completed' && onClose && !hasAutoClosedRef.current) {
hasAutoClosedRef.current = true;
const timer = setTimeout(() => {
onClose();
}, 2000);
return () => clearTimeout(timer);
}
// Reset when status changes away from completed
if (status !== 'completed') {
hasAutoClosedRef.current = false;
}
// Don't auto-close on error - user should manually dismiss
}, [status, onClose]);