console: POC db connect select database draft design [GCU-107]

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7897
GitOrigin-RevId: 4da175532c5422bd8985b9134f5aba7b190d05e0
This commit is contained in:
Matthew Goodwin 2023-02-09 13:52:07 -06:00 committed by hasura-bot
parent ebb7ade403
commit 7c804c8a82
32 changed files with 912 additions and 4 deletions

View File

@ -2,8 +2,12 @@
"eslint.validate": ["json"],
"cSpell.words": ["clsx", "hasura", "citus"],
"tailwindCSS.experimental.classRegex": [
// detects in simple variables like const twFoo = `pt-2`;
"tw\\w+ ?= ?`([^`]*)`",
"tw\\w+: ?`([^`]*)`"
// detects tw strings in clsx function calls like: clsx('pt-2', 'relative');
["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^`]*)(?:'|\"|`)"],
// detects strings in objects and nested objects. Just prefix variable name with tw like so: const twStyleObject = {};
["tw\\w+ = {\\s+([^;]+)\\s+};", "(?:'|\"|`)([^`]*)(?:'|\"|`)"]
],
"tailwindCSS.rootFontSize": 14
}

View File

@ -0,0 +1,37 @@
import { hasuraToast } from '@/new-components/Toasts';
import { useArgs } from '@storybook/client-api';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import { ConnectDatabase } from './ConnectDatabase';
export default {
component: ConnectDatabase,
argTypes: {
onEnableEnterpriseTrial: { action: 'Enable Enterprise Clicked' },
onContactSales: { action: 'Contact Sales Clicked' },
},
} as ComponentMeta<typeof ConnectDatabase>;
export const Primary: ComponentStory<typeof ConnectDatabase> = args => {
const [, updateArgs] = useArgs();
return (
<ConnectDatabase
{...args}
onEnableEnterpriseTrial={() => {
hasuraToast({
message:
'Missing EE Trial Forms here. Setting ee trial prop to active as a temporary measure.',
title: 'Sign Up Not Implemented',
toastOptions: {
duration: 3000,
},
});
updateArgs({ ...args, eeState: 'active' });
}}
/>
);
};
Primary.args = {
eeState: 'inactive',
initialDb: 'snowflake',
};

View File

@ -0,0 +1,23 @@
import {
SelectDatabase,
SelectDatabaseProps,
} from '@/features/ConnectDBRedesign/components/SelectDatabase/SelectDatabase';
export const ConnectDatabase = (props: SelectDatabaseProps) => {
return (
<div className="flex flex-col items-center">
<div className="py-lg border-b border-slate-300 w-full flex justify-center">
<div className="max-w-3xl w-full">
<div className="text-xl font-bold">Connect Your First Database</div>
<div className="text-muted">
Connect your first database to access your database objects in your
GraphQL API.
</div>
</div>
</div>
<div className="max-w-3xl py-lg w-full">
<SelectDatabase {...props} />
</div>
</div>
);
};

View File

@ -0,0 +1,40 @@
import { SelectDatabase } from '@/features/ConnectDBRedesign/components/SelectDatabase';
import { hasuraToast } from '@/new-components/Toasts';
import { useArgs } from '@storybook/client-api';
import { ComponentMeta, ComponentStory } from '@storybook/react';
export default {
component: SelectDatabase,
argTypes: {
onEnableEnterpriseTrial: { action: 'Enable Enterprise Clicked' },
onContactSales: { action: 'Contact Sales Clicked' },
},
} as ComponentMeta<typeof SelectDatabase>;
export const Primary: ComponentStory<typeof SelectDatabase> = args => {
const [, updateArgs] = useArgs();
return (
<div className="max-w-3xl">
Note: This container has a max width set. When rendering this component
keep width in mind to avoid it growing too large.
<SelectDatabase
{...args}
onEnableEnterpriseTrial={() => {
hasuraToast({
message:
'Missing EE Trial Forms here. Setting ee trial prop to active as a temporary measure.',
title: 'Sign Up Not Implemented',
toastOptions: {
duration: 3000,
},
});
updateArgs({ ...args, eeState: 'active' });
}}
/>
</div>
);
};
Primary.args = {
eeState: 'inactive',
initialDb: 'snowflake',
};

View File

