Skip to content

Commit

Permalink
feat(@cubejs-playground): localhost connection tipbox (#2587)
Browse files Browse the repository at this point in the history
  • Loading branch information
vasilev-alex committed Apr 21, 2021
1 parent 4112b2d commit bab3265
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 88 deletions.
2 changes: 1 addition & 1 deletion packages/cubejs-playground/src/atoms/Button.tsx
Expand Up @@ -2,7 +2,7 @@ import styled from 'styled-components';
import { Button } from 'antd';

const StyledButton: typeof Button = styled(Button)`
&& {
&&:not(.ant-btn-link) {
padding: 5px 12px;
height: auto;
border-color: var(--dark-05-color);
Expand Down
8 changes: 8 additions & 0 deletions packages/cubejs-playground/src/components/GlobalStyles.tsx
Expand Up @@ -244,6 +244,14 @@ const GlobalStyles = createGlobalStyle`
.missing-member-tooltip {
max-width: none;
}
.ant-btn-link > span {
text-decoration: underline;
}
a.ant-typography {
text-decoration: underline;
}
`;

export default GlobalStyles;
@@ -1,12 +1,14 @@
import { Col, PageHeader, Row, Typography } from 'antd';
import { Col, Row, Space, Typography } from 'antd';
import { useState } from 'react';
import styled from 'styled-components';

import envVarsDatabaseMap from '../../shared/env-vars-db-map';
import { fetchWithTimeout } from '../../utils';
import ConnectionTest from './components/ConnectionTest';
import DatabaseCard from './components/DatabaseCard';
import { DatabaseCard, SelectedDatabaseCard } from './components/DatabaseCard';
import DatabaseForm from './components/DatabaseForm';
import { Button } from '../../atoms';
import { LocalhostTipBox } from './components/LocalhostTipBox';

const { Title, Paragraph } = Typography;

Expand All @@ -22,20 +24,24 @@ const databases = envVarsDatabaseMap.reduce<any>(
[]
);

async function testConnection(variables: Record<string, any>) {
const response = await fetchWithTimeout('/playground/test-connection', {
method: 'post',
headers: {
'Content-Type': 'application/json',
async function testConnection(variables: Record<string, string>) {
const response = await fetchWithTimeout(
'/playground/test-connection',
{
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
variables,
}),
},
body: JSON.stringify({
variables,
}),
}, 5 * 1000);
5 * 1000
);

const { error } = await response.json();
if (error) {
throw new Error(error)
throw new Error(error);
}
}

Expand All @@ -47,7 +53,7 @@ const Layout = styled.div`
background-color: #fff;
`;

async function saveConnection(variables: Record<string, any>) {
async function saveConnection(variables: Record<string, string>) {
await fetch('/playground/env', {
method: 'post',
headers: {
Expand All @@ -61,84 +67,106 @@ async function saveConnection(variables: Record<string, any>) {
await fetch('/restart');
}

export type Database = {
title: string;
logo: string;
instructions?: string;
};

export default function ConnectionWizardPage({ history }) {
const [isLoading, setLoading] = useState(false);
const [isTestConnectionLoading, setTestConnectionLoading] = useState(false);
const [testConnectionResult, setTestConnectionResult] = useState<any>(null);
const [db, selectDatabase] = useState<any>(null);
const [db, selectDatabase] = useState<Database | null>(null);

return (
<Layout>
<Title>Set Up a Database connection</Title>

{db ? (
<>
<Row gutter={[12, 12]}>
<Col span={24}>
<PageHeader
title={<DatabaseCard db={db} />}
onBack={() => selectDatabase(null)}
/>
</Col>

<Col span={24}>
<Typography>
{db.instructions ? (
<p>
<span
dangerouslySetInnerHTML={{ __html: db.instructions }}
/>
</p>
) : (
<p>Enter database credentials to connect to your database</p>
)}
</Typography>
</Col>

<Col span={12}>
<DatabaseForm
db={db}
deployment={{}}
loading={isLoading}
disabled={isTestConnectionLoading}
onCancel={() => undefined}
onSubmit={async (variables) => {
try {
setTestConnectionResult(null);
setTestConnectionLoading(true);

await testConnection(variables);

setTestConnectionResult({
success: true,
});

setLoading(true);
await saveConnection(variables);
setLoading(false);

history.push('/schema');
} catch (error) {
setTestConnectionResult({
success: false,
error,
});
}

setTestConnectionLoading(false);
}}
/>
</Col>
</Row>
<Space direction="vertical" size="large">
<Space size="middle">
<SelectedDatabaseCard db={db} />

<Button type="link" onClick={() => selectDatabase(null)}>
Change
</Button>
</Space>

<Row gutter={[40, 12]}>
<Col span={24}>
<Typography>
{db.instructions ? (
<p>
<span
dangerouslySetInnerHTML={{ __html: db.instructions }}
/>
</p>
) : (
<p>
Enter database credentials to connect to your database.{' '}
<br />
Cube.js will store your credentials into the .env file for
future use.
</p>
)}
</Typography>
</Col>

<Row>
<Col span={12}>
<ConnectionTest
loading={isTestConnectionLoading}
result={testConnectionResult}
/>
</Col>
</Row>
<Col span={12}>
<DatabaseForm
db={db}
deployment={{}}
loading={isLoading}
disabled={isTestConnectionLoading}
onCancel={() => undefined}
onSubmit={async (variables) => {
try {
setTestConnectionResult(null);
setTestConnectionLoading(true);

await testConnection(variables);

setTestConnectionResult({
success: true,
});

setLoading(true);
await saveConnection(variables);
setLoading(false);

history.push('/schema');
} catch (error) {
setTestConnectionResult({
success: false,
error,
});
}

setTestConnectionLoading(false);
}}
/>
</Col>

{['MySQL', 'PostgreSQL', 'Druid', 'Clickhouse'].includes(
db?.title || ''
) && (
<Col span={12}>
<LocalhostTipBox />
</Col>
)}
</Row>

<Row>
<Col span={12}>
<ConnectionTest
loading={isTestConnectionLoading}
result={testConnectionResult}
/>
</Col>
</Row>
</Space>
</>
) : (
<>
Expand Down
@@ -1,12 +1,9 @@
import {
Row,
Col,
Typography,
Card,
Image,
} from 'antd';
import { Row, Col, Typography, Card, Image } from 'antd';
import styled from 'styled-components';

export default function DatabaseCard({ db }) {
import { Database } from '../ConnectionWizardPage';

export function DatabaseCard({ db }) {
return (
<Card>
<Row align="middle" justify="space-between">
Expand All @@ -21,3 +18,29 @@ export default function DatabaseCard({ db }) {
</Card>
);
}

const Wrapper = styled.div`
background-color: #f8f8f9;
padding: 15px 20px;
border-radius: 4px;
`;

type TSelectedDatabaseCardProps = {
db: Database;
};

export function SelectedDatabaseCard({ db }: TSelectedDatabaseCardProps) {
return (
<Wrapper>
<Row align="middle" justify="space-between">
<Col flex="40px">
<Image src={db.logo} preview={false} />
</Col>

<Col>
<Typography.Text strong>{db.title}</Typography.Text>
</Col>
</Row>
</Wrapper>
);
}
@@ -0,0 +1,90 @@
import { Alert, Button, Form, Input, Space, Typography } from 'antd';
import { CopyOutlined } from '@ant-design/icons';
import styled from 'styled-components';
import { InputProps } from 'antd/lib/input/Input';

import { copyToClipboard } from '../../../utils';

const IconButton: typeof Button = styled(Button)`
border: none;
color: var(--primary-1);
`;

const StyledAlert: typeof Alert = styled(Alert)`
border: none;
padding: 16px;
`;

const StyledForm: typeof Form = styled(Form)`
.ant-form-item {
margin-bottom: 20px;
}
.ant-form-item:last-child {
margin-bottom: 0;
}
.ant-form-item-label {
padding-bottom: 4px;
}
`;

type TCopiableInputProps = {
label?: string;
} & InputProps;

function CopiableInput({ label, value, ...props }: TCopiableInputProps) {
const suffix = (
<IconButton
icon={<CopyOutlined />}
onClick={() => copyToClipboard(value)}
/>
);

return (
<Form.Item
label={label ? <b>{label}</b> : null}
labelCol={{ span: 24 }}
wrapperCol={{ span: 24 }}
>
<Input value={value} suffix={suffix} {...props} />
</Form.Item>
);
}

export function LocalhostTipBox() {
return (
<StyledAlert
type="warning"
message={
<Space direction="vertical" size="middle">
<Typography.Text>
To connect to the database running on the localhost use the
following <b>hostname</b> value
</Typography.Text>

<StyledForm>
<CopiableInput label="Mac" value="host.docker.internal" />

<CopiableInput label="Windows" value="docker.for.win.localhost" />

<CopiableInput label="Linux" value="localhost" />

<Space direction="vertical" size="middle">
<Typography.Text>
Please note, for Linux, you need to run Cube.js Docker container
in the{' '}
<Typography.Link href="https://docs.docker.com/network/host/" target="_blank">
network mode "host"
</Typography.Link>{' '}
to be able to connect to the database running on localhost.
</Typography.Text>

<CopiableInput value="docker run --network host" />
</Space>
</StyledForm>
</Space>
}
/>
);
}

0 comments on commit bab3265

Please sign in to comment.