Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Redesign role and group modals, add images to pod table #241

Merged
merged 7 commits into from
Feb 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 131 additions & 87 deletions assets/src/components/account/groups/GroupEdit.tsx
Original file line number Diff line number Diff line change
@@ -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: <Div marginRight="small">Group info</Div>,
IconComponent: PeopleIcon,
...stepBase,
},
{
key: 'bindings',
stepTitle: <Div marginRight="small">User bindings</Div>,
IconComponent: PersonPlusIcon,
...stepBase,
},
]

export default function GroupEdit({ group, edit, setEdit }: any) {
const client = useApolloClient()
const [value, setValue] = useState('')
Expand All @@ -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<any>(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
Expand All @@ -54,98 +70,126 @@ export default function GroupEdit({ group, edit, setEdit }: any) {

return (
<Modal
header="Edit group"
portal
open={edit}
size="large"
onClose={() => setEdit(false)}
actions={(
<Actions
cancel={() => setEdit(false)}
submit={() => mutation()}
loading={loading}
action="Update"
/>
)}
>
<Flex
flexDirection="column"
gap="large"
direction="column"
gap="medium"
>
<Flex>
<Stepper
compact
steps={steps}
stepIndex={step}
/>
</Flex>

{/* Group info */}
{step === 0 && (
<Flex
flexDirection="column"
gap="large"
>
<ValidatedInput
label="Name"
value={name}
onChange={({ target: { value } }) => setName(value)}
/>
<ValidatedInput
label="Description"
value={description}
onChange={({ target: { value } }) => setDescription(value)}
/>
</Flex>
)}

{/* Bindings */}
{step === 1 && (
<Flex
flexDirection="column"
gap="large"
>
<FormField
label="Add users"
width="100%"
{...{
'& :last-child': {
marginTop: 0,
},
}}
>
<ComboBox
inputValue={value}
// @ts-expect-error
placeholder="Search a user"
onSelectionChange={key => {
setValue('')
// @ts-expect-error
addMut({ variables: { userId: key } })
}}
onInputChange={value => {
setValue(value)
fetchUsers(client, value, setSuggestions)
}}
>
{suggestions.map(({ label }) => label)}
</ComboBox>
</FormField>
<GroupMembers
group={group}
edit
/>
</Flex>
)}

{error && (
<GqlError
header="Something went wrong"
error={error}
/>
)}
<TabList
stateRef={tabStateRef}
stateProps={{
orientation: 'horizontal',
selectedKey: view,
onSelectionChange: key => setView(key as string),
}}

<Flex
gap="medium"
justify="end"
>
{Object.entries(TABS).map(([key, { label }]) => (
<Tab key={key}>{label}</Tab>
))}
</TabList>
<TabPanel stateRef={tabStateRef}>
{view === 'Attributes' && (
<Flex
flexDirection="column"
gap="large"
>
<ValidatedInput
label="Name"
value={name}
onChange={({ target: { value } }) => setName(value)}
/>
<ValidatedInput
label="Description"
value={description}
onChange={({ target: { value } }) => setDescription(value)}
/>
</Flex>
{step === 0 && (
<>
<Button
secondary
onClick={() => setEdit(false)}
>
Cancel
</Button>
<Button
onClick={() => setStep(1)}
loading={loading}
>
Next
</Button>
</>
)}
{view === 'Users' && (
<Flex
flexDirection="column"
gap="large"
>
<FormField
label="Add users"
width="100%"
{...{
'& :last-child': {
marginTop: 0,
},
}}

{step === 1 && (
<>
<Button
secondary
onClick={() => setStep(0)}
>
<ComboBox
inputValue={value}
// @ts-expect-error
placeholder="Search a user"
onSelectionChange={key => {
setValue('')
// @ts-expect-error
addMut({ variables: { userId: key } })
}}
onInputChange={value => {
setValue(value)
fetchUsers(client, value, setSuggestions)
}}
>
{suggestions.map(({ label }) => label)}
</ComboBox>
</FormField>
<GroupMembers
group={group}
edit
/>
</Flex>
Back
</Button>
<Button
onClick={() => mutation()}
loading={loading}
>
Update
</Button>
</>
)}
</TabPanel>
</Flex>
</Flex>
</Modal>
)
Expand Down
2 changes: 1 addition & 1 deletion assets/src/components/account/roles/Role.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default function Role({ role, q }: any) {
<Confirm
open={confirm}
title="Delete role"
text="Are you sure? Deleting roles cannot be undone."
text="Are you sure you want to delete this role? This could have downstream effects on a large number of users."
close={() => setConfirm(false)}
submit={() => mutation()}
loading={loading}
Expand Down
14 changes: 4 additions & 10 deletions assets/src/components/account/roles/RoleCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -54,24 +52,20 @@ export default function RoleCreate({ q }: any) {
Create role
</Button>
<Modal
header="Create role"
open={open}
onClose={() => resetAndClose()}
marginVertical={16}
size="large"
actions={(
<Actions
cancel={() => resetAndClose()}
submit={() => mutation()}
loading={loading}
/>
)}
>
<RoleForm
attributes={attributes}
setAttributes={setAttributes}
bindings={uniqueRoleBindings}
setBindings={setRoleBindings}
label="Create"
cancel={() => resetAndClose()}
submit={() => mutation()}
loading={loading}
error={error}
/>
</Modal>
Expand Down
14 changes: 4 additions & 10 deletions assets/src/components/account/roles/RoleEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -32,20 +30,16 @@ export default function RoleEdit({ role, open, setOpen }: any) {
open={open}
size="large"
onClose={() => setOpen(false)}
actions={(
<Actions
cancel={() => setOpen(false)}
submit={() => mutation()}
loading={loading}
action="Update"
/>
)}
>
<RoleForm
attributes={attributes}
setAttributes={setAttributes}
bindings={uniqueRoleBindings}
setBindings={setRoleBindings}
label="Update"
cancel={() => setOpen(false)}
submit={() => mutation()}
loading={loading}
error={error}
/>
</Modal>
Expand Down
Loading