UNPKG

@dbs-portal/module-tenant-management

Version:

Tenant management and multi-tenancy support module

154 lines 13.5 kB
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