diff --git a/assets/src/components/account/groups/GroupEdit.tsx b/assets/src/components/account/groups/GroupEdit.tsx
index 9cf3697e92..f2c0acb286 100644
--- a/assets/src/components/account/groups/GroupEdit.tsx
+++ b/assets/src/components/account/groups/GroupEdit.tsx
@@ -1,31 +1,48 @@
import { useApolloClient, useMutation } from '@apollo/client'
import {
+ Button,
ComboBox,
FormField,
Modal,
- Tab,
- TabList,
- TabPanel,
+ PeopleIcon,
+ PersonPlusIcon,
+ Stepper,
ValidatedInput,
} from '@pluralsh/design-system'
-import { useEffect, useRef, useState } from 'react'
-import { Flex } from 'honorable'
+import { useEffect, useState } from 'react'
+import { Div, Flex } from 'honorable'
import { fetchUsers } from 'components/utils/BindingInput'
-import { GqlError } from '../../utils/Alert'
+import { StepperSteps } from '@pluralsh/design-system/dist/components/Stepper'
-import { Actions } from '../../utils/Actions'
+import { GqlError } from '../../utils/Alert'
import { CREATE_GROUP_MEMBERS, GROUP_MEMBERS, UPDATE_GROUP } from './queries'
import GroupMembers from './GroupMembers'
-const TABS = {
- Attributes: { label: 'Attributes' },
- Users: { label: 'Users' },
+const stepBase = {
+ circleSize: 32,
+ iconSize: 16,
+ vertical: true,
}
+const steps: StepperSteps = [
+ {
+ key: 'info',
+ stepTitle:
Group info
,
+ IconComponent: PeopleIcon,
+ ...stepBase,
+ },
+ {
+ key: 'bindings',
+ stepTitle: User bindings
,
+ IconComponent: PersonPlusIcon,
+ ...stepBase,
+ },
+]
+
export default function GroupEdit({ group, edit, setEdit }: any) {
const client = useApolloClient()
const [value, setValue] = useState('')
@@ -40,8 +57,7 @@ export default function GroupEdit({ group, edit, setEdit }: any) {
refetchQueries: [{ query: GROUP_MEMBERS, variables: { id: group.id } }],
})
const [suggestions, setSuggestions] = useState([])
- const tabStateRef = useRef(null)
- const [view, setView] = useState('Attributes')
+ const [step, setStep] = useState<0|1>(0)
// Run only on first render. Make sure there will be data in Combo Box to start with.
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -54,98 +70,126 @@ export default function GroupEdit({ group, edit, setEdit }: any) {
return (
setEdit(false)}
- actions={(
- setEdit(false)}
- submit={() => mutation()}
- loading={loading}
- action="Update"
- />
- )}
>
+
+
+
+
+ {/* Group info */}
+ {step === 0 && (
+
+ setName(value)}
+ />
+ setDescription(value)}
+ />
+
+ )}
+
+ {/* Bindings */}
+ {step === 1 && (
+
+
+ {
+ setValue('')
+ // @ts-expect-error
+ addMut({ variables: { userId: key } })
+ }}
+ onInputChange={value => {
+ setValue(value)
+ fetchUsers(client, value, setSuggestions)
+ }}
+ >
+ {suggestions.map(({ label }) => label)}
+
+
+
+
+ )}
+
{error && (
)}
- setView(key as string),
- }}
+
+
- {Object.entries(TABS).map(([key, { label }]) => (
- {label}
- ))}
-
-
- {view === 'Attributes' && (
-
- setName(value)}
- />
- setDescription(value)}
- />
-
+ {step === 0 && (
+ <>
+
+
+ >
)}
- {view === 'Users' && (
-
-
+
-
-
+ Back
+
+
+ >
)}
-
+
)
diff --git a/assets/src/components/account/roles/Role.tsx b/assets/src/components/account/roles/Role.tsx
index 79b058d873..2239840bcb 100644
--- a/assets/src/components/account/roles/Role.tsx
+++ b/assets/src/components/account/roles/Role.tsx
@@ -74,7 +74,7 @@ export default function Role({ role, q }: any) {
setConfirm(false)}
submit={() => mutation()}
loading={loading}
diff --git a/assets/src/components/account/roles/RoleCreate.tsx b/assets/src/components/account/roles/RoleCreate.tsx
index 6476419fc1..97deefb7cf 100644
--- a/assets/src/components/account/roles/RoleCreate.tsx
+++ b/assets/src/components/account/roles/RoleCreate.tsx
@@ -7,8 +7,6 @@ import isEqual from 'lodash/isEqual'
import { appendConnection, updateCache } from '../../../utils/graphql'
-import { Actions } from '../../utils/Actions'
-
import { sanitize } from './misc'
import { CREATE_ROLE, ROLES_Q } from './queries'
@@ -54,24 +52,20 @@ export default function RoleCreate({ q }: any) {
Create role
resetAndClose()}
marginVertical={16}
size="large"
- actions={(
- resetAndClose()}
- submit={() => mutation()}
- loading={loading}
- />
- )}
>
resetAndClose()}
+ submit={() => mutation()}
+ loading={loading}
error={error}
/>
diff --git a/assets/src/components/account/roles/RoleEdit.tsx b/assets/src/components/account/roles/RoleEdit.tsx
index a0dbc641bc..e7d3556898 100644
--- a/assets/src/components/account/roles/RoleEdit.tsx
+++ b/assets/src/components/account/roles/RoleEdit.tsx
@@ -6,8 +6,6 @@ import isEqual from 'lodash/isEqual'
import pick from 'lodash/pick'
-import { Actions } from '../../utils/Actions'
-
import { sanitize } from './misc'
import { UPDATE_ROLE } from './queries'
@@ -32,20 +30,16 @@ export default function RoleEdit({ role, open, setOpen }: any) {
open={open}
size="large"
onClose={() => setOpen(false)}
- actions={(
- setOpen(false)}
- submit={() => mutation()}
- loading={loading}
- action="Update"
- />
- )}
>
setOpen(false)}
+ submit={() => mutation()}
+ loading={loading}
error={error}
/>
diff --git a/assets/src/components/account/roles/RoleForm.tsx b/assets/src/components/account/roles/RoleForm.tsx
index e26a77c634..b1fe290b9c 100644
--- a/assets/src/components/account/roles/RoleForm.tsx
+++ b/assets/src/components/account/roles/RoleForm.tsx
@@ -1,96 +1,171 @@
-import { Box } from 'grommet'
-import { Span } from 'honorable'
-import { Tab, TabList, TabPanel } from '@pluralsh/design-system'
-import { useRef, useState } from 'react'
+import { Div, Flex, P } from 'honorable'
+import {
+ BriefcaseIcon,
+ Button,
+ PeopleIcon,
+ Stepper,
+ ValidatedInput,
+} from '@pluralsh/design-system'
+import { useState } from 'react'
+import { StepperSteps } from '@pluralsh/design-system/dist/components/Stepper'
import { GqlError } from '../../utils/Alert'
-import RoleFormGeneralAttributes from './RoleFormGeneralAttributes'
-
+import RoleFormBindings from './RoleFormBindings'
import RolePermissionToggle from './RolePermissionToggle'
-const TABS = {
- General: { label: 'General' },
- Permissions: { label: 'Permissions' },
+const stepBase = {
+ circleSize: 32,
+ iconSize: 16,
+ vertical: true,
}
+const steps: StepperSteps = [
+ {
+ key: 'create-repo',
+ stepTitle: Role info
,
+ IconComponent: BriefcaseIcon,
+ ...stepBase,
+ },
+ {
+ key: 'choose-cloud',
+ stepTitle: Bindings
,
+ IconComponent: PeopleIcon,
+ ...stepBase,
+ },
+]
+
const PermissionTypes = {
- READ: 'can view components',
- CONFIGURE: 'can edit helm/terraform configuration',
- DEPLOY: 'can create/approve deployments',
- OPERATE: 'can delete pods, export logs, and other operational tasks',
+ READ: 'Can view components',
+ CONFIGURE: 'Can edit helm/terraform configuration',
+ DEPLOY: 'Can create/approve deployments',
+ OPERATE: 'Can delete pods, export logs, and other operational tasks',
}
export default function RoleForm({
+ label = 'Create',
+ cancel,
+ submit,
+ loading,
error,
attributes,
setAttributes,
bindings,
setBindings,
- ...box
}): any {
- const [view, setView] = useState('General')
+ const [step, setStep] = useState<0|1>(0)
const permissions = Object.entries(PermissionTypes)
const len = permissions.length
- const tabStateRef = useRef(null)
return (
-
+
+
+
+
+ {/* Role info */}
+ {step === 0 && (
+
+ setAttributes({ ...attributes, name: value })}
+ />
+ setAttributes({ ...attributes, description: value })}
+ />
+
+ Permissions
+
+
+ {permissions.map(([perm, description], i) => (
+
+ ))}
+
+
+ )}
+
+ {/* Bindings */}
+ {step === 1 && (
+
+ )}
+
{error && (
)}
- setView(key as string),
- }}
+
+
- {Object.entries(TABS).map(([key, { label }]) => (
- {label}
- ))}
-
-
- {view === 'General' && (
-
+ {step === 0 && (
+ <>
+
+
+ >
)}
- {view === 'Permissions' && (
-
-
- Permissions
-
- Grant permissions to all users and groups bound to this role
-
-
-
- {permissions.map(([perm, description], i) => (
-
- ))}
-
-
+
+ {step === 1 && (
+ <>
+
+
+ >
)}
-
-
+
+
)
}
diff --git a/assets/src/components/account/roles/RoleFormGeneralAttributes.tsx b/assets/src/components/account/roles/RoleFormBindings.tsx
similarity index 72%
rename from assets/src/components/account/roles/RoleFormGeneralAttributes.tsx
rename to assets/src/components/account/roles/RoleFormBindings.tsx
index 6dc2fc034c..02af1f69d2 100644
--- a/assets/src/components/account/roles/RoleFormGeneralAttributes.tsx
+++ b/assets/src/components/account/roles/RoleFormBindings.tsx
@@ -3,7 +3,7 @@ import { ValidatedInput } from '@pluralsh/design-system'
import { useState } from 'react'
import { BindingInput } from 'components/utils/BindingInput'
-export default function RoleFormGeneralAttributes({
+export default function RoleFormBindings({
attributes,
setAttributes,
bindings,
@@ -17,18 +17,8 @@ export default function RoleFormGeneralAttributes({
gap="small"
>
setAttributes({ ...attributes, name: value })}
- />
- setAttributes({ ...attributes, description: value })}
- />
- {
setRepositories(value)
diff --git a/assets/src/components/account/roles/RolePermissionToggle.tsx b/assets/src/components/account/roles/RolePermissionToggle.tsx
index 1d516f4d2b..c1d9ae4073 100644
--- a/assets/src/components/account/roles/RolePermissionToggle.tsx
+++ b/assets/src/components/account/roles/RolePermissionToggle.tsx
@@ -1,6 +1,9 @@
-import { ListItem } from 'components/utils/List'
-import { Box } from 'grommet'
-import { Span, Switch } from 'honorable'
+import {
+ Div,
+ Flex,
+ P,
+ Switch,
+} from 'honorable'
import { useCallback } from 'react'
export default function RolePermissionToggle({
@@ -8,7 +11,6 @@ export default function RolePermissionToggle({
description,
attributes,
setAttributes,
- first,
last,
}: any) {
const toggle = useCallback(enable => {
@@ -28,19 +30,30 @@ export default function RolePermissionToggle({
[permission, attributes, setAttributes])
return (
-
-
- {permission.toLowerCase()}
- {description}
-
+
+
+ {permission.toLowerCase()}
+
+
+ {description}
+
+
perm === permission)}
onChange={({ target: { checked } }) => toggle(checked)}
/>
-
+
)
}
diff --git a/assets/src/components/cluster/pods/Pods.tsx b/assets/src/components/cluster/pods/Pods.tsx
index 2b7837d798..c9afbaaf3f 100644
--- a/assets/src/components/cluster/pods/Pods.tsx
+++ b/assets/src/components/cluster/pods/Pods.tsx
@@ -36,6 +36,7 @@ import {
ColActions,
ColContainers,
ColCpuReservation,
+ ColImages,
ColMemoryReservation,
ColNameLink,
ColNamespace,
@@ -115,6 +116,7 @@ export default function AllPods() {
ColCpuReservation,
ColRestarts,
ColContainers,
+ ColImages,
ColActions(refetch),
],
[refetch])
diff --git a/assets/src/components/cluster/pods/PodsList.tsx b/assets/src/components/cluster/pods/PodsList.tsx
index a44ab08104..9ad241815e 100644
--- a/assets/src/components/cluster/pods/PodsList.tsx
+++ b/assets/src/components/cluster/pods/PodsList.tsx
@@ -1,4 +1,9 @@
-import { A, Div, Flex } from 'honorable'
+import {
+ A,
+ Div,
+ Flex,
+ Span,
+} from 'honorable'
import { Link, useNavigate } from 'react-router-dom'
import { Row, createColumnHelper } from '@tanstack/react-table'
import {
@@ -22,6 +27,8 @@ import {
import { Confirm } from 'components/utils/Confirm'
import { useMutation } from '@apollo/client'
+import { isEmpty } from 'lodash'
+
import {
LabelWithIcon,
TableCaretLink,
@@ -94,6 +101,7 @@ type PodTableRow = {
total?: number
statuses?: ContainerStatus[]
}
+ images?: string[]
}
const columnHelper = createColumnHelper()
@@ -232,6 +240,34 @@ export const ColContainers = columnHelper.accessor(row => row?.containers?.statu
header: 'Containers',
})
+export const ColImages = columnHelper.accessor(row => row?.images || [],
+ {
+ id: 'images',
+ cell: props => {
+ const images = props.getValue()
+
+ return images.map(image => (
+
+
+ {image}
+
+
+ ))
+ },
+ header: 'Images',
+ meta: {
+ truncate: true,
+ },
+ })
+
export const ColActions = refetch => columnHelper.display({
id: 'actions',
cell: ({ row: { original } }: any) => (
@@ -279,6 +315,14 @@ function getRestarts(status: Pod['status']) {
0)
}
+function getImages(containers): string[] {
+ return containers?.map(container => container?.image).filter(image => !isEmpty(image)) || []
+}
+
+function getPodImages(spec: Pod['spec']) {
+ return [...new Set([...getImages(spec?.containers), ...getImages(spec?.initContainers)])]
+}
+
export const PodsList = memo(({
pods, applications, columns, ...props
}: PodListProps) => {
@@ -313,6 +357,7 @@ export const PodsList = memo(({
},
restarts: getRestarts(pod.status),
containers: getPodContainersStats(pod.status),
+ images: getPodImages(pod.spec),
}
}),
[applications, pods])