Skip to content

Commit

Permalink
feat: redesign organisation layout (#3257)
Browse files Browse the repository at this point in the history
Co-authored-by: kyle-ssg <[email protected]>
  • Loading branch information
azaiko-akveo and kyle-ssg authored Jan 24, 2024
1 parent 165f95b commit 61d0585
Show file tree
Hide file tree
Showing 19 changed files with 1,686 additions and 1,510 deletions.
1 change: 0 additions & 1 deletion frontend/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ module.exports = {
'OptionalObject': true,
'OptionalString': true,
'OrganisationProvider': true,
'OrganisationSelect': true,
'Paging': true,
'Panel': true,
'PanelSearch': true,
Expand Down
2 changes: 2 additions & 0 deletions frontend/common/providers/AccountProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const AccountProvider = class extends Component {
organisations: AccountStore.getOrganisations(),
user: AccountStore.getUser(),
})
this.props.onChange && this.props.onChange()
})

this.listenTo(AccountStore, 'loaded', () => {
Expand Down Expand Up @@ -115,6 +116,7 @@ const AccountProvider = class extends Component {

AccountProvider.propTypes = {
children: OptionalFunc,
onChange: OptionalFunc,
onLogin: OptionalFunc,
onLogout: OptionalFunc,
onNoUser: OptionalFunc,
Expand Down
3 changes: 3 additions & 0 deletions frontend/common/stores/account-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,9 @@ const store = Object.assign({}, BaseStore, {
const id = store.organisation && store.organisation.id
return id && store.getOrganisationRole(id) === 'ADMIN'
},
isSuper() {
return store.model && store.model.is_superuser
},
setToken(token) {
data.token = token
},
Expand Down
2 changes: 1 addition & 1 deletion frontend/e2e/helpers.cafe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ export const login = async (email: string, password: string) => {
await setText('[name="email"]', `${email}`)
await setText('[name="password"]', `${password}`)
await click('#login-btn')
await waitForElementVisible('#project-select-page')
await waitForElementVisible('#project-manage-widget')
}
export const logout = async (t) => {
await click('#account-settings-link')
Expand Down
2 changes: 1 addition & 1 deletion frontend/e2e/tests/initialise-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default async function () {
await click(byId('signup-btn'))
await setText('[name="orgName"]', 'Bullet Train Ltd 0')
await click('#create-org-btn')
await waitForElementVisible(byId('project-select-page'))
await waitForElementVisible(byId('project-manage-widget'))

log('Create Project')
await click(byId('create-first-project-btn'))
Expand Down
7 changes: 3 additions & 4 deletions frontend/e2e/tests/segment-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const testSegment1 = async () => {
await click(byId('create-organisation-btn'))
await setText('[name="orgName"]', 'Bullet Train Ltd 2')
await click('#create-org-btn')
await waitForElementVisible(byId('project-select-page'))
await waitForElementVisible(byId('project-manage-widget'))

log('Create Project')

Expand Down Expand Up @@ -151,7 +151,7 @@ export const testSegment2 = async () => {
await click(byId('create-organisation-btn'))
await setText('[name="orgName"]', 'Bullet Train Ltd 3')
await click('#create-org-btn')
await waitForElementVisible(byId('project-select-page'))
await waitForElementVisible(byId('project-manage-widget'))

log('Create Project')

Expand All @@ -160,7 +160,6 @@ export const testSegment2 = async () => {
await click(byId('create-project-btn'))
await waitForElementVisible(byId('features-page'))


log('Create segments')
await gotoSegments()
await createSegment(0, 'segment_1', [
Expand Down Expand Up @@ -249,7 +248,7 @@ export const testSegment3 = async () => {
await click(byId('create-organisation-btn'))
await setText('[name="orgName"]', 'Bullet Train Ltd 4')
await click('#create-org-btn')
await waitForElementVisible(byId('project-select-page'))
await waitForElementVisible(byId('project-manage-widget'))

log('Create Project')

Expand Down
27 changes: 15 additions & 12 deletions frontend/web/components/AdminAPIKeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@ export class CreateAPIKey extends PureComponent {
this.setState({ isSaving: true })
data
.post(
`${Project.api}organisations/${
AccountStore.getOrganisation().id
}/master-api-keys/`,
`${Project.api}organisations/${this.props.organisationId}/master-api-keys/`,
{
expiry_date: this.state.expiry_date,
name: this.state.name,
organisation: AccountStore.getOrganisation().id,
organisation: this.props.organisationId,
},
)
.then((res) => {
Expand Down Expand Up @@ -126,7 +124,8 @@ export default class AdminAPIKeys extends PureComponent {
static displayName = 'TheComponent'

state = {
isLoading: true,
isLoading: false,
organisationId: null,
}

static propTypes = {}
Expand All @@ -135,12 +134,19 @@ export default class AdminAPIKeys extends PureComponent {
this.fetch()
}

componentDidUpdate() {
if (this.props.organisationId === this.state.organisationId) return

this.fetch()
this.setState({ organisationId: this.props.organisationId })
}

createAPIKey = () => {
openModal(
'New Admin API Key',
<CreateAPIKey
organisationId={this.props.organisationId}
onSuccess={() => {
this.setState({ isLoading: true })
this.fetch()
}}
/>,
Expand All @@ -149,11 +155,10 @@ export default class AdminAPIKeys extends PureComponent {
}

fetch = () => {
this.setState({ isLoading: true })
data
.get(
`${Project.api}organisations/${
AccountStore.getOrganisation().id
}/master-api-keys/`,
`${Project.api}organisations/${this.props.organisationId}/master-api-keys/`,
)
.then((res) => {
this.setState({
Expand All @@ -173,9 +178,7 @@ export default class AdminAPIKeys extends PureComponent {
() => {
data
.delete(
`${Project.api}organisations/${
AccountStore.getOrganisation().id
}/master-api-keys/${v.prefix}/`,
`${Project.api}organisations/${this.props.organisationId}/master-api-keys/${v.prefix}/`,
)
.then(() => {
this.fetch()
Expand Down
73 changes: 5 additions & 68 deletions frontend/web/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ import { Provider } from 'react-redux'
import { getStore } from 'common/store'
import { resolveAuthFlow } from '@datadog/ui-extensions-sdk'
import ConfigProvider from 'common/providers/ConfigProvider'
import Permission from 'common/providers/Permission'
import { getOrganisationUsage } from 'common/services/useOrganisationUsage'
import Button from './base/forms/Button'
import Icon from './Icon'
import AccountStore from 'common/stores/account-store'
import InfoMessage from './InfoMessage'
import OrganisationLimit from './OrganisationLimit'
import OrganisationLink from './OrganisationLink'

const App = class extends Component {
static propTypes = {
Expand Down Expand Up @@ -347,29 +347,14 @@ const App = class extends Component {
<React.Fragment>
<nav className='my-3 my-md-0 hidden-xs-down flex-row navbar-right space'>
<Row>
{!!AccountStore.getOrganisation() && (
<NavLink
id='projects-link'
data-test='projects-link'
activeClassName='active'
className='nav-link'
to={'/projects'}
>
<span className='mr-1'>
<Icon
name='layout'
width={20}
fill='#9DA4AE'
/>
</span>
Projects
</NavLink>
)}
<OrganisationLink />
</Row>
<Row>
<NavLink
id='account-settings-link'
data-test='account-settings-link'
activeClassName='active'
className='nav-link'
className='nav-link mr-4'
to={
projectId
? `/project/${projectId}/environment/${environmentId}/account`
Expand All @@ -385,54 +370,6 @@ const App = class extends Component {
</span>
Account
</NavLink>
{AccountStore.getOrganisationRole() ===
'ADMIN' ? (
<NavLink
id='org-settings-link'
activeClassName='active'
className='nav-link'
to='/organisation-settings'
>
<span className='mr-1'>
<Icon
name='setting'
width={20}
fill='#9DA4AE'
/>
</span>
{'Manage'}
</NavLink>
) : (
!!AccountStore.getOrganisation() && (
<Permission
level='organisation'
permission='MANAGE_USER_GROUPS'
id={AccountStore.getOrganisation().id}
>
{({ permission }) => (
<>
{!!permission && (
<NavLink
id='org-settings-link'
activeClassName='active'
className='nav-link'
to='/organisation-groups'
>
<span
style={{ marginRight: 4 }}
>
<Icon name='setting' />
</span>
{'Manage'}
</NavLink>
)}
</>
)}
</Permission>
)
)}
</Row>
<Row>
<Button
href='https://docs.flagsmith.com'
target='_blank'
Expand Down
5 changes: 1 addition & 4 deletions frontend/web/components/Aside.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,6 @@ const Aside = class extends Component {
disabled ? 'disabled' : ''
}`}
>
<a href={'/projects'} className='nav-logo'>
<Icon name='nav-logo' />
</a>
<hr className='my-0 py-0' />
<Collapsible
data-test={
project?.name
Expand Down Expand Up @@ -258,6 +254,7 @@ const Aside = class extends Component {
}}
/>
</Collapsible>
<hr className='my-0 py-0' />
<Permission
level='project'
permission='ADMIN'
Expand Down
44 changes: 44 additions & 0 deletions frontend/web/components/OrganisationLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { FC, useMemo } from 'react'
import { NavLink } from 'react-router-dom'

import AccountStore from 'common/stores/account-store'
import { useHasPermission } from 'common/providers/Permission'
import Icon from 'components/Icon'

const OrganisationLink: FC = () => {
const organisation = AccountStore.getOrganisation()

const { permission: canManageUserGroups } = useHasPermission({
id: organisation?.id,
level: 'organisation',
permission: 'MANAGE_USER_GROUPS',
})

const pageLink = useMemo(() => {
if (!organisation) return null

if (AccountStore.isAdmin() || canManageUserGroups) {
return '/organisation-settings'
}

return '/projects'
}, [organisation, canManageUserGroups])

return (
pageLink && (
<NavLink
id='org-settings-link'
activeClassName='active'
className='nav-link'
to={pageLink}
>
<span className='mr-1'>
<Icon name='layout' width={20} fill='#9DA4AE' />
</span>
{'Organisation'}
</NavLink>
)
)
}

export default OrganisationLink
62 changes: 62 additions & 0 deletions frontend/web/components/OrganisationManageWidget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { FC, useCallback, useEffect } from 'react'

import AccountProvider from 'common/providers/AccountProvider'
import AccountStore from 'common/stores/account-store'
import AppActions from 'common/dispatcher/app-actions'
import Utils from 'common/utils/utils'
import Project from 'common/project'
import Button from 'components/base/forms/Button'
import CreateOrganisationModal from 'components/modals/CreateOrganisation'
import Icon from './Icon'
import OrganisationSelect from './OrganisationSelect'

type OrganisationManageWidgetType = {
onChange?: () => void
}

const OrganisationManageWidget: FC<OrganisationManageWidgetType> = ({
onChange,
}) => {
const handleCreateOrganisationClick = useCallback(() => {
openModal('Create Organisation', <CreateOrganisationModal />, 'side-modal')
}, [])

useEffect(() => {
AppActions.getOrganisation(AccountStore.getOrganisation().id)
}, [])

return (
<Row>
<AccountProvider onChange={() => onChange && onChange()}>
{({ organisation }: { organisation: unknown }) =>
organisation && (
<OrganisationSelect
onChange={(organisationId: string) => {
AppActions.selectOrganisation(organisationId)
AppActions.getOrganisation(organisationId)
}}
/>
)
}
</AccountProvider>
{!Utils.getFlagsmithHasFeature('disable_create_org') &&
(!Project.superUserCreateOnly ||
(Project.superUserCreateOnly && AccountStore.isSuper())) && (
<div>
<Flex className='text-center ml-3'>
<Button
data-test='create-organisation-btn'
onClick={handleCreateOrganisationClick}
size='large'
className='btn btn-with-icon btn-lg px-3'
>
<Icon name='plus' width={24} fill='#656D7B' />
</Button>
</Flex>
</div>
)}
</Row>
)
}

export default OrganisationManageWidget
Loading

0 comments on commit 61d0585

Please sign in to comment.