Skip to content

Commit

Permalink
adds info for fields, visual changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Amogh-Bharadwaj committed Oct 4, 2023
1 parent a713fed commit 27ef6f0
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 31 deletions.
1 change: 0 additions & 1 deletion ui/app/peers/create/configuration/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const validateFields = (
return false;
}
let validationErr: string | undefined;
console.log(config);
switch (type) {
case 'POSTGRES':
const pgConfig = pgSchema.safeParse(config);
Expand Down
4 changes: 4 additions & 0 deletions ui/app/peers/create/configuration/helpers/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export interface Setting {
label: string;
stateHandler: (value: string, setter: PeerSetter) => void;
type?: string;
optional?: boolean;
tips?: string;
helpfulLink?: string;
default?: string | number;
}

export const getBlankSetting = (dbType: string): PeerConfig => {
Expand Down
12 changes: 12 additions & 0 deletions ui/app/peers/create/configuration/helpers/pg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,45 @@ export const postgresSetting: Setting[] = [
label: 'Host',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, host: value })),
tips: 'Specifies the IP host name or address on which postgres is to listen for TCP/IP connections from client applications. Ensure that this host has us whitelisted so we can connect to it.',
},
{
label: 'Port',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, port: parseInt(value, 10) })),
type: 'number', // type for textfield
default: 5432,
tips: 'Specifies the TCP/IP port or local Unix domain socket file extension on which postgres is listening for connections from client applications.',
},
{
label: 'User',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, user: value })),
tips: 'Specify the user that we should use to connect to this host.',
helpfulLink: 'https://www.postgresql.org/docs/8.0/user-manag.html',
},
{
label: 'Password',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, password: value })),
type: 'password',
tips: 'Password associated with the user you provided.',
helpfulLink: 'https://www.postgresql.org/docs/current/auth-password.html',
},
{
label: 'Database',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, database: value })),
tips: 'Specify which database to associate with this peer.',
helpfulLink:
'https://www.postgresql.org/docs/current/sql-createdatabase.html',
},
{
label: 'Transaction Snapshot',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, transactionSnapshot: value })),
optional: true,
tips: 'This is optional and only needed if this peer is part of any query replication mirror.',
},
];

Expand Down
30 changes: 28 additions & 2 deletions ui/app/peers/create/configuration/helpers/sf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,64 @@ export const snowflakeSetting: Setting[] = [
label: 'Account ID',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, accountId: value })),
tips: 'This is the unique identifier for your Snowflake account. It has a URL-like format',
helpfulLink:
'https://docs.snowflake.com/en/user-guide/admin-account-identifier',
},
{
label: 'Username',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, username: value })),
tips: 'This is the username you use to login to your Snowflake account.',
helpfulLink:
'https://docs.snowflake.com/en/user-guide/admin-user-management',
},
{
label: 'Private Key',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, privateKey: value })),
type: 'file',
tips: 'This can be of any file extension. If you are using an encrypted key, you must fill the below password field for decryption.',
helpfulLink: 'https://docs.snowflake.com/en/user-guide/key-pair-auth',
},
{
label: 'Warehouse',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, warehouse: value })),
tips: 'Warehouses denote a cluster of snowflake resources.',
helpfulLink: 'https://docs.snowflake.com/en/user-guide/warehouses',
},
{
label: 'Database',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, database: value })),
tips: 'Specify which database to associate with this peer.',
helpfulLink: 'https://docs.snowflake.com/en/sql-reference/snowflake-db',
},
{
label: 'Role',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, role: value })),
tips: 'You could use a default role, or setup a role with the required permissions.',
helpfulLink:
'https://docs.snowflake.com/en/user-guide/security-access-control-overview#roles',
},
{
label: 'Query Timeout',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, queryTimeout: parseInt(value, 10) || 0 })),
setter((curr) => ({ ...curr, queryTimeout: parseInt(value, 10) || 30 })),
optional: true,
tips: 'This is the maximum time in seconds that a query can run before being cancelled. If not specified, the default is 30 seconds',
default: 30,
},
{
label: 'S3 Integration',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, s3Integration: value })),
optional: true,
tips: `This is needed only if you plan to run a mirror and you wish to stage AVRO files on S3.`,
helpfulLink:
'https://docs.snowflake.com/en/user-guide/data-load-s3-config-storage-integration',
},
{
label: 'Password',
Expand All @@ -54,6 +76,10 @@ export const snowflakeSetting: Setting[] = [
});
} else setter((curr) => ({ ...curr, password: value }));
},
type: 'password',
optional: true,
tips: 'This is needed only if the private key you provided is encrypted.',
helpfulLink: 'https://docs.snowflake.com/en/user-guide/key-pair-auth',
},
];

