ppt-tool/frontend/components/ui/progress-bar.tsx
Vadym Samoilenko cf21ba4516 Phase 1-2: Foundation + Admin Panel & Client Management
Phase 1 (Foundation):
- Project restructure (presenton-main → backend/ + frontend/)
- Database schema (8 new models, Alembic config, seed script)
- Auth (Azure AD SSO + dev bypass, JWT sessions, AuthMiddleware)
- RBAC (access_service, rbac_middleware, admin routers)
- Audit logging (fire-and-forget, AuditMiddleware, admin router)
- i18n (react-i18next with 5 namespace files)

Phase 2 (Admin Panel & Client Management):
- Admin panel shell (sidebar layout, role guard, 12 pages)
- Redux admin slice with 18 async thunks
- User management (role changes, deactivation)
- Client management (CRUD, brand config, team management)
- Brand config editor (colors, fonts, logos, voice rules)
- Master deck upload & parser (PPTX → HTML → React pipeline)
- Audit log viewer with filters and CSV/JSON export

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:37:17 +00:00

80 lines
No EOL
2.8 KiB
TypeScript

'use client'
import React, { useEffect, useState, useRef } from 'react';
interface ProgressBarProps {
duration: number;
onComplete?: () => void;
}
export const ProgressBar = ({ duration, onComplete }: ProgressBarProps) => {
const [progress, setProgress] = useState(0);
const progressInterval = useRef<NodeJS.Timeout | null>(null);
const startTime = useRef<number>(Date.now());
useEffect(() => {
const updateProgress = () => {
const currentTime = Date.now();
const elapsedTime = currentTime - startTime.current;
const calculatedProgress = (elapsedTime / (duration * 1000)) * 100;
if (calculatedProgress >= 95) {
setProgress(95);
if (progressInterval.current) {
clearInterval(progressInterval.current);
}
onComplete?.();
return;
}
// Slow down progress after 90%
if (calculatedProgress > 90) {
const remainingProgress = Math.min(99 - progress, 0.1);
setProgress(prev => prev + remainingProgress);
} else {
setProgress(Math.min(calculatedProgress, 90));
}
};
progressInterval.current = setInterval(updateProgress, 50);
return () => {
if (progressInterval.current) {
clearInterval(progressInterval.current);
}
};
}, [duration, onComplete]);
return (
<div className="w-full space-y-2">
<div className="flex justify-end items-center text-white/80 text-sm">
{/* <span>Processing...</span> */}
<span className='font-inter text-end font-medium text-xs'>{Math.round(progress)}%</span>
</div>
<div className="w-full bg-white rounded-full h-2 overflow-hidden">
<div
className="h-full bg-gradient-to-r from-[#9034EA] via-[#5146E5] to-[#9034EA] rounded-full animate-gradient transition-all duration-300 ease-out"
style={{
width: `${progress}%`,
backgroundSize: '200% 100%',
}}
/>
</div>
<style jsx>{`
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.animate-gradient {
animation: gradient 2s linear infinite;
}
`}</style>
</div>
);
};