Compare commits

...

2 Commits

21 changed files with 176 additions and 138 deletions

View File

@@ -0,0 +1,18 @@
import DialogContent from '@mui/material/DialogContent'
import { DialogContentProps } from '@mui/material/DialogContent'
export default function FormDialogContent(props: DialogContentProps) {
return (
<DialogContent
{...props}
sx={[
{
display: 'grid',
gap: 1.5,
pt: '8px !important'
},
props.sx
]}
/>
)
}

View File

@@ -1,4 +1,5 @@
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -254,7 +255,7 @@ export default function AdminApiKeysPage() {
</SectionCard>
<Dialog open={Boolean(deleteTarget)} onClose={() => setDeleteTarget(null)} maxWidth="xs" fullWidth>
<DialogTitle>Revoke API Key</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Type the API key name to confirm revocation.
</Typography>
@@ -264,7 +265,7 @@ export default function AdminApiKeysPage() {
value={deleteConfirm}
onChange={(event) => setDeleteConfirm(event.target.value)}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => { setDeleteTarget(null); setDeleteConfirm('') }}>
Cancel
@@ -281,7 +282,7 @@ export default function AdminApiKeysPage() {
</Dialog>
<Dialog open={bulkOpen} onClose={() => setBulkOpen(false)} maxWidth="xs" fullWidth>
<DialogTitle>Revoke Selected API Keys</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Type DELETE to revoke {selected.length} selected API key(s).
</Typography>
@@ -291,7 +292,7 @@ export default function AdminApiKeysPage() {
value={bulkConfirm}
onChange={(event) => setBulkConfirm(event.target.value)}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => { setBulkOpen(false); setBulkConfirm('') }}>
Cancel

View File

@@ -1,5 +1,6 @@
import AddIcon from '@mui/icons-material/Add'
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Autocomplete,
Box,
@@ -264,7 +265,7 @@ export default function AdminPKIClientProfilesPage() {
<Dialog open={editOpen} onClose={() => { if (!busy) setEditOpen(false) }} fullWidth maxWidth="md">
<DialogTitle>{editID ? 'Edit Client Certificate Profile' : 'New Client Certificate Profile'}</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1.5, pt: '8px !important' }}>
<FormDialogContent>
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
<TextField label="Name" value={name} onChange={(event) => setName(event.target.value)} autoFocus />
<TextField select label="Issuer CA" value={caID} onChange={(event) => setCAID(event.target.value)}>
@@ -327,7 +328,7 @@ export default function AdminPKIClientProfilesPage() {
renderInput={(params) => <TextField {...params} label="Allowed Groups" />}
/>
<FormControlLabel control={<Checkbox checked={enabled} onChange={(event) => setEnabled(event.target.checked)} />} label="Enabled" />
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setEditOpen(false)} disabled={busy}>Cancel</Button>
<Button variant="contained" onClick={save} disabled={busy}>
@@ -338,14 +339,14 @@ export default function AdminPKIClientProfilesPage() {
<Dialog open={Boolean(deleteItem)} onClose={() => { if (!busy) { setDeleteItem(null); setDeleteConfirm('') } }} fullWidth maxWidth="sm">
<DialogTitle>Delete Client Certificate Profile</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1.5, pt: '8px !important' }}>
<FormDialogContent>
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
<Typography variant="body2" color="text.secondary">
Type the profile name to confirm deletion.
</Typography>
<Typography variant="body2">{deleteItem?.name}</Typography>
<TextField label="Confirm Name" value={deleteConfirm} onChange={(event) => setDeleteConfirm(event.target.value)} autoFocus />
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => { setDeleteItem(null); setDeleteConfirm('') }} disabled={busy}>Cancel</Button>
<Button

View File

