178 lines
5.9 KiB
JavaScript
178 lines
5.9 KiB
JavaScript
import { useState, useEffect } from 'react';
|
||
import { triggerBackup, listBackups, restoreBackup } from '../backupApi';
|
||
import Modal from './Modal';
|
||
|
||
function AdminPanel({ onShowToast }) {
|
||
const [backups, setBackups] = useState([]);
|
||
const [loading, setLoading] = useState(false);
|
||
const [restoreModal, setRestoreModal] = useState({ isOpen: false, filename: '' });
|
||
const [restoring, setRestoring] = useState(false);
|
||
|
||
useEffect(() => {
|
||
loadBackups();
|
||
}, []);
|
||
|
||
const loadBackups = async () => {
|
||
try {
|
||
setLoading(true);
|
||
const data = await listBackups();
|
||
setBackups(data.backups || []);
|
||
} catch (error) {
|
||
onShowToast(error.message || 'שגיאה בטעינת גיבויים', 'error');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleCreateBackup = async () => {
|
||
try {
|
||
setLoading(true);
|
||
const result = await triggerBackup();
|
||
onShowToast('גיבוי נוצר בהצלחה! 📦', 'success');
|
||
loadBackups(); // Refresh list
|
||
} catch (error) {
|
||
onShowToast(error.message || 'שגיאה ביצירת גיבוי', 'error');
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleRestoreClick = (filename) => {
|
||
setRestoreModal({ isOpen: true, filename });
|
||
};
|
||
|
||
const handleRestoreConfirm = async () => {
|
||
console.log('Restore confirm clicked, filename:', restoreModal.filename);
|
||
setRestoreModal({ isOpen: false, filename: '' });
|
||
setRestoring(true);
|
||
|
||
try {
|
||
console.log('Starting restore...');
|
||
const result = await restoreBackup(restoreModal.filename);
|
||
console.log('Restore result:', result);
|
||
onShowToast('שחזור הושלם בהצלחה! ♻️ מרענן את הדף...', 'success');
|
||
|
||
// Refresh page after 2 seconds to reload all data
|
||
setTimeout(() => {
|
||
window.location.reload();
|
||
}, 2000);
|
||
} catch (error) {
|
||
console.error('Restore error:', error);
|
||
onShowToast(error.message || 'שגיאה בשחזור גיבוי', 'error');
|
||
setRestoring(false);
|
||
}
|
||
};
|
||
|
||
const formatBytes = (bytes) => {
|
||
if (bytes === 0) return '0 Bytes';
|
||
const k = 1024;
|
||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
|
||
};
|
||
|
||
const formatDate = (isoString) => {
|
||
const date = new Date(isoString);
|
||
return date.toLocaleString('he-IL', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
};
|
||
|
||
return (
|
||
<div className="admin-panel">
|
||
<div className="admin-header">
|
||
<h2>ניהול גיבויים 🛡️</h2>
|
||
<button
|
||
className="btn primary"
|
||
onClick={handleCreateBackup}
|
||
disabled={loading}
|
||
>
|
||
{loading ? 'יוצר גיבוי...' : 'צור גיבוי חדש'}
|
||
</button>
|
||
</div>
|
||
|
||
{loading && backups.length === 0 ? (
|
||
<div className="loading">טוען גיבויים...</div>
|
||
) : backups.length === 0 ? (
|
||
<div className="empty-state">אין גיבויים זמינים</div>
|
||
) : (
|
||
<div className="backups-list">
|
||
<table className="backups-table">
|
||
<thead>
|
||
<tr>
|
||
<th className="col-filename">קובץ</th>
|
||
<th className="col-date">תאריך</th>
|
||
<th className="col-size">גודל</th>
|
||
<th className="col-actions">פעולות</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{backups.map((backup) => (
|
||
<tr key={backup.filename}>
|
||
<td className="filename">{backup.filename}</td>
|
||
<td className="date">{formatDate(backup.last_modified)}</td>
|
||
<td className="size">{formatBytes(backup.size)}</td>
|
||
<td className="actions">
|
||
<button
|
||
className="btn ghost small"
|
||
onClick={() => handleRestoreClick(backup.filename)}
|
||
disabled={loading}
|
||
>
|
||
שחזר
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
)}
|
||
|
||
<Modal
|
||
isOpen={restoreModal.isOpen || restoring}
|
||
onClose={() => !restoring && setRestoreModal({ isOpen: false, filename: '' })}
|
||
title={restoring ? "⏳ משחזר גיבוי..." : "⚠️ אישור שחזור גיבוי"}
|
||
>
|
||
{restoring ? (
|
||
<div className="restore-progress">
|
||
<div className="progress-bar-container">
|
||
<div className="progress-bar-fill"></div>
|
||
</div>
|
||
<p className="progress-text">מוריד גיבוי...</p>
|
||
<p className="progress-text">משחזר מסד נתונים...</p>
|
||
<p className="progress-text-muted">אנא המתן, התהליך עשוי לקחת מספר דקות</p>
|
||
</div>
|
||
) : (
|
||
<div className="restore-warning">
|
||
<p>פעולה זו תמחק את כל הנתונים הנוכחיים!</p>
|
||
<p>האם אתה בטוח שברצונך לשחזר מהגיבוי:</p>
|
||
<p className="filename-highlight">{restoreModal.filename}</p>
|
||
<div className="modal-actions">
|
||
<button
|
||
className="btn ghost"
|
||
onClick={() => setRestoreModal({ isOpen: false, filename: '' })}
|
||
disabled={loading}
|
||
>
|
||
ביטול
|
||
</button>
|
||
<button
|
||
className="btn danger"
|
||
onClick={handleRestoreConfirm}
|
||
disabled={loading}
|
||
>
|
||
שחזר
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</Modal>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default AdminPanel;
|