@ -0,0 +1,87 @@
import { NeonBanner } from '@/features/CloudOnboarding/OnboardingWizard/components/NeonConnectBanner/NeonBanner';
import Globals from '@/Globals';
import { Button } from '@/new-components/Button';
import React from 'react';
import {
EETrialActive,
EETrialExpired,
EETrialInactive,
FancyRadioCards,
} from './components';
import { databases } from './databases';
import DbConnectSVG from './graphics/database-connect.svg';
import { DatabaseKind, EEState } from '../../types';
const enterpriseDbs: DatabaseKind[] = ['snowflake', 'athena'];
export interface SelectDatabaseProps {
eeState: EEState;
initialDb?: DatabaseKind;
onEnableEnterpriseTrial: () => void;
onContactSales: () => void;
}
export const SelectDatabase = ({
eeState,
initialDb = 'postgres',
onEnableEnterpriseTrial,
onContactSales,
}: SelectDatabaseProps) => {
const [selectedDb, setSelectedDb] = React.useState<DatabaseKind>(
initialDb || 'postgres'
);
const displayNeonBanner =
Globals.consoleType === 'cloud' && !!Globals.hasuraCloudTenantId;
const showConnectDbButton =
(enterpriseDbs.includes(selectedDb) && eeState === 'active') ||
!enterpriseDbs.includes(selectedDb);
return (
<div className="flex flex-col">
<img
src={DbConnectSVG}
className={`mb-md w-full`}
alt="Database Connection Diagram"
/>
<FancyRadioCards
items={databases}
value={selectedDb}
onChange={val => {
setSelectedDb(val);
}}
/>
{selectedDb === 'postgres' && displayNeonBanner && (
<div className="mt-3">
<NeonBanner
onClickConnect={() =>
window.alert('todo: implement Neon integration')
}
status={{ status: 'default' }}
buttonText="Create a Neon Database"
setStepperIndex={() => {}}
/>
</div>
)}
{enterpriseDbs.includes(selectedDb) && eeState === 'inactive' && (
<EETrialInactive
selectedDb={selectedDb}
onEnableEnterpriseTrial={onEnableEnterpriseTrial}
/>
)}
{enterpriseDbs.includes(selectedDb) && eeState === 'active' && (
<EETrialActive selectedDb={selectedDb} />
)}
{enterpriseDbs.includes(selectedDb) && eeState === 'expired' && (
<EETrialExpired
selectedDb={selectedDb}
onContactSales={onContactSales}
/>
)}
{showConnectDbButton && (
<Button className="mt-6 self-end">Connect Existing Database</Button>
)}
</div>
);
};

View File

@ -0,0 +1,67 @@
import { Button } from '@/new-components/Button';
import { InputField } from '@/new-components/Form';
import clsx from 'clsx';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { FaRegCopy } from 'react-icons/fa';
const twStyles = {
alignToTopOfInput: `top-[32px]`,
inputHeight: `h-[36px]`,
copyConfirm: {
base: `select-none transition-opacity duration-300 font-bold bg-slate-100/50 rounded backdrop-blur-sm absolute left-0 w-full flex items-center justify-center`,
invisible: `opacity-0 pointer-events-none`,
visible: `opacity-100 pointer-events-auto`,
},
copyButton: `active:opacity-50 border-none bg-transparent absolute right-0 shadow-none bg-none`,
};
export const CopyableInputField: typeof InputField = props => {
const { watch } = useFormContext();
const fieldValue = watch(props.name);
// state to control visibility of copy confirmation
const [showCopiedConfirmation, setShowCopiedConfirmation] =
React.useState(false);
const copyTimer = React.useRef<NodeJS.Timeout>();
const handleCopyButton = () => {
// clear timer if already going...
if (copyTimer.current) {
clearTimeout(copyTimer.current);
}
// copy text to clipboard
navigator.clipboard.writeText(fieldValue);
// show confirmation
setShowCopiedConfirmation(true);
// hide after 1.5s
copyTimer.current = setTimeout(() => {
setShowCopiedConfirmation(false);
}, 1500);
};
return (
<div className="relative">
<InputField {...props} />
<Button
className={clsx(twStyles.copyButton, twStyles.alignToTopOfInput)}
icon={<FaRegCopy />}
onClick={handleCopyButton}
/>
<div
className={clsx(
twStyles.copyConfirm.base,
twStyles.alignToTopOfInput,
twStyles.inputHeight,
twStyles.copyConfirm.invisible,
showCopiedConfirmation && twStyles.copyConfirm.visible
)}
>
Copied!
</div>
</div>
);
};

View File

@ -0,0 +1,17 @@
import React from 'react';
export const DatabaseLogo: React.FC<{ title: string; image: string }> = ({
title,
image,
}) => {
return (
<div className="flex flex-col mt-2 items-center">
<img
src={image}
className="h-[16px] w-[16px] mb-2"
alt={`${title} logo`}
/>
<div className="text-black text-base">{title}</div>
</div>
);
};