@@ -1,5 +1,6 @@
import DownloadIcon from '@mui/icons-material/Download'
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -821,7 +822,7 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="Name" value={acmeName} onChange={(event) => setACMEName(event.target.value)} />
<TextField label="Directory URL" value={acmeDirectoryURL} onChange={(event) => setACMEDirectoryURL(event.target.value)} />
@@ -867,7 +868,7 @@ export default function AdminPKIPage() {
) : null}
<FormControlLabel control={<Checkbox checked={acmeEnabled} onChange={(event) => setACMEEnabled(event.target.checked)} />} label="Enabled" />
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setACMEOpen(false)}>Cancel</Button>
<Button
@@ -893,9 +894,9 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2" color="text.secondary">Delete profile permanently?</Typography>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setACMEDeleteID('')}>Cancel</Button>
<Button color="error" variant="contained" onClick={deleteACME} disabled={busy}>{busy ? 'Working...' : 'Delete'}</Button>
@@ -943,7 +944,7 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField
select
@@ -962,7 +963,7 @@ export default function AdminPKIPage() {
onChange={(event) => setACMEOrderSANDNS(event.target.value)}
/>
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setACMEOrderOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={createACMEOrder} disabled={busy || !acmeOrderProfileID || !acmeOrderCommonName.trim()}>
@@ -978,9 +979,9 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2" color="text.secondary">Delete order permanently?</Typography>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setACMEOrderDeleteID('')}>Cancel</Button>
<Button color="error" variant="contained" onClick={deleteACMEOrder} disabled={busy}>{busy ? 'Working...' : 'Delete'}</Button>
@@ -994,7 +995,7 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1 }}>
<Typography variant="body2" color="text.secondary">
Choose how to store the renewed certificate.
@@ -1010,7 +1011,7 @@ export default function AdminPKIPage() {
<MenuItem value="new_cert">Create a new cert ID</MenuItem>
</TextField>
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setACMEFinalizeID('')}>Cancel</Button>
<Button
@@ -1025,7 +1026,7 @@ export default function AdminPKIPage() {
<Dialog open={Boolean(acmeOrderView)} onClose={() => setACMEOrderView(null)} maxWidth="md" fullWidth>
<DialogTitle>DNS-01 Instructions</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="Order ID" value={acmeOrderView?.id || ''} InputProps={{ readOnly: true }} />
<TextField label="Status" value={acmeOrderView?.status || ''} InputProps={{ readOnly: true }} />
@@ -1044,7 +1045,7 @@ export default function AdminPKIPage() {
</Box>
))}
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
{acmeOrderView ? (
<Button variant="contained" onClick={() => requestFinalizeACMEOrder(acmeOrderView)} disabled={busy || acmeOrderView.status === 'valid'}>
@@ -1062,7 +1063,7 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="Name" value={rootName} onChange={(event) => setRootName(event.target.value)} />
<TextField label="Common Name" value={rootCN} onChange={(event) => setRootCN(event.target.value)} />
@@ -1096,7 +1097,7 @@ export default function AdminPKIPage() {
</Button>
</Box>
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setRootOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={createRoot} disabled={busy}>{busy ? 'Saving...' : 'Create'}</Button>
@@ -1110,7 +1111,7 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="Name" value={interName} onChange={(event) => setInterName(event.target.value)} />
<TextField
@@ -1154,7 +1155,7 @@ export default function AdminPKIPage() {
</Button>
</Box>
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setInterOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={createIntermediate} disabled={busy}>{busy ? 'Saving...' : 'Create'}</Button>
@@ -1168,7 +1169,7 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField select label="Issuer CA" value={issueCA} onChange={(event) => setIssueCA(event.target.value)}>
{cas.map((ca) => (
@@ -1181,7 +1182,7 @@ export default function AdminPKIPage() {
<TextField label="Validity Days" value={issueDays} onChange={(event) => setIssueDays(event.target.value)} />
<FormControlLabel control={<Checkbox checked={issueIsCA} onChange={(event) => setIssueIsCA(event.target.checked)} />} label="Issue as CA certificate" />
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setIssueOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={issueCert} disabled={busy}>{busy ? 'Saving...' : 'Issue'}</Button>
@@ -1195,7 +1196,7 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField select label="Issuer CA (optional)" value={importCA} onChange={(event) => setImportCA(event.target.value)}>
<MenuItem value="">(none, standalone)</MenuItem>
@@ -1232,7 +1233,7 @@ export default function AdminPKIPage() {
</Button>
</Box>
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setImportOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={importCert} disabled={busy}>{busy ? 'Saving...' : 'Import'}</Button>
@@ -1246,9 +1247,9 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<TextField fullWidth label="Reason (optional)" value={revokeReason} onChange={(event) => setRevokeReason(event.target.value)} />
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setRevokeID('')}>Cancel</Button>
<Button color="warning" variant="contained" onClick={revokeCert} disabled={busy}>{busy ? 'Working...' : 'Revoke'}</Button>
@@ -1262,9 +1263,9 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2" color="text.secondary">Delete certificate permanently?</Typography>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDeleteID('')}>Cancel</Button>
<Button color="error" variant="contained" onClick={deleteCert} disabled={busy}>{busy ? 'Working...' : 'Delete'}</Button>
@@ -1278,7 +1279,7 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Type the CA name to confirm deletion.
</Typography>
@@ -1287,7 +1288,7 @@ export default function AdminPKIPage() {
control={<Checkbox checked={deleteCAForce} onChange={(event) => setDeleteCAForce(event.target.checked)} />}
label="Force delete (includes child CAs and issued certs)"
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDeleteCAID('')}>Cancel</Button>
<Button
@@ -1303,7 +1304,7 @@ export default function AdminPKIPage() {
<Dialog open={Boolean(viewCA)} onClose={() => setViewCA(null)} maxWidth="md" fullWidth>
<DialogTitle>CA Details</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="ID" value={viewCA?.id || ''} InputProps={{ readOnly: true }} />
<TextField label="Name" value={viewCA?.name || ''} InputProps={{ readOnly: true }} />
@@ -1326,7 +1327,7 @@ export default function AdminPKIPage() {
sx={{ '& .MuiInputBase-input': { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace' } }}
/>
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button
startIcon={<DownloadIcon />}
@@ -1367,7 +1368,7 @@ export default function AdminPKIPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<TextField
fullWidth
label="Name"
@@ -1375,7 +1376,7 @@ export default function AdminPKIPage() {
onChange={(event) => setEditCAName(event.target.value)}
sx={{ mt: 1 }}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setEditCAID('')}>Cancel</Button>
<Button variant="contained" onClick={saveCAEdit} disabled={busy || !editCAName.trim()}>
@@ -1386,7 +1387,7 @@ export default function AdminPKIPage() {
<Dialog open={Boolean(viewCert)} onClose={() => setViewCert(null)} maxWidth="md" fullWidth>
<DialogTitle>Certificate Details</DialogTitle>
<DialogContent>
<FormDialogContent>
{dialogError ? <Alert severity="error" sx={{ mb: 1 }}>{dialogError}</Alert> : null}
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="ID" value={viewCert?.id || ''} InputProps={{ readOnly: true }} />
@@ -1425,7 +1426,7 @@ export default function AdminPKIPage() {
/>
) : null}
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={renewCertWithACME} disabled={busy || !viewCert}>
{busy ? 'Working...' : 'Renew via ACME'}

View File

@@ -1,5 +1,6 @@
import AddIcon from '@mui/icons-material/Add'
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Autocomplete,
Box,
@@ -317,7 +318,7 @@ export default function AdminSSHPrincipalGrantsPage() {
<Dialog open={editOpen} onClose={() => { if (!busy) setEditOpen(false) }} maxWidth="md" fullWidth>
<DialogTitle>{editID ? 'Edit Principal Grant' : 'New Principal Grant'}</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1.25, mt: 1 }}>
<FormDialogContent>
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
<TextField label="Principal" value={principal} onChange={(event) => setPrincipal(event.target.value)} />
<Autocomplete
@@ -365,7 +366,7 @@ export default function AdminSSHPrincipalGrantsPage() {
label="Disabled"
/>
<TextField label="Reason (optional)" value={reason} onChange={(event) => setReason(event.target.value)} />
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setEditOpen(false)} disabled={busy}>Cancel</Button>
<Button variant="contained" onClick={save} disabled={busy || !principal.trim() || (targetUserIDs.length + targetGroupIDs.length) === 0}>
@@ -376,7 +377,7 @@ export default function AdminSSHPrincipalGrantsPage() {
<Dialog open={!!deleteItem} onClose={() => { if (!busy) setDeleteItem(null) }} maxWidth="xs" fullWidth>
<DialogTitle>Delete Principal Grant</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1, mt: 1 }}>
<FormDialogContent>
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
<Typography variant="body2">
Type the principal name to confirm deletion.
@@ -391,7 +392,7 @@ export default function AdminSSHPrincipalGrantsPage() {
value={deleteConfirm}
onChange={(event) => setDeleteConfirm(event.target.value)}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDeleteItem(null)} disabled={busy}>Cancel</Button>
<Button

View File

@@ -1,4 +1,5 @@
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -159,7 +160,7 @@ export default function AdminSSHSignHistoryPage() {
<Dialog open={inspectOpen} onClose={() => setInspectOpen(false)} maxWidth="md" fullWidth>
<DialogTitle>Certificate Inspect</DialogTitle>
<DialogContent>
<FormDialogContent>
<TextField
multiline
minRows={12}
@@ -168,7 +169,7 @@ export default function AdminSSHSignHistoryPage() {
fullWidth
sx={{ mt: 1, '& .MuiInputBase-input': { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace' } }}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setInspectOpen(false)}>Close</Button>
</DialogActions>

View File

@@ -1,6 +1,7 @@
import AddIcon from '@mui/icons-material/Add'
import DownloadIcon from '@mui/icons-material/Download'
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -339,7 +340,7 @@ export default function AdminSSHUserCAPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="Name" value={name} onChange={(event) => setName(event.target.value)} />
<FormControlLabel control={<Checkbox checked={enabled} onChange={(event) => setEnabled(event.target.checked)} />} label="Enabled" />
@@ -394,7 +395,7 @@ export default function AdminSSHUserCAPage() {
</Box>
) : null}
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setEditOpen(false)}>Cancel</Button>
<Button
@@ -414,12 +415,12 @@ export default function AdminSSHUserCAPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1 }}>
<Typography variant="body2" color="text.secondary">Type CA name to confirm deletion.</Typography>
<TextField size="small" label="Confirm Name" value={deleteConfirm} onChange={(event) => setDeleteConfirm(event.target.value)} />
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDeleteItem(null)}>Cancel</Button>
<Button
@@ -435,7 +436,7 @@ export default function AdminSSHUserCAPage() {
<Dialog open={Boolean(viewItem)} onClose={() => setViewItem(null)} maxWidth="md" fullWidth>
<DialogTitle>SSH User CA Details</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="Name" value={viewItem?.name || ''} InputProps={{ readOnly: true }} />
<TextField label="ID" value={viewItem?.id || ''} InputProps={{ readOnly: true }} />
@@ -452,7 +453,7 @@ export default function AdminSSHUserCAPage() {
sx={{ '& .MuiInputBase-input': { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace' } }}
/>
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
{viewItem ? <Button startIcon={<DownloadIcon />} onClick={() => downloadPublicKey(viewItem)}>Download Public</Button> : null}
{viewItem ? <Button startIcon={<DownloadIcon />} color="warning" onClick={() => downloadPrivateKey(viewItem)}>Download Private</Button> : null}
@@ -467,7 +468,7 @@ export default function AdminSSHUserCAPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="CA" value={signItem ? `${signItem.name} (${signItem.id})` : ''} InputProps={{ readOnly: true }} />
<TextField
@@ -501,7 +502,7 @@ export default function AdminSSHUserCAPage() {
</Box>
) : null}
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setSignOpen(false)}>Close</Button>
{signResult ? <Button onClick={copyResultCert}>Copy Certificate</Button> : null}
@@ -518,7 +519,7 @@ export default function AdminSSHUserCAPage() {
<Dialog open={inspectOpen} onClose={() => setInspectOpen(false)} maxWidth="md" fullWidth>
<DialogTitle>Certificate Inspect</DialogTitle>
<DialogContent>
<FormDialogContent>
<TextField
multiline
minRows={12}
@@ -527,7 +528,7 @@ export default function AdminSSHUserCAPage() {
fullWidth
sx={{ mt: 1, '& .MuiInputBase-input': { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace' } }}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setInspectOpen(false)}>Close</Button>
</DialogActions>

View File

@@ -1,4 +1,5 @@
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -469,10 +470,10 @@ export default function AdminServicePrincipalsPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1, pt: '8px !important' }}>
<FormDialogContent>
<TextField label="Name" value={name} onChange={(event) => setName(event.target.value)} />
<TextField label="Description" value={description} onChange={(event) => setDescription(event.target.value)} />
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setCreateOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={createPrincipal} disabled={busy}>{busy ? 'Saving...' : 'Create'}</Button>
@@ -486,7 +487,7 @@ export default function AdminServicePrincipalsPage() {
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1, pt: '8px !important' }}>
<FormDialogContent>
<TextField
select
label="Binding Source"
@@ -527,7 +528,7 @@ export default function AdminServicePrincipalsPage() {
<MenuItem key={item.id} value={item.id}>{item.name} ({item.id})</MenuItem>
))}
</TextField>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setBindingOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={upsertBinding} disabled={busy}>{busy ? 'Saving...' : 'Save'}</Button>

View File

@@ -1,4 +1,5 @@
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -537,7 +538,7 @@ export default function AdminTLSSettingsPage() {
{listenerError ? <Alert severity="error">{listenerError}</Alert> : null}
</Box>
</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1, pt: '8px !important' }}>
<FormDialogContent>
{!editingMain ? (
<TextField
label="Name"
@@ -719,7 +720,7 @@ export default function AdminTLSSettingsPage() {
<MenuItem value="1.2">1.2</MenuItem>
<MenuItem value="1.3">1.3</MenuItem>
</TextField>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDialogOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={handleSaveDialog} disabled={listenerSaving}>
@@ -730,9 +731,9 @@ export default function AdminTLSSettingsPage() {
<Dialog open={confirmOpen} onClose={() => setConfirmOpen(false)} maxWidth="xs" fullWidth>
<DialogTitle>{confirmTitle}</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2">{confirmMessage}</Typography>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setConfirmOpen(false)}>Cancel</Button>
<Button variant="contained" color={confirmColor} onClick={handleConfirm}>

View File

@@ -2,6 +2,7 @@ import AddIcon from '@mui/icons-material/Add'
import DeleteIcon from '@mui/icons-material/Delete'
import EditIcon from '@mui/icons-material/Edit'
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -392,9 +393,9 @@ export default function AdminUserGroupsPage() {
sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}
>
<Typography variant="body2">{userLabelByID(member.user_id)}</Typography>
<IconButton size="small" color="error" onClick={() => removeMember(member.user_id)} disabled={busy}>
<DeleteIcon fontSize="small" />
</IconButton>
<Button size="small" variant="outlined" color="error" onClick={() => removeMember(member.user_id)} disabled={busy}>
Delete
</Button>
</Box>
))}
{members.length === 0 ? (
@@ -434,7 +435,7 @@ export default function AdminUserGroupsPage() {
<Dialog open={editOpen} onClose={() => setEditOpen(false)} maxWidth="sm" fullWidth>
<DialogTitle>{editID ? 'Edit Group' : 'New Group'}</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1, mt: 1 }}>
<FormDialogContent>
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
<TextField label="Name" value={name} onChange={(event) => setName(event.target.value)} />
<TextField
@@ -448,7 +449,7 @@ export default function AdminUserGroupsPage() {
control={<Checkbox checked={disabled} onChange={(event) => setDisabled(event.target.checked)} />}
label="Disabled"
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setEditOpen(false)}>Cancel</Button>
<Button variant="contained" onClick={saveGroup} disabled={busy || !name.trim()}>
@@ -459,7 +460,7 @@ export default function AdminUserGroupsPage() {
<Dialog open={!!deleteItem} onClose={() => setDeleteItem(null)} maxWidth="xs" fullWidth>
<DialogTitle>Delete Group</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1, mt: 1 }}>
<FormDialogContent>
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
<Typography variant="body2">Type the group name to confirm deletion.</Typography>
<TextField
@@ -469,7 +470,7 @@ export default function AdminUserGroupsPage() {
value={deleteConfirm}
onChange={(event) => setDeleteConfirm(event.target.value)}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDeleteItem(null)}>Cancel</Button>
<Button

View File

@@ -1,5 +1,6 @@
import AddIcon from '@mui/icons-material/Add'
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -278,7 +279,7 @@ export default function AdminUsersPage() {
</SectionCard>
<Dialog open={createOpen} onClose={closeCreateDialog} maxWidth="sm" fullWidth>
<DialogTitle>New User</DialogTitle>
<DialogContent>
<FormDialogContent>
{createError ? <Alert severity="error" sx={{ mb: 1 }}>{createError}</Alert> : null}
<Box component="form" onSubmit={handleCreate} sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField name="username" label="Username" value={username} onChange={(event) => setUsername(event.target.value)} />
@@ -320,11 +321,11 @@ export default function AdminUsersPage() {
</Button>
</DialogActions>
</Box>
</DialogContent>
</FormDialogContent>
</Dialog>
<Dialog open={Boolean(editUser)} onClose={closeEditUser} maxWidth="sm" fullWidth>
<DialogTitle>Edit User</DialogTitle>
<DialogContent>
<FormDialogContent>
{editError ? <Alert severity="error" sx={{ mb: 1 }}>{editError}</Alert> : null}
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField label="Username" value={editUser?.username || ''} disabled />
@@ -357,7 +358,7 @@ export default function AdminUsersPage() {
label="Allow project creation (direct grant)"
/>
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={closeEditUser}>Cancel</Button>
<Button variant="contained" onClick={handleSaveEdit} disabled={savingEdit}>
@@ -367,7 +368,7 @@ export default function AdminUsersPage() {
</Dialog>
<Dialog open={Boolean(deletingUser)} onClose={() => setDeletingUser(null)} maxWidth="xs" fullWidth>
<DialogTitle>Delete User</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Type the username to confirm deletion.
</Typography>
@@ -377,7 +378,7 @@ export default function AdminUsersPage() {
value={deleteConfirm}
onChange={(event) => setDeleteConfirm(event.target.value)}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => { setDeletingUser(null); setDeleteConfirm('') }}>Cancel</Button>
<Button

View File

@@ -4,6 +4,7 @@ import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
import DeleteIcon from '@mui/icons-material/Delete'
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -226,7 +227,7 @@ export default function ApiKeysPage() {
<Dialog open={createOpen} onClose={closeCreate} maxWidth="sm" fullWidth>
<DialogTitle>Create API Key</DialogTitle>
<DialogContent>
<FormDialogContent>
{createError ? <Alert severity="error" sx={{ mb: 1 }}>{createError}</Alert> : null}
<TextField
margin="dense"
@@ -278,7 +279,7 @@ export default function ApiKeysPage() {
Copied.
</Typography>
) : null}
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={closeCreate}>{newToken ? 'Done' : 'Cancel'}</Button>
{!newToken ? (
@@ -291,7 +292,7 @@ export default function ApiKeysPage() {
<Dialog open={Boolean(deleteTarget)} onClose={() => setDeleteTarget(null)} maxWidth="xs" fullWidth>
<DialogTitle>Revoke API Key</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
Type the API key name to confirm revocation.
</Typography>
@@ -301,7 +302,7 @@ export default function ApiKeysPage() {
value={deleteConfirm}
onChange={(event) => setDeleteConfirm(event.target.value)}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => { setDeleteTarget(null); setDeleteConfirm('') }}>Cancel</Button>
<Button

View File

@@ -7,6 +7,7 @@ import RepoSubNav from '../components/RepoSubNav'
import EditIcon from '@mui/icons-material/Edit'
import DeleteIcon from '@mui/icons-material/Delete'
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'
import FormDialogContent from '../components/FormDialogContent'
export default function BranchesPage() {
const { projectId, repoId } = useParams()
@@ -309,7 +310,7 @@ export default function BranchesPage() {
</Paper>
<Dialog open={createOpen} onClose={() => setCreateOpen(false)} maxWidth="sm" fullWidth>
<DialogTitle>Create Branch</DialogTitle>
<DialogContent>
<FormDialogContent>
{createError ? (
<Typography variant="body2" color="error" sx={{ mb: 1 }}>
{createError}
@@ -331,7 +332,7 @@ export default function BranchesPage() {
margin="dense"
placeholder="main"
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setCreateOpen(false)}>Cancel</Button>
<Button onClick={handleCreate} variant="contained">
@@ -341,7 +342,7 @@ export default function BranchesPage() {
</Dialog>
<Dialog open={renameOpen} onClose={() => setRenameOpen(false)} maxWidth="sm" fullWidth>
<DialogTitle>Rename Branch</DialogTitle>
<DialogContent>
<FormDialogContent>
{renameError ? (
<Typography variant="body2" color="error" sx={{ mb: 1 }}>
{renameError}
@@ -355,7 +356,7 @@ export default function BranchesPage() {
fullWidth
margin="dense"
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setRenameOpen(false)}>Cancel</Button>
<Button onClick={handleRename} variant="contained">
@@ -365,7 +366,7 @@ export default function BranchesPage() {
</Dialog>
<Dialog open={deleteOpen} onClose={() => setDeleteOpen(false)} maxWidth="sm" fullWidth>
<DialogTitle>Delete Branch</DialogTitle>
<DialogContent>
<FormDialogContent>
{deleteError ? (
<Typography variant="body2" color="error" sx={{ mb: 1 }}>
{deleteError}
@@ -381,7 +382,7 @@ export default function BranchesPage() {
fullWidth
margin="dense"
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDeleteOpen(false)}>Cancel</Button>
<Button onClick={handleDelete} color="error" variant="contained" disabled={deleteConfirm.trim() !== deleteTarget}>

View File

@@ -3,6 +3,7 @@ import KeyIcon from '@mui/icons-material/Key'
import BlockIcon from '@mui/icons-material/Block'
import VisibilityIcon from '@mui/icons-material/Visibility'
import Alert from '@mui/material/Alert'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -276,7 +277,7 @@ export default function ClientCertificatesPage() {
<Dialog open={Boolean(issueProfile)} onClose={() => { if (!busy) setIssueProfile(null) }} fullWidth maxWidth="sm">
<DialogTitle>Issue Client Certificate</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1.5, pt: '8px !important' }}>
<FormDialogContent>
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
<Typography variant="body2" color="text.secondary">
Profile: {issueProfile?.name}
@@ -291,7 +292,7 @@ export default function ClientCertificatesPage() {
helperText={`Default ${issueProfile?.default_valid_seconds || 0}s, maximum ${issueProfile?.max_valid_seconds || 0}s`}
autoFocus
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setIssueProfile(null)} disabled={busy}>Cancel</Button>
<Button variant="contained" onClick={submitIssue} disabled={busy}>
@@ -302,14 +303,14 @@ export default function ClientCertificatesPage() {
<Dialog open={Boolean(issueResult)} onClose={() => setIssueResult(null)} fullWidth maxWidth="md">
<DialogTitle>Client Certificate Issued</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1.5, pt: '8px !important' }}>
<FormDialogContent>
<Typography variant="body2" color="text.secondary">
Profile: {issueResult?.issuance.profile_name} · SAN URI: {issueResult?.issuance.san_uri}
</Typography>
<TextField label="Certificate" value={issueResult?.certificate || ''} multiline minRows={6} InputProps={{ sx: { fontFamily: 'monospace' } }} />
<TextField label="Private Key" value={issueResult?.private_key || ''} multiline minRows={6} InputProps={{ sx: { fontFamily: 'monospace' } }} />
<TextField label="CA Certificate" value={issueResult?.ca_certificate || ''} multiline minRows={6} InputProps={{ sx: { fontFamily: 'monospace' } }} />
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setIssueResult(null)}>Close</Button>
</DialogActions>
@@ -317,7 +318,7 @@ export default function ClientCertificatesPage() {
<Dialog open={Boolean(viewCert)} onClose={() => setViewCert(null)} fullWidth maxWidth="md">
<DialogTitle>Certificate Details</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1.5, pt: '8px !important' }}>
<FormDialogContent>
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
<TextField label="ID" value={viewCert?.id || ''} InputProps={{ readOnly: true }} />
<TextField label="Issuer CA ID" value={viewCert?.ca_id || 'standalone'} InputProps={{ readOnly: true }} />
@@ -351,7 +352,7 @@ export default function ClientCertificatesPage() {
InputProps={{ readOnly: true, sx: { fontFamily: 'monospace' } }}
/>
) : null}
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={toggleViewDump} disabled={viewCertDumpLoading}>
{viewCertDumpLoading ? 'Loading Dump...' : viewCertDumpOpen ? 'Hide X509 Dump' : 'Show X509 Dump'}
@@ -362,12 +363,12 @@ export default function ClientCertificatesPage() {
<Dialog open={Boolean(revokeItem)} onClose={() => { if (!busy) setRevokeItem(null) }} fullWidth maxWidth="sm">
<DialogTitle>Revoke Client Certificate</DialogTitle>
<DialogContent sx={{ display: 'grid', gap: 1.5, pt: '8px !important' }}>
<FormDialogContent>
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
<Typography variant="body2" color="text.secondary">
Revoke {revokeItem?.common_name} ({revokeItem?.cert_id})?
</Typography>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setRevokeItem(null)} disabled={busy}>Cancel</Button>
<Button variant="contained" color="error" onClick={revoke} disabled={busy}>

View File

@@ -50,7 +50,7 @@ export default function GlobalReposPage() {
return (
<Box>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
<Typography variant="h6">Repositories</Typography>
<Typography variant="h5">Repositories</Typography>
</Box>
<SectionCard
title={

View File

@@ -12,6 +12,7 @@ import EditIcon from '@mui/icons-material/Edit'
import { ListRowActionButton, ListRowActions } from '../components/ListRowActions'
import SectionCard from '../components/SectionCard'
import DeleteIcon from '@mui/icons-material/Delete'
import FormDialogContent from '../components/FormDialogContent'
export default function ProjectHomePage() {
const { projectId } = useParams()
@@ -572,7 +573,7 @@ export default function ProjectHomePage() {
</Box>
<Dialog open={editOpen} onClose={() => setEditOpen(false)} maxWidth="sm" fullWidth>
<DialogTitle>Edit Project</DialogTitle>
<DialogContent>
<FormDialogContent>
{editError ? <Alert severity="error">{editError}</Alert> : null}
<TextField
margin="dense"
@@ -638,7 +639,7 @@ export default function ProjectHomePage() {
</Paper>
)}
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setEditOpen(false)}>Cancel</Button>
<Button onClick={handleEditSave} variant="contained" disabled={savingEdit}>
@@ -648,7 +649,7 @@ export default function ProjectHomePage() {
</Dialog>
<Dialog open={deleteOpen} onClose={closeDelete} maxWidth="sm" fullWidth>
<DialogTitle>Delete Project</DialogTitle>
<DialogContent>
<FormDialogContent>
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
{deleteLoading ? (
<Typography variant="body2" color="text.secondary">
@@ -679,7 +680,7 @@ export default function ProjectHomePage() {
</Box>
)
) : null}
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={closeDelete}>Cancel</Button>
<Button

View File

@@ -12,6 +12,7 @@ import { ListRowActionButton, ListRowActions } from '../components/ListRowAction
import SectionCard from '../components/SectionCard'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import FormDialogContent from '../components/FormDialogContent'
export default function ProjectsPage() {
const [projects, setProjects] = useState<Project[]>([])
@@ -377,7 +378,7 @@ export default function ProjectsPage() {
</SectionCard>
<Dialog open={Boolean(editProject)} onClose={() => setEditProject(null)} maxWidth="sm" fullWidth>
<DialogTitle>Edit Project</DialogTitle>
<DialogContent>
<FormDialogContent>
{editError ? <Alert severity="error">{editError}</Alert> : null}
<TextField
margin="dense"
@@ -443,7 +444,7 @@ export default function ProjectsPage() {
</Paper>
)}
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setEditProject(null)}>Cancel</Button>
<Button onClick={handleEditSave} variant="contained" disabled={savingEdit}>
@@ -453,7 +454,7 @@ export default function ProjectsPage() {
</Dialog>
<Dialog open={createOpen} onClose={closeCreate} maxWidth="sm" fullWidth>
<DialogTitle>New Project</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box component="form" onSubmit={handleCreate} sx={{ display: 'grid', gap: 1, mt: 1 }}>
{createError ? <Alert severity="error">{createError}</Alert> : null}
<TextField
@@ -516,11 +517,11 @@ export default function ProjectsPage() {
</Button>
</Box>
</Box>
</DialogContent>
</FormDialogContent>
</Dialog>
<Dialog open={Boolean(deleteProject)} onClose={closeDelete} maxWidth="sm" fullWidth>
<DialogTitle>Delete Project</DialogTitle>
<DialogContent>
<FormDialogContent>
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
{deleteLoading ? (
<Typography variant="body2" color="text.secondary">
@@ -551,7 +552,7 @@ export default function ProjectsPage() {
</Box>
)
) : null}
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={closeDelete}>Cancel</Button>
<Button

View File

@@ -1,3 +1,4 @@
import FormDialogContent from '../components/FormDialogContent'
import {
Alert,
Box,
@@ -482,7 +483,7 @@ export default function RepoDockerDetailPage(props: RepoDockerDetailPageProps) {
</Box>
<Dialog open={deleteTagOpen} onClose={() => setDeleteTagOpen(false)} maxWidth="xs" fullWidth>
<DialogTitle>Delete tag</DialogTitle>
<DialogContent>
<FormDialogContent>
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
<Typography variant="body2" sx={{ mt: 1 }}>
Delete tag "{deleteTagName}"?
@@ -494,7 +495,7 @@ export default function RepoDockerDetailPage(props: RepoDockerDetailPageProps) {
fullWidth
sx={{ mt: 1 }}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDeleteTagOpen(false)}>Cancel</Button>
<Button
@@ -509,7 +510,7 @@ export default function RepoDockerDetailPage(props: RepoDockerDetailPageProps) {
</Dialog>
<Dialog open={deleteImageOpen} onClose={() => setDeleteImageOpen(false)} maxWidth="xs" fullWidth>
<DialogTitle>Delete image</DialogTitle>
<DialogContent>
<FormDialogContent>
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
<Typography variant="body2" sx={{ mt: 1 }}>
Delete image "{deleteImageLabel || '(root)'}" and all tags?
@@ -521,7 +522,7 @@ export default function RepoDockerDetailPage(props: RepoDockerDetailPageProps) {
fullWidth
sx={{ mt: 1 }}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDeleteImageOpen(false)}>Cancel</Button>
<Button
@@ -536,7 +537,7 @@ export default function RepoDockerDetailPage(props: RepoDockerDetailPageProps) {
</Dialog>
<Dialog open={renameTagOpen} onClose={() => setRenameTagOpen(false)} maxWidth="xs" fullWidth>
<DialogTitle>Rename tag</DialogTitle>
<DialogContent>
<FormDialogContent>
{renameError ? <Alert severity="error">{renameError}</Alert> : null}
<TextField label="From" value={renameTagFrom} fullWidth margin="dense" InputProps={{ readOnly: true }} />
<TextField
@@ -546,7 +547,7 @@ export default function RepoDockerDetailPage(props: RepoDockerDetailPageProps) {
fullWidth
margin="dense"
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setRenameTagOpen(false)}>Cancel</Button>
<Button onClick={handleRenameTag} variant="contained" disabled={renaming}>
@@ -556,7 +557,7 @@ export default function RepoDockerDetailPage(props: RepoDockerDetailPageProps) {
</Dialog>
<Dialog open={renameImageOpen} onClose={() => setRenameImageOpen(false)} maxWidth="xs" fullWidth>
<DialogTitle>Rename image</DialogTitle>
<DialogContent>
<FormDialogContent>
{renameError ? <Alert severity="error">{renameError}</Alert> : null}
<TextField label="From" value={renameImageFromLabel} fullWidth margin="dense" InputProps={{ readOnly: true }} />
<TextField
@@ -567,7 +568,7 @@ export default function RepoDockerDetailPage(props: RepoDockerDetailPageProps) {
margin="dense"
helperText="Leave blank to move to root."
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setRenameImageOpen(false)}>Cancel</Button>
<Button onClick={handleRenameImage} variant="contained" disabled={renaming}>

View File

@@ -1,3 +1,4 @@
import FormDialogContent from '../components/FormDialogContent'
import {
Alert,
Box,
@@ -1206,7 +1207,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
</Box>
<Dialog open={subdirOpen} onClose={() => setSubdirOpen(false)} maxWidth="sm" fullWidth>
<DialogTitle>New Subdirectory</DialogTitle>
<DialogContent>
<FormDialogContent>
{subdirError ? <Alert severity="error">{subdirError}</Alert> : null}
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField
@@ -1298,7 +1299,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
</>
) : null}
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setSubdirOpen(false)}>Cancel</Button>
<Button onClick={handleCreateSubdir} variant="contained" disabled={subdirSaving}>
@@ -1308,7 +1309,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
</Dialog>
<Dialog open={uploadOpen} onClose={() => setUploadOpen(false)} maxWidth="sm" fullWidth>
<DialogTitle>Upload RPM</DialogTitle>
<DialogContent>
<FormDialogContent>
{uploadError ? <Alert severity="error">{uploadError}</Alert> : null}
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<TextField
@@ -1325,7 +1326,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
label="Overwrite existing files"
/>
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setUploadOpen(false)}>Cancel</Button>
<Button onClick={handleUpload} variant="contained" disabled={uploading}>
@@ -1335,7 +1336,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
</Dialog>
<Dialog open={deleteOpen} onClose={() => setDeleteOpen(false)} maxWidth="xs" fullWidth>
<DialogTitle>{deleteIsFile ? 'Delete file' : 'Delete folder'}</DialogTitle>
<DialogContent>
<FormDialogContent>
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
<Typography variant="body2" sx={{ mt: 1 }}>
{deleteIsFile
@@ -1349,7 +1350,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
fullWidth
sx={{ mt: 1 }}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setDeleteOpen(false)}>Cancel</Button>
<Button
@@ -1378,7 +1379,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
fullWidth
>
<DialogTitle>Repo directory status</DialogTitle>
<DialogContent>
<FormDialogContent>
{statusError ? <Alert severity="error">{statusError}</Alert> : null}
<Box sx={{ display: 'grid', gap: 0.5, mt: 1 }}>
<Typography variant="body2">Directory: {statusName}</Typography>
@@ -1459,7 +1460,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
</Typography>
)}
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button
onClick={() => {
@@ -1487,7 +1488,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
fullWidth
>
<DialogTitle>{renameIsRepoDir ? 'Edit repo directory' : 'Rename folder'}</DialogTitle>
<DialogContent>
<FormDialogContent>
{renameError ? <Alert severity="error">{renameError}</Alert> : null}
<Box sx={{ display: 'grid', gap: 1, mt: 1 }}>
<Typography variant="body2">
@@ -1559,7 +1560,7 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
</>
) : null}
</Box>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button
onClick={() => {
@@ -1581,11 +1582,11 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
fullWidth
>
<DialogTitle>Clear runs</DialogTitle>
<DialogContent>
<FormDialogContent>
<Typography variant="body2" sx={{ mt: 1 }}>
Clear recent mirror runs for "{statusName}"?
</Typography>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setClearRunsConfirmOpen(false)}>Cancel</Button>
<Button

View File

@@ -5,6 +5,7 @@ import LinkOffIcon from '@mui/icons-material/LinkOff'
import LinkIcon from '@mui/icons-material/Link'
import Alert from '@mui/material/Alert'
import { Autocomplete } from '@mui/material'
import FormDialogContent from '../components/FormDialogContent'
import {
Box,
Button,
@@ -321,6 +322,7 @@ export default function ReposPage() {
<Typography variant="h5" sx={{ mb: 1 }}>
Repositories
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, flexWrap: 'wrap' }}>
<Button component={Link} to="/projects" size="small">
@@ -535,7 +537,7 @@ export default function ReposPage() {
</SectionCard>
<Dialog open={createOpen} onClose={() => setCreateOpen(false)} maxWidth="sm" fullWidth>
<DialogTitle>New Repository</DialogTitle>
<DialogContent>
<FormDialogContent>
<Box component="form" onSubmit={handleCreate} sx={{ display: 'grid', gap: 1, mt: 1 }}>
{createError ? <Alert severity="error">{createError}</Alert> : null}
<TextField name="name" label="Name" />
@@ -556,11 +558,11 @@ export default function ReposPage() {
</Button>
</Box>
</Box>
</DialogContent>
</FormDialogContent>
</Dialog>
<Dialog open={Boolean(editRepo)} onClose={() => setEditRepo(null)} maxWidth="sm" fullWidth>
<DialogTitle>Edit Repository</DialogTitle>
<DialogContent>
<FormDialogContent>
{editError ? <Alert severity="error">{editError}</Alert> : null}
<TextField
autoFocus
@@ -570,7 +572,7 @@ export default function ReposPage() {
value={editName}
onChange={(event) => setEditName(event.target.value)}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setEditRepo(null)}>Cancel</Button>
<Button onClick={handleEditSave} variant="contained" disabled={savingEdit}>
@@ -580,7 +582,7 @@ export default function ReposPage() {
</Dialog>
<Dialog open={Boolean(deleteRepo)} onClose={closeDelete} maxWidth="sm" fullWidth>
<DialogTitle>Delete Repository</DialogTitle>
<DialogContent>
<FormDialogContent>
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
{deleteLoading ? (
<Typography variant="body2" color="text.secondary">
@@ -615,7 +617,7 @@ export default function ReposPage() {
Unable to load repository stats.
</Typography>
)}
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={closeDelete}>Cancel</Button>
<Button
@@ -630,7 +632,7 @@ export default function ReposPage() {
</Dialog>
<Dialog open={bulkDeleteOpen} onClose={closeBulkDelete} maxWidth="sm" fullWidth>
<DialogTitle>Delete Repositories</DialogTitle>
<DialogContent>
<FormDialogContent>
{bulkDeleteError ? <Alert severity="error">{bulkDeleteError}</Alert> : null}
<Typography variant="body2" color="text.secondary">
You are deleting {selectedRepoIds.length} repositories. This action is permanent.
@@ -654,7 +656,7 @@ export default function ReposPage() {
onChange={(event) => setBulkDeleteConfirm(event.target.value)}
fullWidth
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={closeBulkDelete}>Cancel</Button>
<Button
@@ -669,7 +671,7 @@ export default function ReposPage() {
</Dialog>
<Dialog open={foreignOpen} onClose={closeForeign} maxWidth="sm" fullWidth>
<DialogTitle>Add Foreign Repository</DialogTitle>
<DialogContent>
<FormDialogContent>
{foreignError ? <Alert severity="error">{foreignError}</Alert> : null}
<TextField
margin="dense"
@@ -699,7 +701,7 @@ export default function ReposPage() {
) : null}
</List>
)}
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={closeForeign}>Close</Button>
</DialogActions>

View File

@@ -1,5 +1,6 @@
import Alert from '@mui/material/Alert'
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
import FormDialogContent from '../components/FormDialogContent'
import {
Autocomplete,
Box,
@@ -324,7 +325,7 @@ export default function SSHCertificatesPage() {
<Dialog open={inspectOpen} onClose={() => setInspectOpen(false)} maxWidth="md" fullWidth>
<DialogTitle>Certificate Inspect</DialogTitle>
<DialogContent>
<FormDialogContent>
<TextField
multiline
minRows={12}
@@ -333,7 +334,7 @@ export default function SSHCertificatesPage() {
fullWidth
sx={{ mt: 1, '& .MuiInputBase-input': { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace' } }}
/>
</DialogContent>
</FormDialogContent>
<DialogActions>
<Button onClick={() => setInspectOpen(false)}>Close</Button>
</DialogActions>