feat: add GenerateModal component

This commit is contained in:
2026-02-28 20:07:34 +01:00
parent f7845c4c53
commit 627e6149fb

View File

@@ -0,0 +1,94 @@
import { useState } from 'react'
import Dialog from '@mui/material/Dialog'
import DialogTitle from '@mui/material/DialogTitle'
import DialogContent from '@mui/material/DialogContent'
import DialogActions from '@mui/material/DialogActions'
import Button from '@mui/material/Button'
import Tabs from '@mui/material/Tabs'
import Tab from '@mui/material/Tab'
import Box from '@mui/material/Box'
import IconButton from '@mui/material/IconButton'
import Tooltip from '@mui/material/Tooltip'
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
import DownloadIcon from '@mui/icons-material/Download'
import { configsApi } from '../api'
interface GeneratedFiles {
zones: string
interfaces: string
policy: string
rules: string
masq: string
}
interface Props {
open: boolean
configId: number
configName: string
onClose: () => void
}
const TABS = ['zones', 'interfaces', 'policy', 'rules', 'masq'] as const
export default function GenerateModal({ open, configId, configName, onClose }: Props) {
const [tab, setTab] = useState(0)
const [files, setFiles] = useState<GeneratedFiles | null>(null)
const [loading, setLoading] = useState(false)
const handleOpen = async () => {
if (files) return
setLoading(true)
try {
const res = await configsApi.generate(configId, 'json')
setFiles(res.data)
} finally {
setLoading(false)
}
}
const handleDownloadZip = async () => {
const res = await configsApi.generate(configId, 'zip')
const url = URL.createObjectURL(new Blob([res.data]))
const a = document.createElement('a')
a.href = url
a.download = `${configName}-shorewall.zip`
a.click()
URL.revokeObjectURL(url)
}
const handleCopy = (text: string) => navigator.clipboard.writeText(text)
if (open && !files && !loading) handleOpen()
const currentFile = files ? files[TABS[tab]] : ''
return (
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
<DialogTitle>Generated Shorewall Config {configName}</DialogTitle>
<DialogContent sx={{ p: 0 }}>
<Tabs value={tab} onChange={(_, v) => setTab(v)} sx={{ borderBottom: 1, borderColor: 'divider', px: 2 }}>
{TABS.map((t) => <Tab key={t} label={t} />)}
</Tabs>
<Box sx={{ position: 'relative', p: 2 }}>
<Tooltip title="Copy">
<IconButton size="small" sx={{ position: 'absolute', top: 16, right: 16 }} onClick={() => handleCopy(currentFile)}>
<ContentCopyIcon fontSize="small" />
</IconButton>
</Tooltip>
<Box
component="pre"
sx={{ fontFamily: 'monospace', fontSize: 13, bgcolor: '#1e293b', color: '#e2e8f0', p: 2, borderRadius: 1, overflowX: 'auto', minHeight: 300, whiteSpace: 'pre' }}
>
{loading ? 'Generating…' : currentFile}
</Box>
</Box>
</DialogContent>
<DialogActions>
<Button startIcon={<DownloadIcon />} onClick={handleDownloadZip} variant="outlined">
Download ZIP
</Button>
<Button onClick={onClose}>Close</Button>
</DialogActions>
</Dialog>
)
}