View File

@ -0,0 +1,103 @@
import {
CopyableInputField,
InformationCard,
} from '@/features/ConnectDBRedesign/components/SelectDatabase/components';
import { dbDisplayNames } from '@/features/ConnectDBRedesign/components/SelectDatabase/databases';
import { indefiniteArticle } from '@/features/ConnectDBRedesign/components/SelectDatabase/utils';
import { DatabaseKind } from '@/features/ConnectDBRedesign/types';
import { Button } from '@/new-components/Button';
import { InputField, SimpleForm } from '@/new-components/Form';
import { hasuraToast } from '@/new-components/Toasts';
import React from 'react';
import { FaExternalLinkAlt } from 'react-icons/fa';
import { GrDocker } from 'react-icons/gr';
import { z } from 'zod';
export const EETrialActive: React.VFC<{ selectedDb: DatabaseKind }> = ({
selectedDb,
}) => {
const dbWithArticle = `${indefiniteArticle(selectedDb)} ${
dbDisplayNames[selectedDb]
}`;
return (
<InformationCard blueLeftBorder>
<div className="flex flex-col">
<div className="flex items-center pb-3 mb-3 border-b border-slate-300">
<div className="flex flex-col w-3/4">
<div className="font-bold">
{dbDisplayNames[selectedDb]} Connector Required
</div>
<div className="text-md text-gray-700">
{`The Hasura GraphQL Data Connector Service is required to connect to ${dbWithArticle} database.`}
</div>
</div>
<div className="flex w-1/4 justify-end">
<Button
icon={<FaExternalLinkAlt />}
iconPosition="end"
onClick={() => {
alert('need link to docs here');
}}
>
Deployment Methods
</Button>
</div>
</div>
<div>
<div className="font-bold text-muted flex items-center">
<GrDocker />
<div className="ml-1">Docker Initialization</div>
</div>
<SimpleForm
schema={z.object({
docker_command: z.string(),
agent_path: z.string(),
})}
options={{
defaultValues: {
docker_command:
'docker run -p 127.0.0.1:1234:1234 hasura/graphql-data-connector',
agent_path: 'http://host.docker.internal:1234',
},
}}
onSubmit={values => {
console.log(values);
}}
>
<CopyableInputField
className="mt-1"
label="Run GraphQL Data Connector Service"
tooltip="This is a really great tooltip for this field"
disabled={true}
name="docker_command"
learnMoreLink="https://hasura.io/docs"
/>
<InputField
name="agent_path"
label="Connect to GraphQL Data Connector URI"
tooltip="This is a really great tooltip for this field"
/>
<div className="flex justify-end w-full">
<Button
type="submit"
mode="primary"
onClick={() => {
hasuraToast({
title: 'Not Implemented',
message:
'This feature will be implemented once the dc_add_agent check is merged.',
toastOptions: {
duration: 3000,
},
});
}}
>
Validate And Connect Database
</Button>
</div>
</SimpleForm>
</div>
</div>
</InformationCard>
);
};

View File

@ -0,0 +1,31 @@
import { Button } from '@/new-components/Button';
import React from 'react';
import { FiAlertTriangle } from 'react-icons/fi';
import { DatabaseKind } from '../../../types';
import { InformationCard } from './InformationCard';
export const EETrialExpired: React.VFC<{
onContactSales: () => void;
selectedDb: DatabaseKind;
}> = ({ onContactSales, selectedDb }) => {
return (
<InformationCard>
<div className="flex items-center">
<div className="flex flex-col w-3/4">
<div className="text-[21px] flex items-center gap-2">
<FiAlertTriangle color="rgb(220 38 38)" /> Enterprise Trial Expired
</div>
<div className="text-md text-gray-700">
With an Enterprise Edition license you can add data sources such as
Snowflake, Amazon Athena, and more to your GraphQL API.
</div>
</div>
<div className="flex w-1/4 justify-end">
<Button size="md" onClick={onContactSales}>
Contact Sales
</Button>
</div>
</div>
</InformationCard>
);
};

View File

