Skip to content

Commit

Permalink
feat: allow user to connect multiple cloud accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
greghub committed Sep 29, 2023
1 parent 7a3e8e0 commit ad95798
Show file tree
Hide file tree
Showing 37 changed files with 1,929 additions and 417 deletions.
101 changes: 52 additions & 49 deletions dashboard/components/account-details/AwsAccountDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { ChangeEvent, ReactNode, useRef, useState } from 'react';
import classNames from 'classnames';
import { AWSCredentials } from '@utils/cloudAccountHelpers';
import Folder2Icon from '../icons/Folder2Icon';
import SelectInput from '../onboarding-wizard/SelectInput';
import LabelledInput from '../onboarding-wizard/LabelledInput';
Expand All @@ -7,7 +9,7 @@ import KeyIcon from '../icons/KeyIcon';
import VariableIcon from '../icons/VariableIcon';
import DocumentTextIcon from '../icons/DocumentTextIcon';
import ShieldSecurityIcon from '../icons/ShieldSecurityIcon';
import { CloudAccount } from '../cloud-account/hooks/useCloudAccounts/useCloudAccount';
import { CloudAccountPayload } from '../cloud-account/hooks/useCloudAccounts/useCloudAccount';

interface SelectOptions {
icon: ReactNode;
Expand All @@ -16,8 +18,8 @@ interface SelectOptions {
}

interface AwsAccountDetailsProps {
cloudAccountData: CloudAccount;
setCloudAccountData: (formData: CloudAccount) => void;
cloudAccountData?: CloudAccountPayload<AWSCredentials>;
hasError?: boolean;
}

const options: SelectOptions[] = [
Expand Down Expand Up @@ -45,12 +47,15 @@ const options: SelectOptions[] = [

function AwsAccountDetails({
cloudAccountData,
setCloudAccountData
hasError = false
}: AwsAccountDetailsProps) {
const [credentialType, setCredentialType] = useState<string>(
options.find(option => option.value === cloudAccountData.credentials.source)
?.value ?? options[0].value
options.find(
option => option.value === cloudAccountData?.credentials.source
)?.value ?? options[0].value
);
const [isValidationError, setIsValidationError] = useState<boolean>(false);
const [errorMessage, setErrorMessage] = useState<string>('');

const fileInputRef = useRef<HTMLInputElement | null>(null);
const handleButtonClick = () => {
Expand All @@ -59,65 +64,49 @@ function AwsAccountDetails({
}
};

function handleNameChange(event: ChangeEvent<HTMLInputElement>) {
setCloudAccountData({
...cloudAccountData,
name: event?.target.value
});
}

function handleSelectChange(newValue: string) {
setCredentialType(newValue);

setCloudAccountData({
...cloudAccountData,
credentials: {
...cloudAccountData.credentials,
source: newValue
}
});
}

function handleProfileChange(event: ChangeEvent<HTMLInputElement>) {
setCloudAccountData({
...cloudAccountData,
credentials: {
...cloudAccountData.credentials,
profile: event?.target.value
}
});
}

const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];

if (!file) return;

setCloudAccountData({
...cloudAccountData,
credentials: {
...cloudAccountData.credentials,
path: file.name
const fileName = event.target.files?.[0]?.name;

if (fileName) {
if (!fileName.endsWith('.db')) {
setIsValidationError(true);
setErrorMessage(
'The chosen file is not supported. Please choose a different file for the credentials.'
);
}
});
} else {
setIsValidationError(true);
setErrorMessage('Please choose a file.');
}
};

return (
<div className="flex flex-col space-y-8 py-10">
<div className="flex flex-col space-y-4 py-10">
<LabelledInput
type="text"
id="account-name"
name="name"
label="Account name"
placeholder="my-aws-account"
value={cloudAccountData.name}
onChange={handleNameChange}
required
value={cloudAccountData?.name}
/>

<div className="flex flex-col space-y-8 rounded-md bg-komiser-100 p-5">
<div
className={classNames(
'flex flex-col space-y-8 rounded-md p-5',
hasError ? 'bg-error-100' : 'bg-komiser-100'
)}
>
<div>
<SelectInput
icon="Change"
label={'Source'}
name="source"
label="Source"
displayValues={options}
value={credentialType}
handleChange={handleSelectChange}
Expand All @@ -138,22 +127,26 @@ function AwsAccountDetails({
type="text"
label="File path"
id="file-path-input"
name="path"
icon={<Folder2Icon />}
subLabel="Enter the path or browse the file"
placeholder="C:\Documents\Komiser\credentials"
fileInputRef={fileInputRef}
iconClick={handleButtonClick}
handleFileChange={handleFileChange}
value={cloudAccountData.credentials.path}
value={cloudAccountData?.credentials.path}
hasError={isValidationError}
errorMessage={errorMessage}
/>
<LabelledInput
type="text"
id="profile"
name="profile"
label="Profile"
placeholder="default"
subLabel="Name of the section in the credentials file"
value={cloudAccountData.credentials.profile}
onChange={handleProfileChange}
value={cloudAccountData?.credentials.profile}
required
/>
</div>
)}
Expand All @@ -163,20 +156,30 @@ function AwsAccountDetails({
<LabelledInput
type="text"
id="access-key-id"
name="aws_access_key_id"
label="Access key ID"
placeholder="AKIABCDEFGHIJKLMN12"
subLabel="Unique identifier used to access AWS services"
required
/>
<LabelledInput
type="text"
id="secret-access-key"
name="aws_secret_access_key"
label="Secret access key"
placeholder="AbCdEfGhIjKlMnOpQrStUvWxYz0123456789AbCd"
subLabel="The secret access key is generated by AWS when an access key is created"
required
/>
</div>
)}
</div>
{hasError && (
<div className="text-sm text-error-600">
We couldn&apos;t connect to your AWS account. Please check if the file
is correct.
</div>
)}
</div>
);
}
Expand Down
127 changes: 127 additions & 0 deletions dashboard/components/account-details/AzureAccountDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import classNames from 'classnames';
import { AzureCredentials } from '@utils/cloudAccountHelpers';
import LabelledInput from '../onboarding-wizard/LabelledInput';
import { CloudAccountPayload } from '../cloud-account/hooks/useCloudAccounts/useCloudAccount';

interface AzureAccountDetailsProps {
cloudAccountData?: CloudAccountPayload<AzureCredentials>;
hasError?: boolean;
}

function AzureAccountDetails({
cloudAccountData,
hasError = false
}: AzureAccountDetailsProps) {
return (
<div className="flex flex-col space-y-4 py-10">
<LabelledInput
type="text"
id="account-name"
name="name"
label="Account name"
placeholder="my-azure-account"
required
value={cloudAccountData?.name}
/>

<div
className={classNames(
'flex flex-col space-y-8 rounded-md p-5',
hasError ? 'bg-error-100' : 'bg-komiser-100'
)}
>
<LabelledInput
type="text"
id="source"
name="source"
label="Source"
value="Credentials Keys"
disabled={true}
required
icon={
<svg
width="24"
height="25"
viewBox="0 0 24 25"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
>
<path
d="M19.79 15.5621C17.73 17.6121 14.78 18.2421 12.19 17.4321L7.48002 22.1321C7.14002 22.4821 6.47002 22.6921 5.99002 22.6221L3.81002 22.3221C3.09002 22.2221 2.42002 21.5421 2.31002 20.8221L2.01002 18.6421C1.94002 18.1621 2.17002 17.4921 2.50002 17.1521L7.20002 12.4521C6.40002 9.85215 7.02002 6.90215 9.08002 4.85215C12.03 1.90215 16.82 1.90215 19.78 4.85215C22.74 7.80215 22.74 12.6121 19.79 15.5621Z"
stroke="#0C1717"
stroke-width="1.5"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M6.89001 18.1221L9.19001 20.4221"
stroke="#0C1717"
stroke-width="1.5"
stroke-miterlimit="10"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M14.5 11.6318C15.3284 11.6318 16 10.9603 16 10.1318C16 9.30341 15.3284 8.63184 14.5 8.63184C13.6716 8.63184 13 9.30341 13 10.1318C13 10.9603 13.6716 11.6318 14.5 11.6318Z"
stroke="#0C1717"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
}
/>
<LabelledInput
type="text"
id="tenant-id"
name="tenantId"
value={cloudAccountData?.credentials.tenantId}
required
label="Tenant ID"
subLabel="The ID of the AAD directory in which you created the application"
placeholder="00000-00000-00000-00000"
/>
<LabelledInput
type="text"
id="client-id"
name="clientId"
value={cloudAccountData?.credentials.clientId}
required
label="Client ID"
subLabel="The secret access key is generated by AWS when an access key is created"
placeholder="aSbG%hF7kL9p#2jN5mH8qR3tV6wZ"
/>
<LabelledInput
type="text"
id="client-secret"
name="clientSecret"
value={cloudAccountData?.credentials.clientSecret}
required
label="Client secret"
subLabel="You can find it as the authentication key string"
placeholder="9e5b97c6-16a1-4f5d-a3a7-62c4b3b0d8c7"
/>
<LabelledInput
type="text"
id="subscription-id"
name="subscriptionId"
value={cloudAccountData?.credentials.subscriptionId}
required
label="Subscription ID"
subLabel="The subscription ID is a GUID that uniquely identifies your subscription to use Azure services"
placeholder="abcdef12-3456-7890-1234-567890abcdef"
/>
</div>
{hasError && (
<div className="text-sm text-error-600">
We couldn&apos;t connect to your Azure account. Please check if the
file is correct.
</div>
)}
</div>
);
}

export default AzureAccountDetails;
62 changes: 62 additions & 0 deletions dashboard/components/account-details/CivoAccountDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import classNames from 'classnames';
import RecordCircleIcon from '@components/icons/RecordCircleIcon';
import { CivoCredentials } from '@utils/cloudAccountHelpers';
import LabelledInput from '../onboarding-wizard/LabelledInput';
import { CloudAccountPayload } from '../cloud-account/hooks/useCloudAccounts/useCloudAccount';

interface CivoAccountDetailsProps {
cloudAccountData?: CloudAccountPayload<CivoCredentials>;
hasError?: boolean;
}

function CivoAccountDetails({
cloudAccountData,
hasError = false
}: CivoAccountDetailsProps) {
return (
<div className="flex flex-col space-y-4 py-10">
<LabelledInput
type="text"
id="account-name"
name="name"
value={cloudAccountData?.name}
label="Account name"
placeholder="my-civo-account"
/>

<div
className={classNames(
'flex flex-col space-y-8 rounded-md p-5',
hasError ? 'bg-error-100' : 'bg-komiser-100'
)}
>
<LabelledInput
type="text"
id="source"
name="source"
label="Source"
value="API Token"
disabled={true}
icon={<RecordCircleIcon />}
/>
<LabelledInput
type="text"
id="api-token"
name="token"
value={cloudAccountData?.credentials.token}
label="API token"
subLabel="An API key that is unique to your account"
placeholder="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
/>
</div>
{hasError && (
<div className="text-sm text-error-600">
We couldn&apos;t connect to your Civo account. Please check if the
file is correct.
</div>
)}
</div>
);
}

export default CivoAccountDetails;
Loading

0 comments on commit ad95798

Please sign in to comment.