Compare commits
2 Commits
a42da315bc
...
25f6dd27de
| Author | SHA1 | Date | |
|---|---|---|---|
| 25f6dd27de | |||
| 3acb6314d3 |
@@ -3,6 +3,7 @@ import KeyIcon from '@mui/icons-material/Key'
|
|||||||
import BlockIcon from '@mui/icons-material/Block'
|
import BlockIcon from '@mui/icons-material/Block'
|
||||||
import VisibilityIcon from '@mui/icons-material/Visibility'
|
import VisibilityIcon from '@mui/icons-material/Visibility'
|
||||||
import Alert from '@mui/material/Alert'
|
import Alert from '@mui/material/Alert'
|
||||||
|
import ConfirmDeleteDialog from '../components/ConfirmDeleteDialog'
|
||||||
import FormDialogContent from '../components/FormDialogContent'
|
import FormDialogContent from '../components/FormDialogContent'
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -656,21 +657,17 @@ export default function ClientCertificatesPage() {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<Dialog open={Boolean(revokeItem)} onClose={() => { if (!busy) setRevokeItem(null) }} fullWidth maxWidth="sm">
|
<ConfirmDeleteDialog
|
||||||
<DialogTitle>Revoke Client Certificate</DialogTitle>
|
open={Boolean(revokeItem)}
|
||||||
<FormDialogContent>
|
title="Revoke Client Certificate"
|
||||||
{dialogError ? <Alert severity="error">{dialogError}</Alert> : null}
|
prompt={`Revoke ${revokeItem?.common_name || ''} (${revokeItem?.cert_id || ''})?`}
|
||||||
<Typography variant="body2" color="text.secondary">
|
error={dialogError}
|
||||||
Revoke {revokeItem?.common_name} ({revokeItem?.cert_id})?
|
busy={busy}
|
||||||
</Typography>
|
confirmText="Revoke"
|
||||||
</FormDialogContent>
|
busyText="Revoking..."
|
||||||
<DialogActions>
|
onConfirm={revoke}
|
||||||
<Button variant="contained" color="error" onClick={revoke} disabled={busy}>
|
onClose={() => setRevokeItem(null)}
|
||||||
{busy ? 'Revoking...' : 'Revoke'}
|
/>
|
||||||
</Button>
|
|
||||||
<Button onClick={() => setRevokeItem(null)} disabled={busy}>Cancel</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import EditIcon from '@mui/icons-material/Edit'
|
|||||||
import { ListRowActionButton, ListRowActions } from '../components/ListRowActions'
|
import { ListRowActionButton, ListRowActions } from '../components/ListRowActions'
|
||||||
import SectionCard from '../components/SectionCard'
|
import SectionCard from '../components/SectionCard'
|
||||||
import DeleteIcon from '@mui/icons-material/Delete'
|
import DeleteIcon from '@mui/icons-material/Delete'
|
||||||
|
import ConfirmDeleteDialog from '../components/ConfirmDeleteDialog'
|
||||||
import FormDialogContent from '../components/FormDialogContent'
|
import FormDialogContent from '../components/FormDialogContent'
|
||||||
import SelectField from '../components/SelectField'
|
import SelectField from '../components/SelectField'
|
||||||
|
|
||||||
@@ -648,15 +649,21 @@ export default function ProjectHomePage() {
|
|||||||
<Button onClick={() => setEditOpen(false)}>Cancel</Button>
|
<Button onClick={() => setEditOpen(false)}>Cancel</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<Dialog open={deleteOpen} onClose={closeDelete} maxWidth="sm" fullWidth>
|
<ConfirmDeleteDialog
|
||||||
<DialogTitle>Delete Project</DialogTitle>
|
open={deleteOpen}
|
||||||
<FormDialogContent>
|
title="Delete Project"
|
||||||
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
|
prompt={deleteLoading ? 'Checking project contents...' : 'Type the project slug to confirm deletion.'}
|
||||||
{deleteLoading ? (
|
expectedText={deleteCounts && deleteCounts.repos + deleteCounts.issues + deleteCounts.wiki + deleteCounts.uploads > 0 ? (project?.slug || undefined) : undefined}
|
||||||
<Typography variant="body2" color="text.secondary">
|
confirmLabel="Project Slug"
|
||||||
Checking project contents...
|
confirmValue={deleteConfirm}
|
||||||
</Typography>
|
onConfirmValueChange={setDeleteConfirm}
|
||||||
) : deleteCounts ? (
|
error={deleteError}
|
||||||
|
busy={deleting}
|
||||||
|
confirmDisabled={deleteLoading || !project || !deleteCounts}
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
onClose={closeDelete}
|
||||||
|
>
|
||||||
|
{deleteLoading ? null : deleteCounts ? (
|
||||||
deleteCounts.repos + deleteCounts.issues + deleteCounts.wiki + deleteCounts.uploads === 0 ? (
|
deleteCounts.repos + deleteCounts.issues + deleteCounts.wiki + deleteCounts.uploads === 0 ? (
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
This project has no repositories, issues, wiki pages, or files.
|
This project has no repositories, issues, wiki pages, or files.
|
||||||
@@ -670,38 +677,10 @@ export default function ProjectHomePage() {
|
|||||||
<Typography variant="body2">Issues: {deleteCounts.issues}</Typography>
|
<Typography variant="body2">Issues: {deleteCounts.issues}</Typography>
|
||||||
<Typography variant="body2">Wiki pages: {deleteCounts.wiki}</Typography>
|
<Typography variant="body2">Wiki pages: {deleteCounts.wiki}</Typography>
|
||||||
<Typography variant="body2">Files: {deleteCounts.uploads}</Typography>
|
<Typography variant="body2">Files: {deleteCounts.uploads}</Typography>
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
Type the project slug to confirm deletion.
|
|
||||||
</Typography>
|
|
||||||
<TextField
|
|
||||||
label="Project Slug"
|
|
||||||
value={deleteConfirm}
|
|
||||||
onChange={(event) => setDeleteConfirm(event.target.value)}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
</FormDialogContent>
|
</ConfirmDeleteDialog>
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
onClick={handleDelete}
|
|
||||||
variant="contained"
|
|
||||||
color="error"
|
|
||||||
disabled={
|
|
||||||
deleting ||
|
|
||||||
deleteLoading ||
|
|
||||||
!project ||
|
|
||||||
(deleteCounts
|
|
||||||
? deleteCounts.repos + deleteCounts.issues + deleteCounts.wiki + deleteCounts.uploads > 0 &&
|
|
||||||
deleteConfirm !== project.slug
|
|
||||||
: true)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{deleting ? 'Deleting...' : 'Delete'}
|
|
||||||
</Button>
|
|
||||||
<Button onClick={closeDelete}>Cancel</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import AddIcon from '@mui/icons-material/Add'
|
|||||||
import DeleteIcon from '@mui/icons-material/Delete'
|
import DeleteIcon from '@mui/icons-material/Delete'
|
||||||
import EditIcon from '@mui/icons-material/Edit'
|
import EditIcon from '@mui/icons-material/Edit'
|
||||||
import Alert from '@mui/material/Alert'
|
import Alert from '@mui/material/Alert'
|
||||||
|
import ConfirmDeleteDialog from '../components/ConfirmDeleteDialog'
|
||||||
import { Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, List, ListItem, ListItemText, MenuItem, Paper, Tab, Tabs, TextField, Typography } from '@mui/material'
|
import { Box, Button, Chip, Dialog, DialogActions, DialogContent, DialogTitle, List, ListItem, ListItemText, MenuItem, Paper, Tab, Tabs, TextField, Typography } from '@mui/material'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
@@ -520,15 +521,21 @@ export default function ProjectsPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
</FormDialogContent>
|
</FormDialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<Dialog open={Boolean(deleteProject)} onClose={closeDelete} maxWidth="sm" fullWidth>
|
<ConfirmDeleteDialog
|
||||||
<DialogTitle>Delete Project</DialogTitle>
|
open={Boolean(deleteProject)}
|
||||||
<FormDialogContent>
|
title="Delete Project"
|
||||||
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
|
prompt={deleteLoading ? 'Checking project contents...' : 'Type the project slug to confirm deletion.'}
|
||||||
{deleteLoading ? (
|
expectedText={deleteCounts && deleteCounts.repos + deleteCounts.issues + deleteCounts.wiki + deleteCounts.uploads > 0 ? (deleteProject?.slug || undefined) : undefined}
|
||||||
<Typography variant="body2" color="text.secondary">
|
confirmLabel="Project Slug"
|
||||||
Checking project contents...
|
confirmValue={deleteConfirm}
|
||||||
</Typography>
|
onConfirmValueChange={setDeleteConfirm}
|
||||||
) : deleteCounts ? (
|
error={deleteError}
|
||||||
|
busy={deleting}
|
||||||
|
confirmDisabled={deleteLoading || !deleteProject || !deleteCounts}
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
onClose={closeDelete}
|
||||||
|
>
|
||||||
|
{deleteLoading ? null : deleteCounts ? (
|
||||||
deleteCounts.repos + deleteCounts.issues + deleteCounts.wiki + deleteCounts.uploads === 0 ? (
|
deleteCounts.repos + deleteCounts.issues + deleteCounts.wiki + deleteCounts.uploads === 0 ? (
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
This project has no repositories, issues, wiki pages, or files.
|
This project has no repositories, issues, wiki pages, or files.
|
||||||
@@ -542,38 +549,10 @@ export default function ProjectsPage() {
|
|||||||
<Typography variant="body2">Issues: {deleteCounts.issues}</Typography>
|
<Typography variant="body2">Issues: {deleteCounts.issues}</Typography>
|
||||||
<Typography variant="body2">Wiki pages: {deleteCounts.wiki}</Typography>
|
<Typography variant="body2">Wiki pages: {deleteCounts.wiki}</Typography>
|
||||||
<Typography variant="body2">Files: {deleteCounts.uploads}</Typography>
|
<Typography variant="body2">Files: {deleteCounts.uploads}</Typography>
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
Type the project slug to confirm deletion.
|
|
||||||
</Typography>
|
|
||||||
<TextField
|
|
||||||
label="Project Slug"
|
|
||||||
value={deleteConfirm}
|
|
||||||
onChange={(event) => setDeleteConfirm(event.target.value)}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
</FormDialogContent>
|
</ConfirmDeleteDialog>
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
onClick={handleDelete}
|
|
||||||
variant="contained"
|
|
||||||
color="error"
|
|
||||||
disabled={
|
|
||||||
deleting ||
|
|
||||||
deleteLoading ||
|
|
||||||
!deleteProject ||
|
|
||||||
(deleteCounts
|
|
||||||
? deleteCounts.repos + deleteCounts.issues + deleteCounts.wiki + deleteCounts.uploads > 0 &&
|
|
||||||
deleteConfirm !== deleteProject.slug
|
|
||||||
: true)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{deleting ? 'Deleting...' : 'Delete'}
|
|
||||||
</Button>
|
|
||||||
<Button onClick={closeDelete}>Cancel</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import RepoSubNav from '../components/RepoSubNav'
|
|||||||
import TintedPanel from '../components/TintedPanel'
|
import TintedPanel from '../components/TintedPanel'
|
||||||
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'
|
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'
|
||||||
import DriveFileRenameOutlineIcon from '@mui/icons-material/DriveFileRenameOutline'
|
import DriveFileRenameOutlineIcon from '@mui/icons-material/DriveFileRenameOutline'
|
||||||
|
import ConfirmDeleteDialog from '../components/ConfirmDeleteDialog'
|
||||||
|
|
||||||
type RepoDockerDetailPageProps = {
|
type RepoDockerDetailPageProps = {
|
||||||
initialRepo?: Repo
|
initialRepo?: Repo
|
||||||
@@ -482,60 +483,40 @@ export default function RepoDockerDetailPage(props: RepoDockerDetailPageProps) {
|
|||||||
)}
|
)}
|
||||||
</TintedPanel>
|
</TintedPanel>
|
||||||
</Box>
|
</Box>
|
||||||
<Dialog open={deleteTagOpen} onClose={() => setDeleteTagOpen(false)} maxWidth="xs" fullWidth>
|
<ConfirmDeleteDialog
|
||||||
<DialogTitle>Delete tag</DialogTitle>
|
open={deleteTagOpen}
|
||||||
<FormDialogContent>
|
title="Delete tag"
|
||||||
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
|
prompt="Type the tag name to confirm deletion."
|
||||||
<Typography variant="body2" sx={{ mt: 1 }}>
|
expectedText={deleteTagName}
|
||||||
|
confirmLabel="Type tag to confirm"
|
||||||
|
confirmValue={deleteTagConfirm}
|
||||||
|
onConfirmValueChange={setDeleteTagConfirm}
|
||||||
|
error={deleteError}
|
||||||
|
busy={deleting}
|
||||||
|
onConfirm={handleDeleteTag}
|
||||||
|
onClose={() => setDeleteTagOpen(false)}
|
||||||
|
>
|
||||||
|
<Typography variant="body2">
|
||||||
Delete tag "{deleteTagName}"?
|
Delete tag "{deleteTagName}"?
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField
|
</ConfirmDeleteDialog>
|
||||||
label="Type tag to confirm"
|
<ConfirmDeleteDialog
|
||||||
value={deleteTagConfirm}
|
open={deleteImageOpen}
|
||||||
onChange={(event) => setDeleteTagConfirm(event.target.value)}
|
title="Delete image"
|
||||||
fullWidth
|
prompt="Type the image name to confirm deletion."
|
||||||
sx={{ mt: 1 }}
|
expectedText={deleteImageLabel}
|
||||||
/>
|
confirmLabel="Type image name to confirm"
|
||||||
</FormDialogContent>
|
confirmValue={deleteImageConfirm}
|
||||||
<DialogActions>
|
onConfirmValueChange={setDeleteImageConfirm}
|
||||||
<Button
|
error={deleteError}
|
||||||
onClick={handleDeleteTag}
|
busy={deleting}
|
||||||
variant="contained"
|
onConfirm={handleDeleteImage}
|
||||||
color="error"
|
onClose={() => setDeleteImageOpen(false)}
|
||||||
disabled={deleting || deleteTagConfirm.trim() !== deleteTagName}
|
|
||||||
>
|
>
|
||||||
{deleting ? 'Deleting...' : 'Delete'}
|
<Typography variant="body2">
|
||||||
</Button>
|
|
||||||
<Button onClick={() => setDeleteTagOpen(false)}>Cancel</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
<Dialog open={deleteImageOpen} onClose={() => setDeleteImageOpen(false)} maxWidth="xs" fullWidth>
|
|
||||||
<DialogTitle>Delete image</DialogTitle>
|
|
||||||
<FormDialogContent>
|
|
||||||
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
|
|
||||||
<Typography variant="body2" sx={{ mt: 1 }}>
|
|
||||||
Delete image "{deleteImageLabel || '(root)'}" and all tags?
|
Delete image "{deleteImageLabel || '(root)'}" and all tags?
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField
|
</ConfirmDeleteDialog>
|
||||||
label="Type image name to confirm"
|
|
||||||
value={deleteImageConfirm}
|
|
||||||
onChange={(event) => setDeleteImageConfirm(event.target.value)}
|
|
||||||
fullWidth
|
|
||||||
sx={{ mt: 1 }}
|
|
||||||
/>
|
|
||||||
</FormDialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
onClick={handleDeleteImage}
|
|
||||||
variant="contained"
|
|
||||||
color="error"
|
|
||||||
disabled={deleting || deleteImageConfirm.trim() !== deleteImageLabel}
|
|
||||||
>
|
|
||||||
{deleting ? 'Deleting...' : 'Delete'}
|
|
||||||
</Button>
|
|
||||||
<Button onClick={() => setDeleteImageOpen(false)}>Cancel</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
<Dialog open={renameTagOpen} onClose={() => setRenameTagOpen(false)} maxWidth="xs" fullWidth>
|
<Dialog open={renameTagOpen} onClose={() => setRenameTagOpen(false)} maxWidth="xs" fullWidth>
|
||||||
<DialogTitle>Rename tag</DialogTitle>
|
<DialogTitle>Rename tag</DialogTitle>
|
||||||
<FormDialogContent>
|
<FormDialogContent>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import DriveFileRenameOutlineIcon from '@mui/icons-material/DriveFileRenameOutli
|
|||||||
import FolderIcon from '@mui/icons-material/Folder'
|
import FolderIcon from '@mui/icons-material/Folder'
|
||||||
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile'
|
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile'
|
||||||
import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined'
|
import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined'
|
||||||
|
import ConfirmDeleteDialog from '../components/ConfirmDeleteDialog'
|
||||||
import MonitorHeartOutlinedIcon from '@mui/icons-material/MonitorHeartOutlined'
|
import MonitorHeartOutlinedIcon from '@mui/icons-material/MonitorHeartOutlined'
|
||||||
import ProjectNavBar from '../components/ProjectNavBar'
|
import ProjectNavBar from '../components/ProjectNavBar'
|
||||||
import RepoSubNav from '../components/RepoSubNav'
|
import RepoSubNav from '../components/RepoSubNav'
|
||||||
@@ -1325,35 +1326,25 @@ export default function RepoRpmDetailPage(props: RepoRpmDetailPageProps) {
|
|||||||
<Button onClick={() => setUploadOpen(false)}>Cancel</Button>
|
<Button onClick={() => setUploadOpen(false)}>Cancel</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<Dialog open={deleteOpen} onClose={() => setDeleteOpen(false)} maxWidth="xs" fullWidth>
|
<ConfirmDeleteDialog
|
||||||
<DialogTitle>{deleteIsFile ? 'Delete file' : 'Delete folder'}</DialogTitle>
|
open={deleteOpen}
|
||||||
<FormDialogContent>
|
title={deleteIsFile ? 'Delete file' : 'Delete folder'}
|
||||||
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
|
prompt="Type the name to confirm deletion."
|
||||||
<Typography variant="body2" sx={{ mt: 1 }}>
|
expectedText={deleteName}
|
||||||
|
confirmLabel="Type name to confirm"
|
||||||
|
confirmValue={deleteConfirm}
|
||||||
|
onConfirmValueChange={setDeleteConfirm}
|
||||||
|
error={deleteError}
|
||||||
|
busy={deleting}
|
||||||
|
onConfirm={handleDeleteFolder}
|
||||||
|
onClose={() => setDeleteOpen(false)}
|
||||||
|
>
|
||||||
|
<Typography variant="body2">
|
||||||
{deleteIsFile
|
{deleteIsFile
|
||||||
? `Delete file "${deleteName}"?`
|
? `Delete file "${deleteName}"?`
|
||||||
: `Delete folder "${deleteName}" and all of its contents?`}
|
: `Delete folder "${deleteName}" and all of its contents?`}
|
||||||
</Typography>
|
</Typography>
|
||||||
<TextField
|
</ConfirmDeleteDialog>
|
||||||
label="Type name to confirm"
|
|
||||||
value={deleteConfirm}
|
|
||||||
onChange={(event) => setDeleteConfirm(event.target.value)}
|
|
||||||
fullWidth
|
|
||||||
sx={{ mt: 1 }}
|
|
||||||
/>
|
|
||||||
</FormDialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
onClick={handleDeleteFolder}
|
|
||||||
variant="contained"
|
|
||||||
color="error"
|
|
||||||
disabled={deleting || deleteConfirm.trim() !== deleteName}
|
|
||||||
>
|
|
||||||
{deleting ? 'Deleting...' : 'Delete'}
|
|
||||||
</Button>
|
|
||||||
<Button onClick={() => setDeleteOpen(false)}>Cancel</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
<Dialog
|
<Dialog
|
||||||
open={statusOpen}
|
open={statusOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import EditIcon from '@mui/icons-material/Edit'
|
|||||||
import LinkOffIcon from '@mui/icons-material/LinkOff'
|
import LinkOffIcon from '@mui/icons-material/LinkOff'
|
||||||
import LinkIcon from '@mui/icons-material/Link'
|
import LinkIcon from '@mui/icons-material/Link'
|
||||||
import Alert from '@mui/material/Alert'
|
import Alert from '@mui/material/Alert'
|
||||||
|
import ConfirmDeleteDialog from '../components/ConfirmDeleteDialog'
|
||||||
import FormDialogContent from '../components/FormDialogContent'
|
import FormDialogContent from '../components/FormDialogContent'
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -581,15 +582,21 @@ export default function ReposPage() {
|
|||||||
<Button onClick={() => setEditRepo(null)}>Cancel</Button>
|
<Button onClick={() => setEditRepo(null)}>Cancel</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<Dialog open={Boolean(deleteRepo)} onClose={closeDelete} maxWidth="sm" fullWidth>
|
<ConfirmDeleteDialog
|
||||||
<DialogTitle>Delete Repository</DialogTitle>
|
open={Boolean(deleteRepo)}
|
||||||
<FormDialogContent>
|
title="Delete Repository"
|
||||||
{deleteError ? <Alert severity="error">{deleteError}</Alert> : null}
|
prompt={deleteLoading ? 'Loading repository stats...' : 'Type the repository name to confirm deletion.'}
|
||||||
{deleteLoading ? (
|
expectedText={deleteRepo?.name}
|
||||||
<Typography variant="body2" color="text.secondary">
|
confirmLabel="Repository Name"
|
||||||
Loading repository stats...
|
confirmValue={deleteConfirm}
|
||||||
</Typography>
|
onConfirmValueChange={setDeleteConfirm}
|
||||||
) : deleteStats ? (
|
error={deleteError}
|
||||||
|
busy={deleting}
|
||||||
|
confirmDisabled={deleteLoading || !deleteRepo}
|
||||||
|
onConfirm={handleDelete}
|
||||||
|
onClose={closeDelete}
|
||||||
|
>
|
||||||
|
{deleteLoading ? null : deleteStats ? (
|
||||||
<Box sx={{ display: 'grid', gap: 1 }}>
|
<Box sx={{ display: 'grid', gap: 1 }}>
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
This will permanently delete the repository and all its data.
|
This will permanently delete the repository and all its data.
|
||||||
@@ -600,41 +607,28 @@ export default function ReposPage() {
|
|||||||
) : (
|
) : (
|
||||||
<Typography variant="body2">Last commit: none</Typography>
|
<Typography variant="body2">Last commit: none</Typography>
|
||||||
)}
|
)}
|
||||||
{deleteStats.last_message ? (
|
{deleteStats.last_message ? <Typography variant="body2">Message: {deleteStats.last_message}</Typography> : null}
|
||||||
<Typography variant="body2">Message: {deleteStats.last_message}</Typography>
|
|
||||||
) : null}
|
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
Type the repository name to confirm deletion.
|
|
||||||
</Typography>
|
|
||||||
<TextField
|
|
||||||
label="Repository Name"
|
|
||||||
value={deleteConfirm}
|
|
||||||
onChange={(event) => setDeleteConfirm(event.target.value)}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
) : (
|
) : (
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
Unable to load repository stats.
|
Unable to load repository stats.
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
</FormDialogContent>
|
</ConfirmDeleteDialog>
|
||||||
<DialogActions>
|
<ConfirmDeleteDialog
|
||||||
<Button
|
open={bulkDeleteOpen}
|
||||||
onClick={handleDelete}
|
title="Delete Repositories"
|
||||||
variant="contained"
|
prompt={`Type DELETE ${selectedRepoIds.length} to confirm.`}
|
||||||
color="error"
|
expectedText={`DELETE ${selectedRepoIds.length}`}
|
||||||
disabled={deleting || deleteLoading || !deleteRepo || deleteConfirm !== deleteRepo.name}
|
confirmLabel="Confirmation"
|
||||||
|
confirmValue={bulkDeleteConfirm}
|
||||||
|
onConfirmValueChange={setBulkDeleteConfirm}
|
||||||
|
error={bulkDeleteError}
|
||||||
|
busy={bulkDeleting}
|
||||||
|
onConfirm={handleBulkDelete}
|
||||||
|
onClose={closeBulkDelete}
|
||||||
>
|
>
|
||||||
{deleting ? 'Deleting...' : 'Delete'}
|
<>
|
||||||
</Button>
|
|
||||||
<Button onClick={closeDelete}>Cancel</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
<Dialog open={bulkDeleteOpen} onClose={closeBulkDelete} maxWidth="sm" fullWidth>
|
|
||||||
<DialogTitle>Delete Repositories</DialogTitle>
|
|
||||||
<FormDialogContent>
|
|
||||||
{bulkDeleteError ? <Alert severity="error">{bulkDeleteError}</Alert> : null}
|
|
||||||
<Typography variant="body2" color="text.secondary">
|
<Typography variant="body2" color="text.secondary">
|
||||||
You are deleting {selectedRepoIds.length} repositories. This action is permanent.
|
You are deleting {selectedRepoIds.length} repositories. This action is permanent.
|
||||||
</Typography>
|
</Typography>
|
||||||
@@ -647,29 +641,8 @@ export default function ReposPage() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
|
</>
|
||||||
Type DELETE {selectedRepoIds.length} to confirm.
|
</ConfirmDeleteDialog>
|
||||||
</Typography>
|
|
||||||
<TextField
|
|
||||||
sx={{ mt: 1 }}
|
|
||||||
label="Confirmation"
|
|
||||||
value={bulkDeleteConfirm}
|
|
||||||
onChange={(event) => setBulkDeleteConfirm(event.target.value)}
|
|
||||||
fullWidth
|
|
||||||
/>
|
|
||||||
</FormDialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
onClick={handleBulkDelete}
|
|
||||||
variant="contained"
|
|
||||||
color="error"
|
|
||||||
disabled={bulkDeleting || bulkDeleteConfirm !== `DELETE ${selectedRepoIds.length}`}
|
|
||||||
>
|
|
||||||
{bulkDeleting ? 'Deleting...' : 'Delete'}
|
|
||||||
</Button>
|
|
||||||
<Button onClick={closeBulkDelete}>Cancel</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
<Dialog open={foreignOpen} onClose={closeForeign} maxWidth="sm" fullWidth>
|
<Dialog open={foreignOpen} onClose={closeForeign} maxWidth="sm" fullWidth>
|
||||||
<DialogTitle>Add Foreign Repository</DialogTitle>
|
<DialogTitle>Add Foreign Repository</DialogTitle>
|
||||||
<FormDialogContent>
|
<FormDialogContent>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import TextField from '@mui/material/TextField'
|
|||||||
import Typography from '@mui/material/Typography'
|
import Typography from '@mui/material/Typography'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import ConfirmDeleteDialog from '../components/ConfirmDeleteDialog'
|
||||||
import FormDialogContent from '../components/FormDialogContent'
|
import FormDialogContent from '../components/FormDialogContent'
|
||||||
import SSHAccessProfileDetailsDialog from '../components/SSHAccessProfileDetailsDialog'
|
import SSHAccessProfileDetailsDialog from '../components/SSHAccessProfileDetailsDialog'
|
||||||
import { ListRowActionButton, ListRowActions } from '../components/ListRowActions'
|
import { ListRowActionButton, ListRowActions } from '../components/ListRowActions'
|
||||||
@@ -68,12 +69,14 @@ export default function SSHServersPage() {
|
|||||||
const [dialogError, setDialogError] = useState<string | null>(null)
|
const [dialogError, setDialogError] = useState<string | null>(null)
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
const [deleteItem, setDeleteItem] = useState<SSHAccessProfile | null>(null)
|
const [deleteItem, setDeleteItem] = useState<SSHAccessProfile | null>(null)
|
||||||
|
const [deleteConfirm, setDeleteConfirm] = useState('')
|
||||||
const [serverDialogOpen, setServerDialogOpen] = useState(false)
|
const [serverDialogOpen, setServerDialogOpen] = useState(false)
|
||||||
const [editingServerID, setEditingServerID] = useState<string | null>(null)
|
const [editingServerID, setEditingServerID] = useState<string | null>(null)
|
||||||
const [serverForm, setServerForm] = useState<SSHServerFormState>(emptyServerForm())
|
const [serverForm, setServerForm] = useState<SSHServerFormState>(emptyServerForm())
|
||||||
const [serverDialogError, setServerDialogError] = useState<string | null>(null)
|
const [serverDialogError, setServerDialogError] = useState<string | null>(null)
|
||||||
const [serverSaving, setServerSaving] = useState(false)
|
const [serverSaving, setServerSaving] = useState(false)
|
||||||
const [deleteServerItem, setDeleteServerItem] = useState<SSHServer | null>(null)
|
const [deleteServerItem, setDeleteServerItem] = useState<SSHServer | null>(null)
|
||||||
|
const [deleteServerConfirm, setDeleteServerConfirm] = useState('')
|
||||||
const [viewServerItem, setViewServerItem] = useState<SSHServer | null>(null)
|
const [viewServerItem, setViewServerItem] = useState<SSHServer | null>(null)
|
||||||
const [viewSharedServerItem, setViewSharedServerItem] = useState<SSHServer | null>(null)
|
const [viewSharedServerItem, setViewSharedServerItem] = useState<SSHServer | null>(null)
|
||||||
const [hostKeyServer, setHostKeyServer] = useState<SSHServer | null>(null)
|
const [hostKeyServer, setHostKeyServer] = useState<SSHServer | null>(null)
|
||||||
@@ -342,10 +345,12 @@ export default function SSHServersPage() {
|
|||||||
try {
|
try {
|
||||||
await api.deleteSSHAccessProfileForSelf(deleteItem.id)
|
await api.deleteSSHAccessProfileForSelf(deleteItem.id)
|
||||||
setDeleteItem(null)
|
setDeleteItem(null)
|
||||||
|
setDeleteConfirm('')
|
||||||
await load()
|
await load()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Failed to delete SSH access profile')
|
setError(err instanceof Error ? err.message : 'Failed to delete SSH access profile')
|
||||||
setDeleteItem(null)
|
setDeleteItem(null)
|
||||||
|
setDeleteConfirm('')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,10 +400,12 @@ export default function SSHServersPage() {
|
|||||||
try {
|
try {
|
||||||
await api.deleteSSHServerForSelf(deleteServerItem.id)
|
await api.deleteSSHServerForSelf(deleteServerItem.id)
|
||||||
setDeleteServerItem(null)
|
setDeleteServerItem(null)
|
||||||
|
setDeleteServerConfirm('')
|
||||||
await load()
|
await load()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : 'Failed to delete SSH server')
|
setError(err instanceof Error ? err.message : 'Failed to delete SSH server')
|
||||||
setDeleteServerItem(null)
|
setDeleteServerItem(null)
|
||||||
|
setDeleteServerConfirm('')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,7 +494,7 @@ export default function SSHServersPage() {
|
|||||||
<ListRowActionButton onClick={() => openEdit(item)}>Edit</ListRowActionButton>
|
<ListRowActionButton onClick={() => openEdit(item)}>Edit</ListRowActionButton>
|
||||||
) : null}
|
) : null}
|
||||||
{item.owner_scope === 'user' ? (
|
{item.owner_scope === 'user' ? (
|
||||||
<ListRowActionButton color="error" onClick={() => setDeleteItem(item)}>
|
<ListRowActionButton color="error" onClick={() => { setDeleteItem(item); setDeleteConfirm('') }}>
|
||||||
Delete
|
Delete
|
||||||
</ListRowActionButton>
|
</ListRowActionButton>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -547,7 +554,7 @@ export default function SSHServersPage() {
|
|||||||
<ListRowActions>
|
<ListRowActions>
|
||||||
<ListRowActionButton onClick={() => setViewServerItem(item)}>View</ListRowActionButton>
|
<ListRowActionButton onClick={() => setViewServerItem(item)}>View</ListRowActionButton>
|
||||||
<ListRowActionButton onClick={() => openServerEdit(item)}>Edit</ListRowActionButton>
|
<ListRowActionButton onClick={() => openServerEdit(item)}>Edit</ListRowActionButton>
|
||||||
<ListRowActionButton color="error" onClick={() => setDeleteServerItem(item)}>
|
<ListRowActionButton color="error" onClick={() => { setDeleteServerItem(item); setDeleteServerConfirm('') }}>
|
||||||
Delete
|
Delete
|
||||||
</ListRowActionButton>
|
</ListRowActionButton>
|
||||||
<ListRowActionButton onClick={() => void openHostKeys(item)}>Host Keys</ListRowActionButton>
|
<ListRowActionButton onClick={() => void openHostKeys(item)}>Host Keys</ListRowActionButton>
|
||||||
@@ -646,20 +653,18 @@ export default function SSHServersPage() {
|
|||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Dialog open={Boolean(deleteItem)} onClose={() => setDeleteItem(null)} maxWidth="xs" fullWidth>
|
<ConfirmDeleteDialog
|
||||||
<DialogTitle>Delete SSH Access Profile</DialogTitle>
|
open={Boolean(deleteItem)}
|
||||||
<FormDialogContent>
|
title="Delete SSH Access Profile"
|
||||||
<Typography variant="body2">
|
prompt="Type the profile name to confirm deletion."
|
||||||
Delete <strong>{deleteItem?.name}</strong>?
|
expectedText={deleteItem?.name}
|
||||||
</Typography>
|
confirmLabel="Profile Name"
|
||||||
</FormDialogContent>
|
confirmValue={deleteConfirm}
|
||||||
<DialogActions>
|
onConfirmValueChange={setDeleteConfirm}
|
||||||
<Button variant="contained" color="error" onClick={handleDelete}>
|
confirmDisabled={!deleteItem}
|
||||||
Delete
|
onConfirm={handleDelete}
|
||||||
</Button>
|
onClose={() => { setDeleteItem(null); setDeleteConfirm('') }}
|
||||||
<Button onClick={() => setDeleteItem(null)}>Cancel</Button>
|
/>
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<SSHServerFormDialog
|
<SSHServerFormDialog
|
||||||
open={serverDialogOpen}
|
open={serverDialogOpen}
|
||||||
@@ -682,20 +687,18 @@ export default function SSHServersPage() {
|
|||||||
onClose={() => setViewSharedServerItem(null)}
|
onClose={() => setViewSharedServerItem(null)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Dialog open={Boolean(deleteServerItem)} onClose={() => setDeleteServerItem(null)} maxWidth="xs" fullWidth>
|
<ConfirmDeleteDialog
|
||||||
<DialogTitle>Delete SSH Server</DialogTitle>
|
open={Boolean(deleteServerItem)}
|
||||||
<FormDialogContent>
|
title="Delete SSH Server"
|
||||||
<Typography variant="body2">
|
prompt="Type the server name to confirm deletion."
|
||||||
Delete <strong>{deleteServerItem?.name}</strong>?
|
expectedText={deleteServerItem?.name}
|
||||||
</Typography>
|
confirmLabel="Server Name"
|
||||||
</FormDialogContent>
|
confirmValue={deleteServerConfirm}
|
||||||
<DialogActions>
|
onConfirmValueChange={setDeleteServerConfirm}
|
||||||
<Button variant="contained" color="error" onClick={handleServerDelete}>
|
confirmDisabled={!deleteServerItem}
|
||||||
Delete
|
onConfirm={handleServerDelete}
|
||||||
</Button>
|
onClose={() => { setDeleteServerItem(null); setDeleteServerConfirm('') }}
|
||||||
<Button onClick={() => setDeleteServerItem(null)}>Cancel</Button>
|
/>
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog open={Boolean(connectPasswordItem)} onClose={closeConnectPasswordPrompt} maxWidth="xs" fullWidth>
|
<Dialog open={Boolean(connectPasswordItem)} onClose={closeConnectPasswordPrompt} maxWidth="xs" fullWidth>
|
||||||
<DialogTitle>Enter SSH Credentials</DialogTitle>
|
<DialogTitle>Enter SSH Credentials</DialogTitle>
|
||||||
|
|||||||
Reference in New Issue
Block a user