@ -0,0 +1,35 @@
import { Button } from '@/new-components/Button';
import React from 'react';
import { DatabaseKind } from '../../../types';
import { dbDisplayNames } from '../databases';
import { indefiniteArticle } from '../utils';
import { InformationCard } from './InformationCard';
export const EETrialInactive: React.VFC<{
onEnableEnterpriseTrial: () => void;
selectedDb: DatabaseKind;
}> = ({ onEnableEnterpriseTrial, selectedDb }) => {
const dbWithArticle = `${indefiniteArticle(selectedDb)} ${
dbDisplayNames[selectedDb]
}`;
return (
<InformationCard>
<div className="flex items-center">
<div className="flex flex-col w-3/4">
<div className="text-[21px]">
{`Looking to connect to ${dbWithArticle} database?`}
</div>
<div className="text-md text-gray-700">
Deploy data connectors to add data sources such as Snowflake, Amazon
Athena, and more to your GraphQL API.
</div>
</div>
<div className="flex w-1/4 justify-end">
<Button mode={'primary'} size="md" onClick={onEnableEnterpriseTrial}>
Enable Enterprise
</Button>
</div>
</div>
</InformationCard>
);
};

View File

@ -0,0 +1,59 @@
import * as RadioGroup from '@radix-ui/react-radio-group';
import clsx from 'clsx';
import React from 'react';
import { DatabaseKind } from '../../../types';
const twRadioStyles = {
root: `grid grid-cols-4 gap-3`,
itemContainer: {
default: `flex items-center border bg-white shadow-sm rounded border-gray-300 cursor-pointer relative flex-[0_0_160px] h-[88px]`,
active: `ring-2 ring-blue-300 border-blue-400`,
disabled: ` cursor-not-allowed bg-gray-200`,
},
radioButton: `bg-white w-[20px] h-[20px] rounded-full shadow-eq shadow-blue-900 hover:bg-blue-100 flex-[2] absolute top-0 left-0 m-3`,
indicator: `flex items-center justify-center w-full h-full relative after:content[''] after:block after:w-[10px] after:h-[10px] after:rounded-[50%] after:bg-blue-600`,
label: `text-base whitespace-nowrap cursor-pointer flex-[1] h-full w-full flex justify-center items-center`,
};
export const FancyRadioCards: React.VFC<{
value: string;
items: {
value: string;
content: React.ReactNode | string;
}[];
onChange: (value: DatabaseKind) => void;
}> = ({ value, items, onChange }) => {
return (
<RadioGroup.Root
className={twRadioStyles.root}
defaultValue={value}
aria-label="Radio cards"
onValueChange={onChange}
>
{items.map((item, i) => {
return (
<div
className={clsx(
twRadioStyles.itemContainer.default,
value === item.value && twRadioStyles.itemContainer.active
)}
>
<RadioGroup.Item
className={twRadioStyles.radioButton}
value={item.value}
id={`radio-item-${item.value}`}
>
<RadioGroup.Indicator className={twRadioStyles.indicator} />
</RadioGroup.Item>
<label
className={twRadioStyles.label}
htmlFor={`radio-item-${item.value}`}
>
{item.content}
</label>
</div>
);
})}
</RadioGroup.Root>
);
};

View File

@ -0,0 +1,25 @@
import clsx from 'clsx';
import React from 'react';
const twStyles = {
container: `border border-gray-300 mt-3 shadow-md rounded bg-white p-6`,
blueBorder: `border-l-4 border-l-[#297393]`,
};
export const InformationCard: React.FC<{
blueLeftBorder?: boolean;
className?: string;
innerContainerClassName?: string;
}> = ({ children, blueLeftBorder, className, innerContainerClassName }) => {
return (
<div
className={clsx(
twStyles.container,
blueLeftBorder && twStyles.blueBorder,
className
)}
>
{children}
</div>
);
};

View File

@ -0,0 +1,7 @@
export { CopyableInputField } from './CopyableInputField';
export { DatabaseLogo } from './DatabaseLogo';
export { EETrialActive } from './EETrialActive';
export { EETrialExpired } from './EETrialExpired';
export { EETrialInactive } from './EETrialInactive';
export { FancyRadioCards } from './FancyRadioCards';
export { InformationCard } from './InformationCard';

View File

