Skip to content

Commit

Permalink
add cloud instance setup modal (#1404)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsladerman authored Sep 20, 2024
1 parent 205f75f commit 82d2f9e
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 1 deletion.
150 changes: 150 additions & 0 deletions assets/src/components/layout/CloudConsoleWelcomeModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { ApolloError } from '@apollo/client'
import {
Button,
CheckIcon,
Checkbox,
Codeline,
CopyIcon,
Flex,
Input,
Modal,
WrapWithIf,
} from '@pluralsh/design-system'

import { GqlError } from 'components/utils/Alert'
import { InlineLink } from 'components/utils/typography/InlineLink'
import { useCreateAccessTokenMutation } from 'generated/graphql'
import CopyToClipboard from 'react-copy-to-clipboard'

import { useState } from 'react'
import { useTheme } from 'styled-components'

export function CloudConsoleWelcomeModal() {
const theme = useTheme()
const [open, setOpen] = useState(true)
const [canClose, setCanClose] = useState(false)
const [error, setError] = useState<ApolloError | undefined>(undefined)

return (
<Modal
open={open}
actions={
<Button
onClick={() => setOpen(false)}
disabled={!canClose}
>
I'm done
</Button>
}
>
<Flex
direction="column"
gap="large"
>
{error && <GqlError error={error} />}
<span>
To finish configuring Plural Console for your new cloud instance, you
must generate an access token and enter it into a command in your
local terminal.
</span>

<GenerateAccessToken setError={setError} />
<span>
Next, run this command in the Plural CLI from your local terminal. If
you have not yet installed the Plural CLI, learn how{' '}
<InlineLink
href="https://docs.plural.sh/getting-started/quickstart#install-plural-cli"
target="_blank"
rel="noreferrer"
>
here
</InlineLink>
.
</span>
<Codeline css={{ background: theme.colors['fill-two'] }}>
plural up --cloud
</Codeline>
<span>
The command will prompt you to enter the newly generated access token.
After you enter it, you should be all set.
</span>
<Checkbox
small
checked={canClose}
onChange={(e) => setCanClose(e.target.checked)}
css={{ '& .label': { userSelect: 'none' } }}
>
I successfully authenticated my cloud instance locally.
<span css={{ color: theme.colors['text-danger'] }}>*</span>
</Checkbox>
</Flex>
</Modal>
)
}

function GenerateAccessToken({
setError,
}: {
setError: (error?: ApolloError) => void
}) {
const [token, setToken] = useState('')
const [copied, setCopied] = useState(false)
const [mutation, { loading }] = useCreateAccessTokenMutation({
onError: (e) => setError(e),
onCompleted: (data) => {
const token = data.createAccessToken?.token ?? ''

setToken(token)
setError(undefined)
navigator.clipboard
.writeText(token)
.then(() => showCopied())
.catch((e) => console.error("Couldn't copy URL to clipboard", e))
},
})
const showCopied = () => {
setCopied(true)
setTimeout(() => setCopied(false), 3000)
}

return (
<Flex
direction="column"
gap="medium"
>
<span>Start by generating and copying the access token.</span>
<Flex gap="medium">
<Input
css={{ flex: 1, caretColor: 'transparent' }}
placeholder="Access token"
value={token}
/>
{token ? (
<WrapWithIf
condition={!copied}
wrapper={
<CopyToClipboard
text={token}
onCopy={showCopied}
/>
}
>
<Button
secondary
startIcon={copied ? <CheckIcon /> : <CopyIcon />}
>
{copied ? 'Copied!' : 'Copy token'}
</Button>
</WrapWithIf>
) : (
<Button
loading={loading}
onClick={() => mutation()}
>
Generate & copy
</Button>
)}
</Flex>
</Flex>
)
}
7 changes: 7 additions & 0 deletions assets/src/components/layout/Console.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import LoadingIndicator from 'components/utils/LoadingIndicator'

import { DeploymentSettingsProvider } from 'components/contexts/DeploymentSettingsContext'

import { useLogin } from 'components/contexts'

import { PluralProvider } from '../contexts/PluralContext'
import { InstallationsProvider } from '../Installations'
import { EnsureLogin } from '../login/Login'
Expand All @@ -26,6 +28,7 @@ import { ContentOverlay } from './Overlay'
import Sidebar from './Sidebar'
import Subheader from './Subheader'
import WithApplicationUpdate from './WithApplicationUpdate'
import { CloudConsoleWelcomeModal } from './CloudConsoleWelcomeModal'

export default function Console() {
return (
Expand Down Expand Up @@ -59,6 +62,7 @@ export default function Console() {

function ConsoleContent() {
const isProduction = import.meta.env.MODE === 'production'
const { configuration } = useLogin()

return (
<Flex
Expand Down Expand Up @@ -90,6 +94,9 @@ function ConsoleContent() {
)}
</WithApplicationUpdate>
)}
{configuration?.cloud && !configuration?.installed && (
<CloudConsoleWelcomeModal />
)}
<Header />
<Flex
width="100%"
Expand Down
4 changes: 3 additions & 1 deletion assets/src/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10424,7 +10424,7 @@ export type ManifestFragment = { __typename?: 'PluralManifest', cluster?: string
export type MeQueryVariables = Exact<{ [key: string]: never; }>;


export type MeQuery = { __typename?: 'RootQueryType', externalToken?: string | null, me?: { __typename?: 'User', unreadNotifications?: number | null, id: string, pluralId?: string | null, name: string, email: string, profile?: string | null, backgroundColor?: string | null, readTimestamp?: string | null, boundRoles?: Array<{ __typename?: 'Role', id: string, name: string, description?: string | null, repositories?: Array<string | null> | null, permissions?: Array<Permission | null> | null, roleBindings?: Array<{ __typename?: 'RoleBinding', id: string, user?: { __typename?: 'User', id: string, pluralId?: string | null, name: string, email: string, profile?: string | null, backgroundColor?: string | null, readTimestamp?: string | null, roles?: { __typename?: 'UserRoles', admin?: boolean | null } | null, personas?: Array<{ __typename?: 'Persona', id: string, name: string, description?: string | null, bindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'PersonaConfiguration', all?: boolean | null, deployments?: { __typename?: 'PersonaDeployment', addOns?: boolean | null, clusters?: boolean | null, pipelines?: boolean | null, providers?: boolean | null, repositories?: boolean | null, services?: boolean | null } | null, home?: { __typename?: 'PersonaHome', manager?: boolean | null, security?: boolean | null } | null, sidebar?: { __typename?: 'PersonaSidebar', audits?: boolean | null, kubernetes?: boolean | null, pullRequests?: boolean | null, settings?: boolean | null, backups?: boolean | null, stacks?: boolean | null } | null } | null } | null> | null } | null, group?: { __typename?: 'Group', id: string, name: string, description?: string | null, insertedAt?: string | null, updatedAt?: string | null } | null } | null> | null } | null> | null, roles?: { __typename?: 'UserRoles', admin?: boolean | null } | null, personas?: Array<{ __typename?: 'Persona', id: string, name: string, description?: string | null, bindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'PersonaConfiguration', all?: boolean | null, deployments?: { __typename?: 'PersonaDeployment', addOns?: boolean | null, clusters?: boolean | null, pipelines?: boolean | null, providers?: boolean | null, repositories?: boolean | null, services?: boolean | null } | null, home?: { __typename?: 'PersonaHome', manager?: boolean | null, security?: boolean | null } | null, sidebar?: { __typename?: 'PersonaSidebar', audits?: boolean | null, kubernetes?: boolean | null, pullRequests?: boolean | null, settings?: boolean | null, backups?: boolean | null, stacks?: boolean | null } | null } | null } | null> | null } | null, clusterInfo?: { __typename?: 'ClusterInfo', version?: string | null, platform?: string | null, gitCommit?: string | null } | null, configuration?: { __typename?: 'ConsoleConfiguration', vpnEnabled?: boolean | null, gitCommit?: string | null, isDemoProject?: boolean | null, isSandbox?: boolean | null, pluralLogin?: boolean | null, byok?: boolean | null, externalOidc?: boolean | null, manifest?: { __typename?: 'PluralManifest', cluster?: string | null, bucketPrefix?: string | null, network?: { __typename?: 'ManifestNetwork', pluralDns?: boolean | null, subdomain?: string | null } | null } | null, gitStatus?: { __typename?: 'GitStatus', cloned?: boolean | null, output?: string | null } | null, features?: { __typename?: 'AvailableFeatures', audits?: boolean | null, cd?: boolean | null, databaseManagement?: boolean | null, userManagement?: boolean | null, vpn?: boolean | null } | null } | null };
export type MeQuery = { __typename?: 'RootQueryType', externalToken?: string | null, me?: { __typename?: 'User', unreadNotifications?: number | null, id: string, pluralId?: string | null, name: string, email: string, profile?: string | null, backgroundColor?: string | null, readTimestamp?: string | null, boundRoles?: Array<{ __typename?: 'Role', id: string, name: string, description?: string | null, repositories?: Array<string | null> | null, permissions?: Array<Permission | null> | null, roleBindings?: Array<{ __typename?: 'RoleBinding', id: string, user?: { __typename?: 'User', id: string, pluralId?: string | null, name: string, email: string, profile?: string | null, backgroundColor?: string | null, readTimestamp?: string | null, roles?: { __typename?: 'UserRoles', admin?: boolean | null } | null, personas?: Array<{ __typename?: 'Persona', id: string, name: string, description?: string | null, bindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'PersonaConfiguration', all?: boolean | null, deployments?: { __typename?: 'PersonaDeployment', addOns?: boolean | null, clusters?: boolean | null, pipelines?: boolean | null, providers?: boolean | null, repositories?: boolean | null, services?: boolean | null } | null, home?: { __typename?: 'PersonaHome', manager?: boolean | null, security?: boolean | null } | null, sidebar?: { __typename?: 'PersonaSidebar', audits?: boolean | null, kubernetes?: boolean | null, pullRequests?: boolean | null, settings?: boolean | null, backups?: boolean | null, stacks?: boolean | null } | null } | null } | null> | null } | null, group?: { __typename?: 'Group', id: string, name: string, description?: string | null, insertedAt?: string | null, updatedAt?: string | null } | null } | null> | null } | null> | null, roles?: { __typename?: 'UserRoles', admin?: boolean | null } | null, personas?: Array<{ __typename?: 'Persona', id: string, name: string, description?: string | null, bindings?: Array<{ __typename?: 'PolicyBinding', id?: string | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null, group?: { __typename?: 'Group', id: string, name: string } | null } | null> | null, configuration?: { __typename?: 'PersonaConfiguration', all?: boolean | null, deployments?: { __typename?: 'PersonaDeployment', addOns?: boolean | null, clusters?: boolean | null, pipelines?: boolean | null, providers?: boolean | null, repositories?: boolean | null, services?: boolean | null } | null, home?: { __typename?: 'PersonaHome', manager?: boolean | null, security?: boolean | null } | null, sidebar?: { __typename?: 'PersonaSidebar', audits?: boolean | null, kubernetes?: boolean | null, pullRequests?: boolean | null, settings?: boolean | null, backups?: boolean | null, stacks?: boolean | null } | null } | null } | null> | null } | null, clusterInfo?: { __typename?: 'ClusterInfo', version?: string | null, platform?: string | null, gitCommit?: string | null } | null, configuration?: { __typename?: 'ConsoleConfiguration', vpnEnabled?: boolean | null, gitCommit?: string | null, isDemoProject?: boolean | null, isSandbox?: boolean | null, pluralLogin?: boolean | null, byok?: boolean | null, externalOidc?: boolean | null, cloud?: boolean | null, installed?: boolean | null, manifest?: { __typename?: 'PluralManifest', cluster?: string | null, bucketPrefix?: string | null, network?: { __typename?: 'ManifestNetwork', pluralDns?: boolean | null, subdomain?: string | null } | null } | null, gitStatus?: { __typename?: 'GitStatus', cloned?: boolean | null, output?: string | null } | null, features?: { __typename?: 'AvailableFeatures', audits?: boolean | null, cd?: boolean | null, databaseManagement?: boolean | null, userManagement?: boolean | null, vpn?: boolean | null } | null } | null };

export type UsersQueryVariables = Exact<{
q?: InputMaybe<Scalars['String']['input']>;
Expand Down Expand Up @@ -21750,6 +21750,8 @@ export const MeDocument = gql`
pluralLogin
byok
externalOidc
cloud
installed
manifest {
...Manifest
}
Expand Down
2 changes: 2 additions & 0 deletions assets/src/graph/users.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ query Me {
pluralLogin
byok
externalOidc
cloud
installed
manifest {
...Manifest
}
Expand Down

0 comments on commit 82d2f9e

Please sign in to comment.