Compare commits
2 Commits
0dad181e9a
...
4e7d3fc0ec
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e7d3fc0ec | |||
| 2d14e02211 |
@@ -1,6 +1,7 @@
|
||||
import Box from '@mui/material/Box'
|
||||
import Button from '@mui/material/Button'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import Link from '@mui/material/Link'
|
||||
import List from '@mui/material/List'
|
||||
import ListItem from '@mui/material/ListItem'
|
||||
import TextField from '@mui/material/TextField'
|
||||
@@ -48,6 +49,7 @@ export default function AdminSSHServersPage() {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [search, setSearch] = useState('')
|
||||
const [groupSearch, setGroupSearch] = useState('')
|
||||
const [dialogOpen, setDialogOpen] = useState(false)
|
||||
const [editingID, setEditingID] = useState<string | null>(null)
|
||||
const [form, setForm] = useState<SSHServerFormState>(emptyForm())
|
||||
@@ -119,6 +121,39 @@ export default function AdminSSHServersPage() {
|
||||
)
|
||||
}, [items, search])
|
||||
|
||||
const filteredGroups = useMemo(() => {
|
||||
const needle: string = groupSearch.trim().toLowerCase()
|
||||
|
||||
if (!needle) {
|
||||
return groups
|
||||
}
|
||||
return groups.filter((item: SSHServerGroup) =>
|
||||
[
|
||||
item.name,
|
||||
item.id,
|
||||
item.description,
|
||||
(item.server_ids || []).join(' ')
|
||||
]
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
.includes(needle)
|
||||
)
|
||||
}, [groups, groupSearch])
|
||||
|
||||
const groupsByServerID = useMemo(() => {
|
||||
const next: Record<string, SSHServerGroup[]> = {}
|
||||
|
||||
groups.forEach((group: SSHServerGroup) => {
|
||||
(group.server_ids || []).forEach((serverID: string) => {
|
||||
if (!next[serverID]) {
|
||||
next[serverID] = []
|
||||
}
|
||||
next[serverID].push(group)
|
||||
})
|
||||
})
|
||||
return next
|
||||
}, [groups])
|
||||
|
||||
const openCreate = () => {
|
||||
setEditingID(null)
|
||||
setForm(emptyForm())
|
||||
@@ -444,7 +479,10 @@ export default function AdminSSHServersPage() {
|
||||
</Typography>
|
||||
) : null}
|
||||
<List>
|
||||
{filteredItems.map((item) => (
|
||||
{filteredItems.map((item) => {
|
||||
const serverGroups: SSHServerGroup[] = groupsByServerID[item.id] || []
|
||||
|
||||
return (
|
||||
<ListItem key={item.id} divider sx={{ alignItems: 'flex-start' }}>
|
||||
<CompactListItemText
|
||||
primary={
|
||||
@@ -456,6 +494,19 @@ export default function AdminSSHServersPage() {
|
||||
secondary={
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{item.host}:{item.port}· Tags: {(item.tags || []).join(', ') || '-'}· Updated: {fmt(item.updated_at)}
|
||||
<br />
|
||||
Groups: {serverGroups.length ? serverGroups.map((group: SSHServerGroup, index: number) => (
|
||||
<Box key={group.id} component="span">
|
||||
{index > 0 ? ',' : ''}
|
||||
<Link
|
||||
underline="hover"
|
||||
onClick={() => setViewGroupItem(group)}
|
||||
sx={{ cursor: 'default' }}
|
||||
>
|
||||
{group.name}
|
||||
</Link>
|
||||
</Box>
|
||||
)) : '-'}
|
||||
{item.description? (<><br />{item.description}</>): null}
|
||||
</Typography>
|
||||
}
|
||||
@@ -473,7 +524,8 @@ export default function AdminSSHServersPage() {
|
||||
<ListRowActionButton onClick={() => void openHostKeys(item)}>Host Keys</ListRowActionButton>
|
||||
</ListRowActions>
|
||||
</ListItem>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
</SectionCard>
|
||||
@@ -481,7 +533,18 @@ export default function AdminSSHServersPage() {
|
||||
<SectionCard
|
||||
collapsible
|
||||
collapseStorageKey="admin-ssh-servers:ssh-server-groups"
|
||||
title={`SSH Server Groups (${groups.length})`}
|
||||
title={
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, minWidth: 0, flexWrap: 'wrap' }}>
|
||||
<Typography variant="h6">SSH Server Groups ({filteredGroups.length})</Typography>
|
||||
<TextField
|
||||
size="small"
|
||||
label="Search"
|
||||
value={groupSearch}
|
||||
onChange={(event) => setGroupSearch(event.target.value)}
|
||||
sx={{ minWidth: 240, maxWidth: 320 }}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
actions={
|
||||
<Button variant="outlined" onClick={openGroupCreate}>
|
||||
New Group
|
||||
@@ -489,7 +552,7 @@ export default function AdminSSHServersPage() {
|
||||
}
|
||||
>
|
||||
<List>
|
||||
{groups.map((item) => (
|
||||
{filteredGroups.map((item) => (
|
||||
<ListItem key={item.id} divider sx={{ alignItems: 'flex-start' }}>
|
||||
<CompactListItemText
|
||||
primary={
|
||||
|
||||
Reference in New Issue
Block a user