Expand All @@ -64,6 +90,6 @@ export const blankSnowflakeSetting: SnowflakeConfig = {
warehouse: '',
database: '',
role: '',
queryTimeout: 0,
queryTimeout: 30,
s3Integration: '',
};
29 changes: 23 additions & 6 deletions ui/app/peers/create/configuration/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Label } from '@/lib/Label';
import { LayoutMain, RowWithTextField } from '@/lib/Layout';
import { Panel } from '@/lib/Panel';
import { TextField } from '@/lib/TextField';
import { Tooltip } from '@/lib/Tooltip';
import Link from 'next/link';
import { useRouter, useSearchParams } from 'next/navigation';
import { useState } from 'react';
Expand Down Expand Up @@ -43,22 +44,38 @@ export default function CreateConfig() {
return (
<LayoutMain alignSelf='center' justifySelf='center' width='xxLarge'>
<Panel>
<Label variant='title3'>New peer</Label>
<Label colorName='lowContrast'>Set up a new peer.</Label>
<Label variant='title3'>
Setup a new{' '}
{dbType.charAt(0).toUpperCase() + dbType.slice(1).toLowerCase()} peer
</Label>
</Panel>
<Panel>
<Label colorName='lowContrast' variant='subheadline'>
Configuration
</Label>
<RowWithTextField
label={<Label as='label'>Name</Label>}
label={
<Label>
Name
{
<Tooltip
style={{ width: '100%' }}
content={'Peer name is a required field.'}
>
<Label colorName='lowContrast' colorSet='destructive'>
*
</Label>
</Tooltip>
}
</Label>
}
action={
<TextField
variant='simple'
onChange={(e) => setName(e.target.value)}
/>
}
/>
<Label colorName='lowContrast' variant='subheadline'>
Configuration
</Label>
{dbType && configComponentMap(dbType)}
</Panel>
<Panel>
Expand Down
4 changes: 2 additions & 2 deletions ui/app/peers/create/configuration/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ export const sfSchema = z.object({
username: z
.string({
required_error: 'Username is required',
invalid_type_error: 'Username must be a number',
invalid_type_error: 'Username must be a string',
})
.nonempty({ message: 'Username must be non-empty' })
.max(255, 'Port must be below 65535'),
.max(255, 'Username must be less than 255 characters'),
database: z
.string({
required_error: 'Database is required',
Expand Down
69 changes: 49 additions & 20 deletions ui/components/ConfigForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { Setting } from '@/app/peers/create/configuration/helpers/common';
import { Label } from '@/lib/Label';
import { RowWithTextField } from '@/lib/Layout';
import { TextField } from '@/lib/TextField';
import { Tooltip } from '@/lib/Tooltip';
import { PeerSetter } from '../app/peers/create/configuration/types';
import { InfoPopover } from './InfoPopover';

interface ConfigProps {
settings: Setting[];
Expand All @@ -15,15 +17,16 @@ export default function ConfigForm(props: ConfigProps) {
file: File,
setFile: (value: string, setter: PeerSetter) => void
) => {
const reader = new FileReader();
reader.readAsText(file);
reader.onload = () => {
console.log(reader.result);
setFile(reader.result as string, props.setter);
};
reader.onerror = (error) => {
console.log(error);
};
if (file) {
const reader = new FileReader();
reader.readAsText(file);
reader.onload = () => {
setFile(reader.result as string, props.setter);
};
reader.onerror = (error) => {
console.log(error);
};
}
};

const handleChange = (
Expand All @@ -42,18 +45,44 @@ export default function ConfigForm(props: ConfigProps) {
return (
<RowWithTextField
key={id}
label={<Label>{setting.label}</Label>}
label={
<Label>
{setting.label}{' '}
{!setting.optional && (
<Tooltip
style={{ width: '100%' }}
content={'This is a required field.'}
>
<Label colorName='lowContrast' colorSet='destructive'>
*
</Label>
</Tooltip>
)}
</Label>
}
action={
<TextField
variant='simple'
style={
setting.type === 'file'
? { border: 'none', height: 'auto' }
: { border: 'auto' }
}
type={setting.type}
onChange={(e) => handleChange(e, setting)}
/>
<div
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<TextField
variant='simple'
style={
setting.type === 'file'
? { border: 'none', height: 'auto' }
: { border: 'auto' }
}
type={setting.type}
defaultValue={setting.default}
onChange={(e) => handleChange(e, setting)}
/>
{setting.tips && (
<InfoPopover tips={setting.tips} link={setting.helpfulLink} />
)}
</div>
}
/>
);
Expand Down
52 changes: 52 additions & 0 deletions ui/components/InfoPopover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Icon } from '@/lib/Icon';
import * as Popover from '@radix-ui/react-popover';
export const InfoPopover = ({
tips,
link,
}: {
tips: string;
link?: string;
}) => {
return (
<Popover.Root modal={true}>
<Popover.Trigger asChild>
<button className='IconButton' aria-label='Update dimensions'>
<Icon name='info' />
</button>
</Popover.Trigger>

<Popover.Portal>
<Popover.Content
style={{ position: 'absolute' }}
className='PopoverContent'
side='right'
sideOffset={5}
>
<div
style={{
border: '1px solid #d9d7d7',
boxShadow: '0 0 10px #d9d7d7',
padding: '0.5rem',
borderRadius: '0.5rem',
minWidth: '15rem',
}}
>
<p className='Text' style={{ fontSize: 13 }}>
{tips}
</p>

{link && (
<a

Check warning

Code scanning / CodeQL

Potentially unsafe external link Medium

External links without noopener/noreferrer are a potential security risk.
href={link}
target='_blank'
style={{ color: '#0070f3', fontSize: 13 }}
>
Click here for more info.
</a>
)}
</div>
</Popover.Content>
</Popover.Portal>
</Popover.Root>
);
};
1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-form": "^0.0.3",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^1.2.2",
Expand Down
Loading

0 comments on commit 27ef6f0

Please sign in to comment.