Files
hr-performance-system/components/ErrorModal.jsx
donald c24634f4b7 Initial commit: HR Performance System
- Database schema with 31 tables for 4-card system
- LLM API integration (Gemini, DeepSeek, OpenAI)
- Error handling system with modal component
- Connection test UI for LLM services
- Environment configuration files
- Complete database documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 23:34:13 +08:00

128 lines
3.4 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 錯誤彈窗元件
* 統一顯示應用程式中的錯誤訊息
*/
import React, { useEffect, useState } from 'react';
import './ErrorModal.css';
const ErrorModal = ({ error, onClose, autoClose = true, duration = 5000 }) => {
const [isVisible, setIsVisible] = useState(false);
const [countdown, setCountdown] = useState(duration / 1000);
useEffect(() => {
if (error) {
setIsVisible(true);
setCountdown(duration / 1000);
if (autoClose) {
const timer = setTimeout(() => {
handleClose();
}, duration);
const countdownInterval = setInterval(() => {
setCountdown((prev) => Math.max(0, prev - 1));
}, 1000);
return () => {
clearTimeout(timer);
clearInterval(countdownInterval);
};
}
}
}, [error, autoClose, duration]);
const handleClose = () => {
setIsVisible(false);
setTimeout(() => {
if (onClose) onClose();
}, 300);
};
if (!error) return null;
const getSeverityClass = () => {
if (!error.statusCode) return 'error';
if (error.statusCode >= 500) return 'error';
if (error.statusCode >= 400) return 'warning';
return 'info';
};
const getSeverityIcon = () => {
const severity = getSeverityClass();
switch (severity) {
case 'error':
return '❌';
case 'warning':
return '⚠️';
case 'info':
return '';
default:
return '❌';
}
};
return (
<div className={`error-modal-overlay ${isVisible ? 'visible' : ''}`} onClick={handleClose}>
<div
className={`error-modal ${getSeverityClass()} ${isVisible ? 'visible' : ''}`}
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="error-modal-header">
<div className="error-modal-title">
<span className="error-icon">{getSeverityIcon()}</span>
<h3>{error.title || '錯誤'}</h3>
</div>
<button className="error-modal-close" onClick={handleClose} aria-label="關閉">
</button>
</div>
{/* Body */}
<div className="error-modal-body">
<p className="error-message">{error.message}</p>
{error.statusCode && (
<div className="error-metadata">
<span className="error-code">錯誤代碼: {error.statusCode}</span>
{error.timestamp && (
<span className="error-time">
時間: {new Date(error.timestamp).toLocaleString('zh-TW')}
</span>
)}
</div>
)}
{error.details && (
<details className="error-details">
<summary>詳細資訊</summary>
<pre>{JSON.stringify(error.details, null, 2)}</pre>
</details>
)}
{error.path && (
<div className="error-path">
<small>路徑: {error.path}</small>
</div>
)}
</div>
{/* Footer */}
<div className="error-modal-footer">
{autoClose && (
<span className="error-countdown">
{countdown > 0 ? `${countdown} 秒後自動關閉` : '關閉中...'}
</span>
)}
<button className="error-modal-button" onClick={handleClose}>
確定
</button>
</div>
</div>
</div>
);
};
export default ErrorModal;