@ -0,0 +1,66 @@
import React from 'react';
import { DatabaseLogo } from './components';
import postgresLogo from './graphics/db-logos/postgres.svg';
import googleLogo from './graphics/db-logos/google.svg';
import microsoftLogo from './graphics/db-logos/microsoft.svg';
import citusLogo from './graphics/db-logos/citus.svg';
import cockroachLogo from './graphics/db-logos/cockroach.svg';
import amazonLogo from './graphics/db-logos/amazon.svg';
import snowflakeLogo from './graphics/db-logos/snowflake.svg';
import { DatabaseKind } from '../../types';
export const dbDisplayNames: Record<DatabaseKind, string> = {
postgres: 'PostgresSQL',
citus: 'Citus',
cockroach: 'CockroachDB',
alloydb: 'AlloyDB',
mssql: 'MSSQL',
bigquery: 'BigQuery',
snowflake: 'Snowflake',
athena: 'Amazon Athena',
};
export const databases: { value: DatabaseKind; content: React.ReactNode }[] = [
{
value: 'postgres',
content: (
<DatabaseLogo title={dbDisplayNames.postgres} image={postgresLogo} />
),
},
{
value: 'citus',
content: <DatabaseLogo title={dbDisplayNames.citus} image={citusLogo} />,
},
{
value: 'cockroach',
content: (
<DatabaseLogo title={dbDisplayNames.cockroach} image={cockroachLogo} />
),
},
{
value: 'alloydb',
content: <DatabaseLogo title={dbDisplayNames.alloydb} image={googleLogo} />,
},
{
value: 'mssql',
content: (
<DatabaseLogo title={dbDisplayNames.mssql} image={microsoftLogo} />
),
},
{
value: 'bigquery',
content: (
<DatabaseLogo title={dbDisplayNames.bigquery} image={googleLogo} />
),
},
{
value: 'snowflake',
content: (
<DatabaseLogo title={dbDisplayNames.snowflake} image={snowflakeLogo} />
),
},
{
value: 'athena',
content: <DatabaseLogo title={dbDisplayNames.athena} image={amazonLogo} />,
},
];

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -0,0 +1,9 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.9375" width="16" height="16" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_44_21233" transform="scale(0.0625)"/>
</pattern>
<image id="image0_44_21233" width="16" height="16" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABdUlEQVQ4jc2TO04jQRCGv+rxjDDaFZiAERkSsNqQG7BH4ABIiARENjEiMBIHmAzYZM0NOALcYEMEC7KEQJqAh7HxGM90FwEv29gWZPuH1fV/XV1dJQxQvvdjUZAYQNGosHZy0C9PegP6e27eWhOLsNAVV448z0Wyevq3L0D/TI+7VhAjsjyoqhfSvhlpR7JSvXsD2J2fZUQjYGyo+V01VGJv/bgs9c3ZX2L0ICgVfK/I6Gfcj9eu2TjPMvHNUgFAHWOP1znGlySYMKHxP7QGgPxBqZ9liX3QEMBa1yh0JrhMw1Zi8YqSBCUvFPPy7BzqZ1nSvnEhEHZ6ugCvsqmGaStP/W+m3a450isboN3GoYDnaylmdVdML+3AFAAz9PQT+g8A37f/HYJsAbUv+GoIW1OV6uHbh9+Wp8e93IsFuka5edHTRGE/gKhU6RjlTjU25ubVuBiel+kVoMKRgWiyUu2/TL2635xZFGW3eWFbGKKwUu27zk/aOpGGPDxYiQAAAABJRU5ErkJggg=="/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,9 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.6875" width="16" height="16" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_44_21195" transform="scale(0.03125)"/>
</pattern>
<image id="image0_44_21195" width="32" height="32" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAE5UlEQVR42rVXa2yUVRDdbh87QkstaNMqz9m2KIVWo9TSat3t2HY02hASKkT9oZhoZov4iI9UoiBGbRSJ6A+MJoOloikhEBNMTKMI0ag8FB8oMYKoiIGgUq0EoVBzm1ny+fnttsWyyfzZvXfOmblnzr0bCo3QB0gygGQUkFwEJJcAyQwgmQYk4933oXPxAZIcICkDkluBZCWQdAPJbiD5EUh+AZKfgOQbIHkHSJ4EklogiYwE8DggaQGSLiA5ACR9QNI/hDgKJGuAZPrZAhcAyQIg+QhI/h4iaFC4rsSHA5wFJAwk7wHJCU8iR+IwkOwBkp1Ash1IvgaSQ0BychASu5xWhgJeCCTt1r5kG98HkmVA0gwk5UBSBCTnA0m+rXffzQOSTiA5koLAaSBZPhh4hQnLLd4PJM8DSU1O7cLRQ+xcNpDEgGRLChJfpNscs7NyLX4mEkuUhkIrMyq21IajHU0Xo3Icle9A5ftReREq34bKMVQej8qZvlwTbBL8BA6lAm8Eku+AZBPEE1Wh0PKMkrUN41B5Hip3ovJWVO5C5UdReQ4qV6FyGSpPQOWxfgKW043grz4C3weBX2OCeiz7qkV5pV3XuYStqLwTlb9C5WWofDkqwzAnKDfgKN72L5oKJBuhXuYXL27JxtVNjMofovJvqLwClTGFA55nInROmJFmkjZ4wN00tXoXOAW3R+KJ60vfaBiDyktRuQeVv0XlZlTO8iXMA5IbgeQlINkMJDtsOp4CkokpOrDVN4aTvFXMjcQTTdHOxiJUfh2VT6Hyp6h8ZUCy6gF9kBwLENZfQLIkYM9l5g/95hGt07rrcpI/TorEE/WTV93g1L0Rlfut8qqARLOB5IcAYCewp4FkFpAU+/aEgeRZz9q38m+5vTD6WlO1+zEzEkuUFbW1uMrXGHivU3wAeE0K8ONAclea82/2GNKXkbrW8mhHUw0qzw7lXL0wK3fOnaNQeTEq9xmBDlSO+JIAkKxPYShuasYGALvKbwKSfbZubySeuLaks7EYlV9A5ckDC1G5DpUPG7gTXiwgmbt2f07j60WetZlAUmqCTFa+G+olVvpmQwSVX0TltiS4q36dgbv4AJXHBBAoN1cMIuBEtc5uylYgWW3W7Sz81IAL1ktF5bbqMCo/gMqfo3I0ScDN+u8eAi+nOMe8FHaaLpzqn4B6uXDmgcoMVF5gnvLgmcSo/IoH3EV7GjertRfPYMBH7OExK+uKe8PRzsYsVL4blY9ahwu9BPb4CLw6iKVeCiQrnJpt9HqBpMeeYc6QlgLJTPdcs/z5Zmq9Vj3/KyEqH/MR2IXKRYOQCNu9X2ldcYAIJKN9uWeg8npUPmkT9jgqh/0ETvsIuIUP/2fhMD6uxah8Hyrv8+Rd67oRtLjHR6DfWnUPKucOA9QpfCIqCypvs6qT+brdVZ1q4ycBBFwcR+VN9gaYYuMa9uzLsvOdispzUXmV2XefL8+7qFySjnlbwCZvnEDl/ai82dropkbtznCVHvRVmwx3mW0443ZpCEyxW69/BONPVH4OlS8Y6vm5Nv8xAsBO0J/ZkeQMR7XZqPyQMT9bYKf4JSnFNkQSN6Py9hRnGhTOXD5G5UdQuRSVM/73/z6b4fnOES35XhPaQatyh11ebfYMLzgn/3xtrgvcQxSVp1tEUdk9z7NHCucflO7Q/CFSv7QAAAAASUVORK5CYII="/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,9 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.3125" width="16" height="16" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_44_21201" transform="scale(0.03125)"/>
</pattern>
<image id="image0_44_21201" width="32" height="32" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IArs4c6QAAAKhQTFRFR3BMaDL/aTP/aDP/aTP/ajP/aTL/aDD/cDD/aTP/ajP/aDT/aTL/ajL/aTP/aDL/aDL/aDP/fU3/aDH/ajT/vaX/s5b/aTP/////2cz/9vL/xrL/7OX/j2b/s5n/ckD/mHP/vaX/tZn/qov/ooD/0L//jmb/q43/4tn/x7P/l3L/hVn/fEz/fE3/7Ob/e0z/vKX/s5j/0MD/vab/oH7/hln/4tj/qoz/OmmteQAAADh0Uk5TAGDfoM+f3yAQ78+A739fgJCQ76DP+vr////////////////////////////////////////////R1fvCAAABX0lEQVQ4y31T53rCMAx0ICEDKKND8iJh7+7x/m9Wm2EpoV/1I/l0Ovt0li1EiCxKihwgL5K7TNxGOnLFEEnaKGd9aES/vvwBbqLFNklbAP8xfH1srd1fSy+2qsaM4ddLtdkopS3A6kOhekbpwOLshvr72hotUVblFRicBLjuFHHKcy/SZvkEZ2ucMGDoToClC5z5jyUkz0RE2cHok4wqCYu4wgQr/9vhGztzEVNi8NP/VqgIiwWNyBW8edgq1kUuiDxX1jtwQmZOKCNMJTiPzim8a0YgCS29R+9UL5lEzHdwMzFQI8Tinnowl21AvjKbdFAWywtBVQG8E1loolTzM2GHdJRu4MNaE54gNVPg4z6qH09Y4LE2bjEI6VrttTyYWQC65ytPTpdGGvMd0s710nZCn0tEGTrs0LUODHh6hNt6jRGiqL++brPebT7glF/e3ij9431nUTvuuWKcRGz1L1LVS43OWNCRAAAAAElFTkSuQmCC"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,9 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.0625" width="16" height="16" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_44_21215" transform="scale(0.0078125)"/>
</pattern>
<image id="image0_44_21215" width="128" height="128" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAABcUlEQVR4nO3YMQ1CQRBAwTsEkGABRQjAC2YQQAtisECCgcPAr/nFmym32mxetfN7Oa8Rc3y859b89hq5Wxz2XoB9CSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAgAAAEiZ4/5Zey/xd9fT3Bqv58jdwh8gTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0CcAOIEECeAOAHECSBOAHECiBNAnADiBBAngDgBxAkgTgBxAogTQJwA4gQQJ4A4AcQJIE4AcQKIE0DcD7bzDOLQOL8WAAAAAElFTkSuQmCC"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,4 @@
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" fill="white"/>
<path d="M15 2.28572V3.71428C15 4.97322 11.8646 6 8 6C4.13541 6 1 4.97322 1 3.71428V2.28572C1 1.02678 4.13541 0 8 0C11.8646 0 15 1.02678 15 2.28572ZM15 5.5V8.71428C15 9.97322 11.8646 11 8 11C4.13541 11 1 9.97322 1 8.71428V5.5C2.50391 6.53572 5.2565 7.01788 8 7.01788C10.7435 7.01788 13.4961 6.53572 15 5.5ZM15 10.5V13.7143C15 14.9732 11.8646 16 8 16C4.13541 16 1 14.9732 1 13.7143V10.5C2.50391 11.5357 5.2565 12.0179 8 12.0179C10.7435 12.0179 13.4961 11.5357 15 10.5Z" fill="#64748B"/>
</svg>

