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