- Removed unnecessary React imports. - Adjusted package.json scripts for Electron integration. - Updated components to replace Next.js-specific imports with Electron-compatible alternatives. - Minor tsconfig.json changes for better compatibility.
135 lines
4.5 KiB
TypeScript
135 lines
4.5 KiB
TypeScript
'use client'
|
|
import {useEffect, useState, useRef} from 'react';
|
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
|
import {
|
|
faCheckCircle,
|
|
faExclamationCircle,
|
|
faInfoCircle,
|
|
faTimes,
|
|
faTimesCircle
|
|
} from '@fortawesome/free-solid-svg-icons';
|
|
|
|
interface StaticAlertProps {
|
|
type: 'success' | 'error' | 'info' | 'warning';
|
|
message: string;
|
|
onClose: () => void;
|
|
}
|
|
|
|
const iconMap = {
|
|
success: faCheckCircle,
|
|
error: faExclamationCircle,
|
|
info: faInfoCircle,
|
|
warning: faTimesCircle,
|
|
};
|
|
|
|
const bgColorMap = {
|
|
success: 'bg-success',
|
|
error: 'bg-error',
|
|
info: 'bg-info',
|
|
warning: 'bg-warning',
|
|
};
|
|
|
|
export default function StaticAlert(
|
|
{type, message, onClose}: StaticAlertProps) {
|
|
const [visible, setVisible] = useState(false);
|
|
const onCloseRef = useRef(onClose);
|
|
|
|
useEffect(() => {
|
|
onCloseRef.current = onClose;
|
|
}, [onClose]);
|
|
|
|
useEffect(() => {
|
|
setVisible(true);
|
|
const timer = setTimeout(() => {
|
|
setVisible(false);
|
|
setTimeout(() => onCloseRef.current(), 500); // Wait for fade out animation to complete
|
|
}, 4800);
|
|
|
|
return () => {
|
|
clearTimeout(timer);
|
|
};
|
|
}, []);
|
|
|
|
const handleClose = () => {
|
|
setVisible(false);
|
|
setTimeout(() => onCloseRef.current(), 1000); // Wait for fade out animation to complete
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`max-w-sm rounded-xl shadow-2xl transition-all duration-500 ease-in-out transform ${
|
|
visible ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0'
|
|
} overflow-hidden font-['Montserrat'] border border-secondary/50 backdrop-blur-sm`}
|
|
>
|
|
<div className={`p-4 ${bgColorMap[type]} flex items-center relative`}>
|
|
<div className="absolute top-0 left-0 w-full h-1 bg-white/30 rounded-t-xl"></div>
|
|
<div
|
|
className="mr-4 flex-shrink-0 rounded-full bg-white/20 p-2.5 text-text-primary flex items-center justify-center shadow-md">
|
|
<FontAwesomeIcon
|
|
icon={iconMap[type]}
|
|
size="lg"
|
|
className="animate-pulse"
|
|
style={{
|
|
animation: 'pulse 2s infinite'
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="flex-grow mr-3">
|
|
<div className="text-text-primary font-medium text-base">{message}</div>
|
|
</div>
|
|
<button
|
|
onClick={handleClose}
|
|
className="text-text-primary/90 hover:text-text-primary p-1.5 rounded-lg hover:bg-white/20 transition-all duration-300"
|
|
style={{
|
|
transition: 'all 0.3s ease',
|
|
}}
|
|
onMouseEnter={(e) => {
|
|
e.currentTarget.style.transform = 'rotate(90deg)';
|
|
}}
|
|
onMouseLeave={(e) => {
|
|
e.currentTarget.style.transform = 'rotate(0deg)';
|
|
}}
|
|
>
|
|
<FontAwesomeIcon icon={faTimes}/>
|
|
</button>
|
|
</div>
|
|
<div className="h-1.5 w-full bg-secondary/50 relative">
|
|
<div
|
|
className={`h-full ${
|
|
type === 'success' ? 'bg-success' :
|
|
type === 'error' ? 'bg-error' :
|
|
type === 'warning' ? 'bg-warning' :
|
|
'bg-info'
|
|
} shadow-sm`}
|
|
style={{
|
|
animation: 'shrink 5s linear forwards',
|
|
width: '100%'
|
|
}}
|
|
></div>
|
|
</div>
|
|
<style>{`
|
|
@keyframes pulse {
|
|
0% {
|
|
opacity: 0.7;
|
|
}
|
|
50% {
|
|
opacity: 1;
|
|
}
|
|
100% {
|
|
opacity: 0.7;
|
|
}
|
|
}
|
|
|
|
@keyframes shrink {
|
|
0% {
|
|
width: 100%;
|
|
}
|
|
100% {
|
|
width: 0%;
|
|
}
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|