After

Width:  |  Height:  |  Size: 616 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,9 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="0.3125" width="16" height="16" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_44_21227" transform="scale(0.03125)"/>
</pattern>
<image id="image0_44_21227" width="32" height="32" xlink:href="data:image/png;base64,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"/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1 @@
export { SelectDatabase } from './SelectDatabase';

View File

@ -0,0 +1 @@
export const twLayoutWidth = `w-[672px]`;

View File

@ -0,0 +1,5 @@
// returns the correct indefinite article based on the first character of the input string
export const indefiniteArticle = (word: string): string => {
const vowels = ['a', 'e', 'i', 'o', 'u'];
return vowels.includes(word.charAt(0)) ? 'an' : 'a';
};

View File

@ -0,0 +1,2 @@
export { SelectDatabase } from './components/SelectDatabase';
export type { EEState } from './components/SelectDatabase';

View File

@ -0,0 +1,11 @@
export type DatabaseKind =
| 'postgres'
| 'mssql'
| 'bigquery'
| 'citus'
| 'alloydb'
| 'snowflake'
| 'athena'
| 'cockroach';
export type EEState = 'active' | 'inactive' | 'expired';

View File

@ -1,3 +1,4 @@
import { Badge } from '@/new-components/Badge';
import React from 'react';
import { FaDatabase } from 'react-icons/fa';
import { GDCTable } from '..';
@ -28,9 +29,9 @@ export const getTablesListAsTree = async ({
title: (
<div className="inline-block">
<span className="font-bold text-lg">{source.name}</span>
<span className="items-center ml-sm px-sm py-0.5 rounded-full text-sm tracking-wide font-semibold bg-indigo-100 text-indigo-800">
<Badge color="indigo" className="ml-sm">
Beta
</span>
</Badge>
</div>
),
key: JSON.stringify({ database: source.name }),

View File

@ -20,6 +20,7 @@
"@radix-ui/react-collapsible": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@radix-ui/react-radio-group": "^1.1.1",
"@radix-ui/react-switch": "^1.0.0",
"@radix-ui/react-tabs": "^1.0.0",
"@radix-ui/react-tooltip": "^1.0.0",
@ -12300,6 +12301,65 @@
"react-dom": "^16.8 || ^17.0 || ^18.0"
}
},
"node_modules/@radix-ui/react-radio-group": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.1.1.tgz",
"integrity": "sha512-fmg1CuDKt3GAkL3YnHekmdOicyrXlbp/s/D0MrHa+YB2Un+umpJGheiRowlQtxSpb1eeehKNTINgNESi8WK5rA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.0",
"@radix-ui/react-compose-refs": "1.0.0",
"@radix-ui/react-context": "1.0.0",
"@radix-ui/react-direction": "1.0.0",
"@radix-ui/react-presence": "1.0.0",
"@radix-ui/react-primitive": "1.0.1",
"@radix-ui/react-roving-focus": "1.0.2",
"@radix-ui/react-use-controllable-state": "1.0.0",
"@radix-ui/react-use-previous": "1.0.0",
"@radix-ui/react-use-size": "1.0.0"
},
"peerDependencies": {
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
}
},
"node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-collection": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.1.tgz",
"integrity": "sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.0",
"@radix-ui/react-context": "1.0.0",
"@radix-ui/react-primitive": "1.0.1",
"@radix-ui/react-slot": "1.0.1"
},
"peerDependencies": {
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
}
},
"node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-roving-focus": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.2.tgz",
"integrity": "sha512-HLK+CqD/8pN6GfJm3U+cqpqhSKYAWiOJDe+A+8MfxBnOue39QEeMa43csUn2CXCHQT0/mewh1LrrG4tfkM9DMA==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.0",
"@radix-ui/react-collection": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.0",
"@radix-ui/react-context": "1.0.0",
"@radix-ui/react-direction": "1.0.0",
"@radix-ui/react-id": "1.0.0",
"@radix-ui/react-primitive": "1.0.1",
"@radix-ui/react-use-callback-ref": "1.0.0",
"@radix-ui/react-use-controllable-state": "1.0.0"
},
"peerDependencies": {
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
}
},
"node_modules/@radix-ui/react-roving-focus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.0.tgz",
@ -73121,6 +73181,55 @@
"@radix-ui/react-slot": "1.0.1"
}
},
"@radix-ui/react-radio-group": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.1.1.tgz",
"integrity": "sha512-fmg1CuDKt3GAkL3YnHekmdOicyrXlbp/s/D0MrHa+YB2Un+umpJGheiRowlQtxSpb1eeehKNTINgNESi8WK5rA==",
"requires": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.0",
"@radix-ui/react-compose-refs": "1.0.0",
"@radix-ui/react-context": "1.0.0",
"@radix-ui/react-direction": "1.0.0",
"@radix-ui/react-presence": "1.0.0",
"@radix-ui/react-primitive": "1.0.1",
"@radix-ui/react-roving-focus": "1.0.2",
"@radix-ui/react-use-controllable-state": "1.0.0",
"@radix-ui/react-use-previous": "1.0.0",
"@radix-ui/react-use-size": "1.0.0"
},
"dependencies": {
"@radix-ui/react-collection": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.1.tgz",
"integrity": "sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==",
"requires": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-compose-refs": "1.0.0",
"@radix-ui/react-context": "1.0.0",
"@radix-ui/react-primitive": "1.0.1",
"@radix-ui/react-slot": "1.0.1"
}
},
"@radix-ui/react-roving-focus": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.2.tgz",
"integrity": "sha512-HLK+CqD/8pN6GfJm3U+cqpqhSKYAWiOJDe+A+8MfxBnOue39QEeMa43csUn2CXCHQT0/mewh1LrrG4tfkM9DMA==",
"requires": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.0",
"@radix-ui/react-collection": "1.0.1",
"@radix-ui/react-compose-refs": "1.0.0",
"@radix-ui/react-context": "1.0.0",
"@radix-ui/react-direction": "1.0.0",
"@radix-ui/react-id": "1.0.0",
"@radix-ui/react-primitive": "1.0.1",
"@radix-ui/react-use-callback-ref": "1.0.0",
"@radix-ui/react-use-controllable-state": "1.0.0"
}
}
}
},
"@radix-ui/react-roving-focus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.0.tgz",
@ -95198,7 +95307,7 @@
"@emotion/core": "^10.0.22",
"@mdx-js/react": "^1.5.2",
"codemirror": "5.51.0",
"codemirror-graphql": "0.12.2",
"codemirror-graphql": "^0.12.0-alpha.0",
"copy-to-clipboard": "^3.2.0",
"entities": "^2.0.0",
"markdown-it": "^10.0.0",

View File

@ -55,6 +55,7 @@
"@radix-ui/react-collapsible": "^1.0.0",
"@radix-ui/react-dialog": "^1.0.0",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@radix-ui/react-radio-group": "^1.1.1",
"@radix-ui/react-switch": "^1.0.0",
"@radix-ui/react-tabs": "^1.0.0",
"@radix-ui/react-tooltip": "^1.0.0",

View File

@ -11,6 +11,9 @@ module.exports = {
mono: ['"Overpass Mono"', 'ui-monospace', 'monospace'],
},
extend: {
boxShadow: {
eq: 'rgb(0 0 0) 0px 0px 2px',
},
colors: {
current: 'currentColor',
yellow: colors.amber,