@dbs-portal/module-tenant-management
Version:
Tenant management and multi-tenancy support module
154 lines • 13.5 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* TenantBackupManager Component - Manage tenant backups and restore operations
*/
import React, { useState } from 'react';
import { Card, List, Button, Space, Typography, Tag, Modal, Form, Input, Select, Progress, Alert, Tooltip, Badge, Row, Col, Statistic, Popconfirm, Upload, message } from 'antd';
import { CloudDownloadOutlined, CloudUploadOutlined, DeleteOutlined, ReloadOutlined, HistoryOutlined, ExclamationCircleOutlined, CheckCircleOutlined, ClockCircleOutlined, InboxOutlined, DownloadOutlined } from '@ant-design/icons';
import { format, formatDistanceToNow } from 'date-fns';
import { useTenantBackups, useCreateTenantBackup, useRestoreTenantFromBackup, useDeleteTenantBackup } from '../hooks';
const { Title, Text } = Typography;
const { Option } = Select;
const { TextArea } = Input;
const { Dragger } = Upload;
export const TenantBackupManager = ({ tenantId, onBackupCreate, onBackupRestore, className }) => {
const [showCreateModal, setShowCreateModal] = useState(false);
const [showRestoreModal, setShowRestoreModal] = useState(false);
const [selectedBackup, setSelectedBackup] = useState(null);
const [form] = Form.useForm();
// Fetch backups
const { data: backups = [], isLoading, refetch } = useTenantBackups(tenantId);
// Mutations
const createBackupMutation = useCreateTenantBackup();
const restoreBackupMutation = useRestoreTenantFromBackup();
const deleteBackupMutation = useDeleteTenantBackup();
const handleCreateBackup = async (values) => {
try {
await createBackupMutation.mutateAsync({
id: tenantId,
name: values.name,
description: values.description,
options: {
type: values.type,
includeSettings: values.includeSettings,
includeData: values.includeData,
includeFiles: values.includeFiles
}
});
onBackupCreate?.();
setShowCreateModal(false);
form.resetFields();
message.success('Backup creation started');
}
catch (error) {
console.error('Failed to create backup:', error);
message.error('Failed to create backup');
}
};
const handleRestoreBackup = async (backup) => {
try {
await restoreBackupMutation.mutateAsync({
tenantId,
backupId: backup.id
});
onBackupRestore?.(backup);
setShowRestoreModal(false);
setSelectedBackup(null);
message.success('Backup restoration started');
}
catch (error) {
console.error('Failed to restore backup:', error);
message.error('Failed to restore backup');
}
};
const handleDeleteBackup = async (backupId) => {
try {
await deleteBackupMutation.mutateAsync({ tenantId, backupId });
message.success('Backup deleted successfully');
}
catch (error) {
console.error('Failed to delete backup:', error);
message.error('Failed to delete backup');
}
};
const handleDownloadBackup = async (backup) => {
try {
// Use the downloadUrl if available
if (backup.downloadUrl) {
const link = document.createElement('a');
link.href = backup.downloadUrl;
link.download = `${backup.name}-${format(new Date(backup.createdAt), 'yyyy-MM-dd')}.zip`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
message.success('Backup download started');
}
else {
message.error('Download URL not available');
}
}
catch (error) {
console.error('Failed to download backup:', error);
message.error('Failed to download backup');
}
};
const getBackupStatusIcon = (status) => {
switch (status) {
case 'completed':
return _jsx(CheckCircleOutlined, { style: { color: '#52c41a' } });
case 'failed':
return _jsx(ExclamationCircleOutlined, { style: { color: '#ff4d4f' } });
case 'in_progress':
return _jsx(ClockCircleOutlined, { style: { color: '#1890ff' } });
default:
return _jsx(ClockCircleOutlined, { style: { color: '#d9d9d9' } });
}
};
const getBackupStatusColor = (status) => {
switch (status) {
case 'completed': return 'success';
case 'failed': return 'error';
case 'in_progress': return 'processing';
default: return 'default';
}
};
const formatFileSize = (bytes) => {
if (bytes === 0)
return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
// Calculate statistics
const totalBackups = backups.length;
const completedBackups = backups.filter(b => b.status === 'completed').length;
const totalSize = backups.reduce((sum, backup) => sum + (backup.size || 0), 0);
const latestBackup = backups.find(b => b.status === 'completed');
return (_jsxs("div", { className: className, children: [_jsxs(Card, { title: _jsxs(Space, { children: [_jsx(CloudDownloadOutlined, {}), _jsx(Title, { level: 4, style: { margin: 0 }, children: "Backup Management" })] }), extra: _jsxs(Space, { children: [_jsx(Button, { icon: _jsx(ReloadOutlined, {}), onClick: () => refetch(), loading: isLoading, children: "Refresh" }), _jsx(Button, { type: "primary", icon: _jsx(CloudUploadOutlined, {}), onClick: () => setShowCreateModal(true), loading: createBackupMutation.isPending, children: "Create Backup" })] }), children: [_jsxs(Row, { gutter: 16, style: { marginBottom: 16 }, children: [_jsx(Col, { span: 6, children: _jsx(Statistic, { title: "Total Backups", value: totalBackups, prefix: _jsx(CloudDownloadOutlined, {}) }) }), _jsx(Col, { span: 6, children: _jsx(Statistic, { title: "Completed", value: completedBackups, valueStyle: { color: '#3f8600' } }) }), _jsx(Col, { span: 6, children: _jsx(Statistic, { title: "Total Size", value: formatFileSize(totalSize), prefix: _jsx(InboxOutlined, {}) }) }), _jsx(Col, { span: 6, children: _jsx(Statistic, { title: "Latest Backup", value: latestBackup ? formatDistanceToNow(new Date(latestBackup.createdAt)) + ' ago' : 'None', prefix: _jsx(HistoryOutlined, {}) }) })] }), backups.length === 0 ? (_jsx(Alert, { message: "No backups found", description: "Create your first backup to ensure data safety and enable restore capabilities.", type: "info", showIcon: true, action: _jsx(Button, { type: "primary", onClick: () => setShowCreateModal(true), children: "Create Backup" }) })) : (_jsx(List, { dataSource: backups, renderItem: (backup) => (_jsx(List.Item, { actions: [
_jsx(Tooltip, { title: "Download backup", children: _jsx(Button, { icon: _jsx(DownloadOutlined, {}), size: "small", disabled: backup.status !== 'completed', loading: false, onClick: () => handleDownloadBackup(backup) }) }, "download"),
_jsx(Tooltip, { title: "Restore from backup", children: _jsx(Button, { icon: _jsx(HistoryOutlined, {}), size: "small", disabled: backup.status !== 'completed', onClick: () => {
setSelectedBackup(backup);
setShowRestoreModal(true);
}, children: "Restore" }) }, "restore"),
_jsx(Popconfirm, { title: "Delete Backup", description: "Are you sure you want to delete this backup? This action cannot be undone.", onConfirm: () => handleDeleteBackup(backup.id), okText: "Delete", okType: "danger", children: _jsx(Button, { icon: _jsx(DeleteOutlined, {}), size: "small", danger: true, loading: deleteBackupMutation.isPending }) }, "delete")
], children: _jsx(List.Item.Meta, { avatar: _jsx(Badge, { status: getBackupStatusColor(backup.status), children: getBackupStatusIcon(backup.status) }), title: _jsxs(Space, { children: [_jsx(Text, { strong: true, children: backup.name }), _jsx(Tag, { color: getBackupStatusColor(backup.status), children: backup.status.replace('_', ' ').toUpperCase() }), _jsx(Tag, { color: "blue", children: backup.type?.toUpperCase() || 'FULL' })] }), description: _jsxs(Space, { direction: "vertical", size: "small", children: [backup.description && _jsx(Text, { children: backup.description }), _jsxs(Space, { children: [_jsxs(Text, { type: "secondary", children: ["Created: ", format(new Date(backup.createdAt), 'PPP p')] }), backup.size && (_jsxs(Text, { type: "secondary", children: ["Size: ", formatFileSize(backup.size)] }))] }), backup.progress !== undefined && backup.status === 'in_progress' && (_jsx(Progress, { percent: backup.progress, size: "small", status: "active" })), backup.error && (_jsx(Alert, { message: backup.error, type: "error", showIcon: true }))] }) }) })) }))] }), _jsx(Modal, { title: "Create Backup", open: showCreateModal, onCancel: () => {
setShowCreateModal(false);
form.resetFields();
}, footer: null, width: 600, children: _jsxs(Form, { form: form, layout: "vertical", onFinish: handleCreateBackup, initialValues: {
type: 'full',
includeSettings: true,
includeData: true,
includeFiles: false
}, children: [_jsx(Form.Item, { name: "name", label: "Backup Name", rules: [{ required: true, message: 'Please enter backup name' }], children: _jsx(Input, { placeholder: "e.g., Weekly Backup" }) }), _jsx(Form.Item, { name: "description", label: "Description", children: _jsx(TextArea, { rows: 3, placeholder: "Optional description for this backup" }) }), _jsx(Form.Item, { name: "type", label: "Backup Type", rules: [{ required: true }], children: _jsxs(Select, { children: [_jsx(Option, { value: "full", children: "Full Backup" }), _jsx(Option, { value: "incremental", children: "Incremental Backup" }), _jsx(Option, { value: "differential", children: "Differential Backup" })] }) }), _jsx(Form.Item, { label: "Include in Backup", children: _jsxs(Space, { direction: "vertical", children: [_jsxs(Form.Item, { name: "includeSettings", valuePropName: "checked", style: { margin: 0 }, children: [_jsx("input", { type: "checkbox" }), " Include tenant settings and configuration"] }), _jsxs(Form.Item, { name: "includeData", valuePropName: "checked", style: { margin: 0 }, children: [_jsx("input", { type: "checkbox" }), " Include tenant data and content"] }), _jsxs(Form.Item, { name: "includeFiles", valuePropName: "checked", style: { margin: 0 }, children: [_jsx("input", { type: "checkbox" }), " Include uploaded files and media"] })] }) }), _jsxs(Row, { justify: "end", gutter: 8, children: [_jsx(Col, { children: _jsx(Button, { onClick: () => {
setShowCreateModal(false);
form.resetFields();
}, children: "Cancel" }) }), _jsx(Col, { children: _jsx(Button, { type: "primary", htmlType: "submit", icon: _jsx(CloudUploadOutlined, {}), loading: createBackupMutation.isPending, children: "Create Backup" }) })] })] }) }), _jsx(Modal, { title: "Restore Backup", open: showRestoreModal, onCancel: () => {
setShowRestoreModal(false);
setSelectedBackup(null);
}, footer: null, width: 500, children: selectedBackup && (_jsxs("div", { children: [_jsx(Alert, { message: "Warning: Restore Operation", description: "Restoring from a backup will replace current tenant data. This action cannot be undone. Please ensure you have a recent backup before proceeding.", type: "warning", showIcon: true, style: { marginBottom: 16 } }), _jsxs(Space, { direction: "vertical", size: "large", style: { width: '100%' }, children: [_jsxs("div", { children: [_jsx(Text, { strong: true, children: "Backup Details:" }), _jsxs("ul", { style: { marginTop: 8 }, children: [_jsxs("li", { children: ["Name: ", selectedBackup.name] }), _jsxs("li", { children: ["Created: ", format(new Date(selectedBackup.createdAt), 'PPP p')] }), _jsxs("li", { children: ["Size: ", formatFileSize(selectedBackup.size || 0)] }), _jsxs("li", { children: ["Type: ", selectedBackup.type?.toUpperCase() || 'FULL'] })] })] }), _jsxs(Row, { justify: "end", gutter: 8, children: [_jsx(Col, { children: _jsx(Button, { onClick: () => {
setShowRestoreModal(false);
setSelectedBackup(null);
}, children: "Cancel" }) }), _jsx(Col, { children: _jsx(Button, { type: "primary", danger: true, icon: _jsx(HistoryOutlined, {}), onClick: () => handleRestoreBackup(selectedBackup), loading: restoreBackupMutation.isPending, children: "Restore Backup" }) })] })] })] })) })] }));
};
//# sourceMappingURL=TenantBackupManager.js.map