Skip to content
Draft
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
9 changes: 7 additions & 2 deletions frontend/web/components/SDKKeysPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import React, { FC } from 'react'
import Button from './base/forms/Button'
import Input from './base/forms/Input'
import Icon from './Icon'
import ServerSideSDKKeys from './ServerSideSDKKeys'
import ServerSideSDKKeysLegacy from './ServerSideSDKKeys'
import { SDKKeysPage as SDKKeysPageNew } from './pages/sdk-keys'
import PageTitle from './PageTitle'
import Utils from 'common/utils/utils'
import { useRouteMatch } from 'react-router-dom'
Expand All @@ -16,6 +17,10 @@ const SDKKeysPage: FC = () => {
const match = useRouteMatch<RouteParams>()
const environmentId = match?.params?.environmentId

if (Utils.getFlagsmithHasFeature('rtk_server_side_sdk_keys')) {
return <SDKKeysPageNew />
}

return (
<div
data-test='segments-page'
Expand Down Expand Up @@ -55,7 +60,7 @@ const SDKKeysPage: FC = () => {
</Row>
</div>
<hr className='py-0 my-4' />
<ServerSideSDKKeys environmentId={environmentId} />
<ServerSideSDKKeysLegacy environmentId={environmentId} />
</div>
)
}
Expand Down
64 changes: 64 additions & 0 deletions frontend/web/components/pages/sdk-keys/SDKKeysPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { FC } from 'react'
import Button from 'components/base/forms/Button'
import Input from 'components/base/forms/Input'
import Icon from 'components/Icon'
import PageTitle from 'components/PageTitle'
import Utils from 'common/utils/utils'
import { useRouteMatch } from 'react-router-dom'
import { ServerSideSDKKeys } from './components'

interface RouteParams {
environmentId: string
projectId: string
}

const SDKKeysPage: FC = () => {
const match = useRouteMatch<RouteParams>()
const environmentId = match?.params?.environmentId
const projectId = match?.params?.projectId

return (
<div
data-test='segments-page'
id='segments-page'
className='app-container container'
>
<PageTitle title='Client-side Environment Key'>
Use this key to initialise{' '}
<Button
theme='text'
href='https://docs.flagsmith.com/clients/overview#client-side-sdks'
target='__blank'
>
Client-side
</Button>{' '}
SDKs.
</PageTitle>
<div className='col-md-6'>
<Row>
<Flex>
<Input
value={environmentId}
inputClassName='input input--wide'
type='text'
title={<h3>Client-side Environment Key</h3>}
placeholder='Client-side Environment Key'
/>
</Flex>
<Button
onClick={() => {
Utils.copyToClipboard(environmentId)
}}
className='ml-2 btn-with-icon'
>
<Icon name='copy' width={20} fill='#656D7B' />
</Button>
</Row>
</div>
<hr className='py-0 my-4' />
<ServerSideSDKKeys environmentId={environmentId} projectId={projectId} />
</div>
)
}

export default SDKKeysPage
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { FC, FormEvent, useEffect, useState } from 'react'
import Button from 'components/base/forms/Button'
import ModalHR from 'components/modals/ModalHR'
import Utils from 'common/utils/utils'

type CreateServerSideKeyModalProps = {
environmentName: string
onSubmit: (name: string) => void
}

const CreateServerSideKeyModal: FC<CreateServerSideKeyModalProps> = ({
environmentName,
onSubmit,
}) => {
const [name, setName] = useState('')
const [isSaving, setIsSaving] = useState(false)

useEffect(() => {
const timer = setTimeout(() => {
document.getElementById('jsTokenName')?.focus()
}, 500)
return () => clearTimeout(timer)
}, [])

const handleSubmit = (e: FormEvent) => {
Utils.preventDefault(e)
if (name) {
setIsSaving(true)
onSubmit(name)
}
}

return (
<div>
<form onSubmit={handleSubmit}>
<div className='modal-body'>
<div className='mb-2'>
This will create a Server-side Environment Key for the environment{' '}
<strong>{environmentName}</strong>.
</div>
<InputGroup
title='Key Name'
placeholder='New Key'
className='mb-2'
id='jsTokenName'
inputProps={{
className: 'full-width modal-input',
}}
onChange={(e: InputEvent) => setName(Utils.safeParseEventValue(e))}
/>
</div>
<ModalHR />
<div className='modal-footer'>
<Button onClick={closeModal} theme='secondary' className='mr-2'>
Cancel
</Button>
<Button type='submit' disabled={!name || isSaving}>
Create
</Button>
</div>
</form>
</div>
)
}

export default CreateServerSideKeyModal
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, { FC } from 'react'
import Button from 'components/base/forms/Button'
import Icon from 'components/Icon'
import Token from 'components/Token'
import Utils from 'common/utils/utils'

type ServerSideKeyRowProps = {
id: string
keyValue: string
name: string
isDeleting: boolean
onRemove: (id: string, name: string) => void
}

const ServerSideKeyRow: FC<ServerSideKeyRowProps> = ({
id,
isDeleting,
keyValue,
name,
onRemove,
}) => {
return (
<Row className='list-item'>
<Flex className='table-column px-3 font-weight-medium'>{name}</Flex>
<div className='table-column'>
<Token style={{ width: 280 }} token={keyValue} />
</div>
<Button
onClick={() => {
Utils.copyToClipboard(keyValue)
}}
className='ml-2 btn-with-icon'
>
<Icon name='copy' width={20} fill='#656D7B' />
</Button>
<div className='table-column'>
<Button
onClick={() => onRemove(id, name)}
disabled={isDeleting}
id='remove-feature'
className='btn btn-with-icon'
>
<Icon name='trash-2' width={20} fill='#656D7B' />
</Button>
</div>
</Row>
)
}

export default ServerSideKeyRow
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import React, { FC } from 'react'
import Button from 'components/base/forms/Button'
import Tooltip from 'components/Tooltip'
import Constants from 'common/constants'
import { useHasPermission } from 'common/providers/Permission'
import {
useCreateServersideEnvironmentKeysMutation,
useDeleteServersideEnvironmentKeysMutation,
useGetServersideEnvironmentKeysQuery,
} from 'common/services/useServersideEnvironmentKey'
import { useGetEnvironmentsQuery } from 'common/services/useEnvironment'
import CreateServerSideKeyModal from './CreateServerSideKeyModal'
import ServerSideKeyRow from './ServerSideKeyRow'

type ServerSideSDKKeysProps = {
environmentId: string
projectId: string
}

const ServerSideSDKKeys: FC<ServerSideSDKKeysProps> = ({
environmentId,
projectId,
}) => {
const { permission: isAdmin } = useHasPermission({
id: environmentId,
level: 'environment',
permission: 'ADMIN',
})

const { data: keys, isLoading } = useGetServersideEnvironmentKeysQuery(
{ environmentId },
{ skip: !environmentId },
)

const { data: environments } = useGetEnvironmentsQuery(
{ projectId: parseInt(projectId, 10) },
{ skip: !projectId },
)

const [createKey] = useCreateServersideEnvironmentKeysMutation()
const [deleteKey, { isLoading: isDeleting }] =
useDeleteServersideEnvironmentKeysMutation()

const environmentName =
environments?.results?.find((env) => env.api_key === environmentId)?.name ??
''

const handleCreate = () => {
openModal(
'Create Server-side Environment Keys',
<CreateServerSideKeyModal
environmentName={environmentName}
onSubmit={(name) => {
createKey({
data: { name },
environmentId,
}).then(() => {
closeModal()
})
}}
/>,
'p-0',
)
}

const handleRemove = (id: string, name: string) => {
openConfirm({
body: (
<div>
Are you sure you want to remove the SDK key <strong>{name}</strong>?
This action cannot be undone.
</div>
),
destructive: true,
onYes: () => {
deleteKey({ environmentId, id })
},
title: 'Delete Server-side Environment Keys',
yesText: 'Confirm',
})
}

return (
<FormGroup className='my-4'>
<div className='col-md-6'>
<h5 className='mb-2'>Server-side Environment Keys</h5>
<p className='fs-small lh-sm mb-0'>
Flags can be evaluated locally within your own Server environments
using our{' '}
<Button
theme='text'
href='https://docs.flagsmith.com/clients/overview#server-side-sdks'
target='__blank'
>
Server-side Environment Keys
</Button>
.
</p>
<p className='fs-small lh-sm mb-0'>
Server-side SDKs should be initialised with a Server-side Environment
Key.
</p>
{isAdmin ? (
<Button onClick={handleCreate} className='my-4'>
Create Server-side Environment Key
</Button>
) : (
<Tooltip
title={
<Button className='my-4' disabled>
Create Server-side Environment Key
</Button>
}
place='right'
>
{Constants.environmentPermissions('ADMIN')}
</Tooltip>
)}
</div>
{isLoading && (
<div className='text-center'>
<Loader />
</div>
)}
{keys && !!keys.length && (
<PanelSearch
id='org-members-list'
title='Server-side Environment Keys'
className='no-pad'
items={keys}
filterRow={(item: { name: string }, search: string) => {
const strToSearch = `${item.name}`
return (
strToSearch.toLowerCase().indexOf(search.toLowerCase()) !== -1
)
}}
renderRow={({
id,
key,
name,
}: {
id: string
key: string
name: string
}) => (
<ServerSideKeyRow
id={id}
keyValue={key}
name={name}
isDeleting={isDeleting}
onRemove={handleRemove}
/>
)}
/>
)}
</FormGroup>
)
}

export default ServerSideSDKKeys
3 changes: 3 additions & 0 deletions frontend/web/components/pages/sdk-keys/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { default as CreateServerSideKeyModal } from './CreateServerSideKeyModal'
export { default as ServerSideKeyRow } from './ServerSideKeyRow'
export { default as ServerSideSDKKeys } from './ServerSideSDKKeys'
1 change: 1 addition & 0 deletions frontend/web/components/pages/sdk-keys/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as SDKKeysPage } from './SDKKeysPage'
Loading