Skip to content

Commit

Permalink
Add filter to Policies and Roles tables, refactor filter (#3690)
Browse files Browse the repository at this point in the history
  • Loading branch information
fiskus authored Aug 24, 2023
1 parent ac96f3d commit fe48902
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 193 deletions.
27 changes: 10 additions & 17 deletions catalog/app/containers/Admin/Buckets/Buckets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { useTracker } from 'utils/tracking'
import * as Types from 'utils/types'
import * as validators from 'utils/validators'

import Filter from '../Filter'
import * as Form from '../Form'
import * as Table from '../Table'

Expand Down Expand Up @@ -1186,7 +1185,7 @@ function CustomBucketIcon({ src }: CustomBucketIconProps) {
return <BucketIcon alt="" classes={classes} src={src} title="Default icon" />
}

const columns = [
const columns: Table.Column<BucketConfig>[] = [
{
id: 'name',
label: 'Name (relevance)',
Expand Down Expand Up @@ -1272,17 +1271,14 @@ interface CRUDProps {

function CRUD({ bucketName }: CRUDProps) {
const { bucketConfigs: rows } = GQL.useQueryS(BUCKET_CONFIGS_QUERY)
const [filter, setFilter] = React.useState('')
const filtered = React.useMemo(
() =>
filter
? rows.filter(({ name, title }) =>
(name + title).toLowerCase().includes(filter.toLowerCase()),
)
: rows,
[filter, rows],
)
const ordering = Table.useOrdering({ rows: filtered, column: columns[0] })
const filtering = Table.useFiltering({
rows,
filterBy: ({ name, title }) => name + title,
})
const ordering = Table.useOrdering({
rows: filtering.filtered,
column: columns[0],
})
const pagination = Pagination.use(ordering.ordered, {
// @ts-expect-error
getItemId: R.prop('name'),
Expand Down Expand Up @@ -1346,8 +1342,7 @@ function CRUD({ bucketName }: CRUDProps) {
</M.Dialog>

<Table.Toolbar heading="Buckets" actions={toolbarActions}>
{/* @ts-expect-error */}
<Filter value={filter} onChange={setFilter} />
<Table.Filter {...filtering} />
</Table.Toolbar>
<Table.Wrapper>
<M.Table size="small">
Expand All @@ -1361,9 +1356,7 @@ function CRUD({ bucketName }: CRUDProps) {
style={{ cursor: 'pointer' }}
>
{columns.map((col) => (
// @ts-expect-error
<M.TableCell key={col.id} align={col.align} {...col.props}>
{/* @ts-expect-error */}
{(col.getDisplay || R.identity)(col.getValue(i), i)}
</M.TableCell>
))}
Expand Down
27 changes: 24 additions & 3 deletions catalog/app/containers/Admin/RolesAndPolicies/AssociatedRoles.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import * as React from 'react'
import * as R from 'ramda'
import * as RF from 'react-final-form'
import * as M from '@material-ui/core'

import * as GQL from 'utils/GraphQL'

import * as Table from '../Table'
import Filter from './Filter'
import { MAX_POLICIES_PER_ROLE } from './shared'

import ROLES_QUERY from './gql/Roles.generated'
Expand Down Expand Up @@ -45,12 +48,26 @@ function RoleSelectionDialog({
[setSelected],
)

const filtering = Table.useFiltering({
rows: roles,
filterBy: ({ name }: ManagedRole) => name,
})
const ordered = React.useMemo(
() => R.sortBy(({ name }: ManagedRole) => name, filtering.filtered),
[filtering.filtered],
)

return (
<M.Dialog maxWidth="xs" open={open} onClose={onClose} onExited={handleExited}>
<M.DialogTitle>Attach policy to roles</M.DialogTitle>
{roles.length && (
<M.Box ml={3} mr={1.5}>
<Filter {...filtering} />
</M.Box>
)}
<M.DialogContent dividers>
{roles.length ? (
roles.map((role) => (
{ordered.length ? (
ordered.map((role) => (
<M.FormControlLabel
key={role.id}
style={{ display: 'flex', marginRight: 0 }}
Expand All @@ -73,7 +90,11 @@ function RoleSelectionDialog({
/>
))
) : (
<M.Typography>No more roles to attach this policy to</M.Typography>
<M.Typography>
{filtering.value
? 'No roles found, try resetting filter'
: 'No more roles to attach this policy to'}
</M.Typography>
)}
</M.DialogContent>
<M.DialogActions>
Expand Down
71 changes: 46 additions & 25 deletions catalog/app/containers/Admin/RolesAndPolicies/AttachedPolicies.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import * as React from 'react'
import * as R from 'ramda'
import * as RF from 'react-final-form'
import * as M from '@material-ui/core'

import * as GQL from 'utils/GraphQL'
import StyledLink from 'utils/StyledLink'

import * as Table from '../Table'
import Filter from './Filter'
import { MAX_POLICIES_PER_ROLE } from './shared'

import POLICIES_QUERY from './gql/Policies.generated'
Expand Down Expand Up @@ -38,34 +41,52 @@ function PolicySelectionDialog({
[setSelected, onClose],
)

const filtering = Table.useFiltering({
rows: policies,
filterBy: ({ title }: Policy) => title,
})
const ordered = React.useMemo(
() => R.sortBy(({ title }: Policy) => title, filtering.filtered),
[filtering.filtered],
)

return (
<M.Dialog maxWidth="xs" open={open} onClose={onClose} onExited={handleExited}>
<M.DialogTitle>Attach a policy</M.DialogTitle>
<M.List dense>
{policies.length ? (
policies.map((policy) => (
<M.ListItem button key={policy.id} onClick={() => select(policy)}>
<M.ListItemText>
{policy.title}
<M.Box component="span" color="text.secondary">
{' '}
(
{policy.managed ? (
<>{policy.permissions.length} buckets</>
) : (
<>unmanaged</>
)}
)
</M.Box>
</M.ListItemText>
</M.ListItem>
))
) : (
<M.DialogContent dividers>
<M.Typography>No more policies to attach</M.Typography>
</M.DialogContent>
)}
</M.List>
{!!policies.length && (
<M.Box ml={3} mr={1.5}>
<Filter {...filtering} />
</M.Box>
)}
<M.DialogContent dividers>
<M.List dense>
{ordered.length ? (
ordered.map((policy) => (
<M.ListItem button key={policy.id} onClick={() => select(policy)}>
<M.ListItemText>
{policy.title}
<M.Box component="span" color="text.secondary">
{' '}
(
{policy.managed ? (
<>{policy.permissions.length} buckets</>
) : (
<>unmanaged</>
)}
)
</M.Box>
</M.ListItemText>
</M.ListItem>
))
) : (
<M.Typography>
{filtering.value
? 'No policies found, try resetting filter'
: 'No more policies to attach'}
</M.Typography>
)}
</M.List>
</M.DialogContent>
<M.DialogActions>
<M.Button autoFocus onClick={onClose} color="primary">
Cancel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react'
import * as R from 'ramda'
import * as RF from 'react-final-form'
import * as M from '@material-ui/core'

Expand All @@ -7,6 +8,9 @@ import * as Model from 'model'
import * as GQL from 'utils/GraphQL'
import StyledLink from 'utils/StyledLink'

import * as Table from '../Table'
import Filter from './Filter'

import BUCKETS_QUERY from './gql/Buckets.generated'
import { BucketPermissionSelectionFragment as BucketPermission } from './gql/BucketPermissionSelection.generated'

Expand Down Expand Up @@ -39,33 +43,51 @@ function BucketAddDialog({ open, onClose, buckets, addBucket }: BucketAddDialogP
[onClose, select],
)

const filtering = Table.useFiltering({
rows: buckets,
filterBy: ({ name, title }: Bucket) => name + title,
})
const ordered = React.useMemo(
() => R.sortBy(({ name }: Bucket) => name, filtering.filtered),
[filtering.filtered],
)

return (
<M.Dialog maxWidth="xs" open={open} onClose={onClose} onExited={handleExited}>
<M.DialogTitle>Add a bucket</M.DialogTitle>
{buckets.length ? (
<M.List>
{buckets.map((bucket) => (
<M.ListItem key={bucket.name} button onClick={() => handleAdd(bucket)}>
<M.ListItemAvatar style={{ minWidth: 44 }}>
<M.Avatar
style={{ width: 32, height: 32 }}
src={bucket.iconUrl || defaultBucketIcon}
/>
</M.ListItemAvatar>
<M.ListItemText>
s3://{bucket.name}{' '}
<M.Box component="span" color="text.secondary" ml={0.5}>
{bucket.title}
</M.Box>
</M.ListItemText>
</M.ListItem>
))}
</M.List>
) : (
<M.DialogContent>
<M.Typography>No more buckets to add</M.Typography>
</M.DialogContent>
{!!buckets.length && (
<M.Box ml={3} mr={1.5}>
<Filter {...filtering} />
</M.Box>
)}
<M.DialogContent dividers>
{ordered.length ? (
<M.List>
{ordered.map((bucket) => (
<M.ListItem key={bucket.name} button onClick={() => handleAdd(bucket)}>
<M.ListItemAvatar style={{ minWidth: 44 }}>
<M.Avatar
style={{ width: 32, height: 32 }}
src={bucket.iconUrl || defaultBucketIcon}
/>
</M.ListItemAvatar>
<M.ListItemText>
s3://{bucket.name}{' '}
<M.Box component="span" color="text.secondary" ml={0.5}>
{bucket.title}
</M.Box>
</M.ListItemText>
</M.ListItem>
))}
</M.List>
) : (
<M.Typography>
{filtering.value
? 'No buckets found, try resetting filter'
: 'No more buckets to add'}
</M.Typography>
)}
</M.DialogContent>
<M.DialogActions>
<M.Button autoFocus onClick={onClose} color="primary">
Cancel
Expand Down
26 changes: 26 additions & 0 deletions catalog/app/containers/Admin/RolesAndPolicies/Filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as React from 'react'
import * as M from '@material-ui/core'

interface FilterProps {
value: string
onChange: (v: string) => void
}

export default function Filter({ value, onChange }: FilterProps) {
return (
<M.InputBase
endAdornment={
value && (
<M.IconButton onClick={() => onChange('')}>
<M.Icon fontSize="small">clear</M.Icon>
</M.IconButton>
)
}
fullWidth
onChange={(event) => onChange(event.target.value)}
placeholder="Filter"
startAdornment={<M.Icon fontSize="small">search</M.Icon>}
value={value}
/>
)
}
21 changes: 14 additions & 7 deletions catalog/app/containers/Admin/RolesAndPolicies/Policies.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const validateNonEmptyString: FF.FieldValidator<any> = validate(
validators.matches(/\S/),
)

const columns = [
const columns: Table.Column<Policy>[] = [
{
id: 'title',
label: 'Title',
Expand Down Expand Up @@ -649,7 +649,14 @@ interface DialogsOpenProps {
export default function Policies() {
const { policies: rows } = GQL.useQueryS(POLICIES_QUERY)

const ordering = Table.useOrdering({ rows, column: columns[0] })
const filtering = Table.useFiltering({
rows,
filterBy: ({ title }) => title,
})
const ordering = Table.useOrdering({
rows: filtering.filtered,
column: columns[0],
})
const dialogs = Dialogs.use()

const toolbarActions = [
Expand All @@ -664,11 +671,11 @@ export default function Policies() {

const inlineActions = (policy: Policy) => [
policy.arn
? {
? ({
title: 'Open AWS Console',
icon: <M.Icon>launch</M.Icon>,
href: getArnLink(policy.arn),
}
} as Table.Action)
: null,
{
title: 'Edit',
Expand Down Expand Up @@ -697,22 +704,22 @@ export default function Policies() {
>
<M.Paper>
{dialogs.render({ fullWidth: true, maxWidth: 'sm' })}
<Table.Toolbar heading="Policies" actions={toolbarActions} />
<Table.Toolbar heading="Policies" actions={toolbarActions}>
<Table.Filter {...filtering} />
</Table.Toolbar>
<Table.Wrapper>
<M.Table>
<Table.Head columns={columns} ordering={ordering} withInlineActions />
<M.TableBody>
{ordering.ordered.map((i: Policy) => (
<M.TableRow hover key={i.id}>
{columns.map((col) => (
// @ts-expect-error
<M.TableCell key={col.id} {...col.props}>
{(col.getDisplay || R.identity)(col.getValue(i), i)}
</M.TableCell>
))}
<M.TableCell align="right" padding="none">
<Table.InlineActions actions={inlineActions(i)}>
{/* @ts-expect-error */}
<SettingsMenu policy={i} openDialog={dialogs.open} />
</Table.InlineActions>
</M.TableCell>
Expand Down
Loading

0 comments on commit fe48902

Please sign in to comment.