EditEnvironmentVariableRow cleanup (#74)

This commit is contained in:
Vivian Phung 2024-05-16 20:39:42 -04:00 committed by GitHub
parent 41666568f5
commit 8cb5eadfb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 328 additions and 85 deletions

View File

@ -1,16 +1,20 @@
import { useCallback, useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import { EnvironmentVariable } from 'gql-client';
import {
IconButton,
Typography,
} from '@snowballtools/material-tailwind-react-fork';
import { useGQLClient } from 'context/GQLClientContext';
import { DeleteVariableDialog } from 'components/projects/Dialog/DeleteVariableDialog';
import { Input } from 'components/shared/Input';
import { Button } from 'components/shared/Button';
import {
CheckRoundFilledIcon,
CrossIcon,
EditBigIcon,
HideEyeOffIcon,
ShowEyeIcon,
TrashIcon,
} from 'components/shared/CustomIcon';
import { EnvironmentVariable } from 'gql-client';
import { useToast } from 'components/shared/Toast';
const ShowPasswordIcon = ({
handler,
@ -26,19 +30,24 @@ const ShowPasswordIcon = ({
}}
className="cursor-pointer"
>
{isVisible ? '-' : '+'}
{isVisible ? <ShowEyeIcon size={16} /> : <HideEyeOffIcon size={16} />}
</span>
);
};
export interface EditEnvironmentVariableRowProps {
variable: EnvironmentVariable;
onUpdate: () => Promise<void>;
isFirstVariable?: boolean;
}
const EditEnvironmentVariableRow = ({
variable,
onUpdate,
}: {
variable: EnvironmentVariable;
onUpdate: () => Promise<void>;
}) => {
isFirstVariable = false,
}: EditEnvironmentVariableRowProps) => {
const client = useGQLClient();
const { toast, dismiss } = useToast();
const { handleSubmit, register, reset } = useForm({
defaultValues: {
@ -58,9 +67,19 @@ const EditEnvironmentVariableRow = ({
if (isEnvironmentVariableRemoved) {
await onUpdate();
setDeleteDialogOpen((preVal) => !preVal);
toast.success('Variable deleted');
toast({
id: 'variable_deleted',
title: 'Variable deleted',
variant: 'success',
onDismiss: dismiss,
});
} else {
toast.error('Variable not deleted');
toast({
id: 'variable_not_deleted',
title: 'Variable not deleted',
variant: 'error',
onDismiss: dismiss,
});
}
}, [variable, onUpdate]);
@ -71,10 +90,21 @@ const EditEnvironmentVariableRow = ({
if (isEnvironmentVariableUpdated) {
await onUpdate();
toast.success('Variable edited');
toast({
id: 'variable_updated',
title: 'Variable edited',
variant: 'success',
onDismiss: dismiss,
});
setEdit((preVal) => !preVal);
} else {
toast.error('Variable not edited');
toast({
id: 'variable_not_updated',
title: 'Variable not edited',
variant: 'error',
onDismiss: dismiss,
});
}
},
[variable, onUpdate],
@ -86,71 +116,55 @@ const EditEnvironmentVariableRow = ({
return (
<>
<div className="flex gap-1">
<div>
<Typography variant="small">Key</Typography>
<Input disabled={!edit} {...register(`key`)} />
</div>
<div>
<Typography variant="small">Value</Typography>
<Input
disabled={!edit}
type={showPassword ? 'text' : 'password'}
leftIcon={
<ShowPasswordIcon
handler={() => {
setShowPassword((prevShowPassword) => !prevShowPassword);
}}
isVisible={showPassword}
/>
<div className="flex gap-1 items-end">
<Input
disabled={!edit}
{...register(`key`)}
label={isFirstVariable ? 'Key' : undefined}
/>
<Input
disabled={!edit}
type={showPassword || edit ? 'text' : 'password'}
label={isFirstVariable ? 'Value' : undefined}
rightIcon={
<ShowPasswordIcon
handler={() => {
setShowPassword((prevShowPassword) => !prevShowPassword);
}}
isVisible={showPassword || edit}
/>
}
{...register(`value`)}
/>
<Button
iconOnly
size="md"
onClick={() => {
edit
? handleSubmit(updateEnvironmentVariableHandler)()
: setEdit((preVal) => !preVal);
}}
>
{edit ? (
<CheckRoundFilledIcon size={16} />
) : (
<EditBigIcon size={16} />
)}
</Button>
<Button
iconOnly
size="md"
onClick={() => {
if (edit) {
reset();
setEdit((preVal) => !preVal);
} else {
setDeleteDialogOpen((preVal) => !preVal);
}
{...register(`value`)}
/>
</div>
{edit ? (
<>
<div className="self-end">
<IconButton
onClick={handleSubmit(updateEnvironmentVariableHandler)}
size="sm"
>
{'S'}
</IconButton>
</div>
<div className="self-end">
<IconButton
size="sm"
onClick={() => {
reset();
setEdit((preVal) => !preVal);
}}
>
{'C'}
</IconButton>
</div>
</>
) : (
<>
<div className="self-end">
<IconButton
size="sm"
onClick={() => {
setEdit((preVal) => !preVal);
}}
>
{'E'}
</IconButton>
</div>
<div className="self-end">
<IconButton
size="sm"
onClick={() => setDeleteDialogOpen((preVal) => !preVal)}
>
{'D'}
</IconButton>
</div>
</>
)}
}}
>
{edit ? <CrossIcon size={16} /> : <TrashIcon size={16} />}
</Button>
</div>
<DeleteVariableDialog
handleCancel={() => setDeleteDialogOpen((preVal) => !preVal)}

View File

@ -0,0 +1,26 @@
import { CustomIcon, CustomIconProps } from './CustomIcon';
export const EditBigIcon: React.FC<CustomIconProps> = (props) => {
return (
<CustomIcon
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
{...props}
>
<g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M4.61304 1.99935L7.5 1.99935C7.77614 1.99935 8 2.22321 8 2.49935C8 2.77549 7.77614 2.99935 7.5 2.99935H4.63333C4.25171 2.99935 3.99557 2.99974 3.79832 3.01585C3.6069 3.03149 3.51538 3.05941 3.45501 3.09018C3.29821 3.17007 3.17072 3.29755 3.09083 3.45436C3.06007 3.51473 3.03214 3.60625 3.01651 3.79767C3.00039 3.99492 3 4.25106 3 4.63268V11.366C3 11.7476 3.00039 12.0038 3.01651 12.201C3.03214 12.3924 3.06007 12.484 3.09083 12.5443C3.17072 12.7011 3.29821 12.8286 3.45501 12.9085C3.51538 12.9393 3.6069 12.9672 3.79832 12.9828C3.99557 12.999 4.25171 12.9993 4.63333 12.9993H11.3667C11.7483 12.9993 12.0044 12.999 12.2017 12.9828C12.3931 12.9672 12.4846 12.9393 12.545 12.9085C12.7018 12.8286 12.8293 12.7011 12.9092 12.5443C12.9399 12.484 12.9679 12.3924 12.9835 12.201C12.9996 12.0038 13 11.7476 13 11.366V8.49935C13 8.22321 13.2239 7.99935 13.5 7.99935C13.7761 7.99935 14 8.22321 14 8.49935V11.3863C14 11.7424 14 12.0396 13.9802 12.2825C13.9595 12.5357 13.9147 12.7735 13.8002 12.9983C13.6244 13.3433 13.3439 13.6238 12.999 13.7995C12.7741 13.9141 12.5364 13.9588 12.2831 13.9795C12.0403 13.9994 11.7431 13.9994 11.387 13.9993H4.61303C4.25693 13.9994 3.95971 13.9994 3.71689 13.9795C3.46363 13.9588 3.22586 13.9141 3.00102 13.7995C2.65605 13.6238 2.37559 13.3433 2.19982 12.9983C2.08526 12.7735 2.04052 12.5357 2.01983 12.2825C1.99999 12.0396 1.99999 11.7424 2 11.3863V4.61239C1.99999 4.25629 1.99999 3.95906 2.01983 3.71624C2.04052 3.46298 2.08526 3.22521 2.19982 3.00037C2.37559 2.6554 2.65605 2.37494 3.00102 2.19917C3.22586 2.08461 3.46363 2.03987 3.71689 2.01918C3.95971 1.99934 4.25694 1.99934 4.61304 1.99935Z"
fill="currentColor"
/>
<path
d="M13.963 1.92174C13.2471 1.20578 12.0863 1.20578 11.3703 1.92174L5.67504 7.617C5.45625 7.83579 5.33333 8.13254 5.33333 8.44196V10.1658C5.33333 10.442 5.55719 10.6658 5.83333 10.6658H7.55719C7.86661 10.6658 8.16336 10.5429 8.38215 10.3241L14.0774 4.62884C14.7934 3.91288 14.7934 2.75208 14.0774 2.03612L13.963 1.92174Z"
fill="currentColor"
/>
</g>
</CustomIcon>
);
};

View File

@ -0,0 +1,18 @@
import { CustomIcon, CustomIconProps } from './CustomIcon';
export const HideEyeOffIcon: React.FC<CustomIconProps> = (props) => {
return (
<CustomIcon
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
d="M2.02012 1.31246C1.82486 1.1172 1.50828 1.1172 1.31301 1.31246C1.11775 1.50772 1.11775 1.82431 1.31301 2.01957L2.02012 1.31246ZM1.62744 7.42685L1.1887 7.18704L1.62744 7.42685ZM11.7196 11.719L12.0731 11.3655L11.7196 11.719ZM13.9797 14.6862C14.1749 14.8815 14.4915 14.8815 14.6868 14.6862C14.8821 14.491 14.8821 14.1744 14.6868 13.9791L13.9797 14.6862ZM1.62804 8.57302L1.18932 8.81287H1.18932L1.62804 8.57302ZM5.2498 3.12216C4.99958 3.23898 4.89145 3.53652 5.00828 3.78674C5.1251 4.03695 5.42264 4.14508 5.67286 4.02826L5.2498 3.12216ZM14.3718 7.42567L14.8105 7.18582L14.3718 7.42567ZM12.3668 10.4904C12.1743 10.6884 12.1786 11.0049 12.3766 11.1975C12.5745 11.39 12.891 11.3857 13.0836 11.1877L12.3668 10.4904ZM14.3724 8.57183L13.9336 8.33203V8.33203L14.3724 8.57183ZM6.58569 6.58512L6.93924 6.23157C6.74398 6.03631 6.4274 6.03631 6.23213 6.23157L6.58569 6.58512ZM9.41411 9.41355L9.76767 9.7671C9.86144 9.67334 9.91411 9.54616 9.91411 9.41355C9.91411 9.28094 9.86144 9.15377 9.76767 9.06L9.41411 9.41355ZM1.31301 2.01957L3.92667 4.63323L4.63378 3.92612L2.02012 1.31246L1.31301 2.01957ZM2.06618 7.66665C2.78183 6.35732 3.64551 5.3672 4.57648 4.68246L3.98397 3.87689C2.91986 4.65956 1.96456 5.76756 1.1887 7.18704L2.06618 7.66665ZM3.92667 4.63323L11.366 12.0726L12.0731 11.3655L4.63378 3.92612L3.92667 4.63323ZM11.366 12.0726L13.9797 14.6862L14.6868 13.9791L12.0731 11.3655L11.366 12.0726ZM1.18932 8.81287C2.47954 11.1728 4.26886 12.674 6.21987 13.243C8.17733 13.8138 10.2399 13.428 12.0158 12.1218L11.4233 11.3162C9.88108 12.4506 8.13835 12.7608 6.49983 12.283C4.85485 11.8033 3.25748 10.5112 2.06675 8.33317L1.18932 8.81287ZM1.1887 7.18704C0.911724 7.69378 0.912792 8.30707 1.18932 8.81287L2.06675 8.33317C1.95313 8.12535 1.95308 7.87358 2.06618 7.66665L1.1887 7.18704ZM5.67286 4.02826C7.09829 3.36272 8.63166 3.32591 10.0669 3.91426C11.5069 4.50457 12.8795 5.7385 13.933 7.66553L14.8105 7.18582C13.6688 5.09759 12.1371 3.68216 10.4462 2.98899C8.75047 2.29385 6.92622 2.33943 5.2498 3.12216L5.67286 4.02826ZM13.0836 11.1877C13.7227 10.5308 14.305 9.7376 14.8111 8.81164L13.9336 8.33203C13.4669 9.18586 12.9371 9.90419 12.3668 10.4904L13.0836 11.1877ZM13.933 7.66553C14.0467 7.87336 14.0467 8.12511 13.9336 8.33203L14.8111 8.81164C15.0881 8.30489 15.087 7.69161 14.8105 7.18582L13.933 7.66553ZM7.9999 9.49934C7.17147 9.49934 6.4999 8.82776 6.4999 7.99934H5.4999C5.4999 9.38005 6.61919 10.4993 7.9999 10.4993V9.49934ZM6.4999 7.99934C6.4999 7.58499 6.66725 7.21067 6.93924 6.93868L6.23213 6.23157C5.78027 6.68343 5.4999 7.30912 5.4999 7.99934H6.4999ZM6.23213 6.93868L9.06056 9.7671L9.76767 9.06L6.93924 6.23157L6.23213 6.93868ZM9.06056 9.06C8.78857 9.33199 8.41425 9.49934 7.9999 9.49934V10.4993C8.69012 10.4993 9.3158 10.219 9.76767 9.7671L9.06056 9.06Z"
fill="currentColor"
/>
</CustomIcon>
);
};

View File

@ -0,0 +1,18 @@
import { CustomIcon, CustomIconProps } from './CustomIcon';
export const ShowEyeIcon: React.FC<CustomIconProps> = (props) => {
return (
<CustomIcon
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
{...props}
>
<path
d="M1.62774 7.42694L2.06646 7.66677L1.62774 7.42694ZM14.3721 7.42686L13.9333 7.66669V7.66669L14.3721 7.42686ZM1.62774 8.5731L2.06646 8.33327H2.06646L1.62774 8.5731ZM14.3721 8.57302L14.8108 8.81285L14.8108 8.81285L14.3721 8.57302ZM2.06646 7.66677C3.61474 4.83447 5.84609 3.50002 7.99991 3.5C10.1537 3.49998 12.3851 4.8344 13.9333 7.66669L14.8108 7.18703C13.1325 4.11679 10.6049 2.49998 7.9999 2.5C5.39486 2.50002 2.86735 4.11687 1.18901 7.18711L2.06646 7.66677ZM1.18901 8.81293C2.86735 11.8832 5.39486 13.5 7.99991 13.5C10.6049 13.4999 13.1325 11.8831 14.8108 8.81285L13.9333 8.33319C12.3851 11.1655 10.1537 12.4999 7.9999 12.5C5.84609 12.5 3.61473 11.1656 2.06646 8.33327L1.18901 8.81293ZM1.18901 7.18711C0.912257 7.69338 0.912257 8.30665 1.18901 8.81293L2.06646 8.33327C1.95311 8.1259 1.95311 7.87414 2.06646 7.66677L1.18901 7.18711ZM13.9333 7.66669C14.0467 7.87406 14.0467 8.12582 13.9333 8.33319L14.8108 8.81285C15.0875 8.30658 15.0875 7.69331 14.8108 7.18703L13.9333 7.66669ZM9.4999 8C9.4999 8.82843 8.82833 9.5 7.9999 9.5V10.5C9.38062 10.5 10.4999 9.38071 10.4999 8H9.4999ZM7.9999 9.5C7.17148 9.5 6.4999 8.82843 6.4999 8H5.4999C5.4999 9.38071 6.61919 10.5 7.9999 10.5V9.5ZM6.4999 8C6.4999 7.17157 7.17148 6.5 7.9999 6.5V5.5C6.61919 5.5 5.4999 6.61929 5.4999 8H6.4999ZM7.9999 6.5C8.82833 6.5 9.4999 7.17157 9.4999 8H10.4999C10.4999 6.61929 9.38062 5.5 7.9999 5.5V6.5Z"
fill="currentColor"
/>
</CustomIcon>
);
};

View File

@ -73,6 +73,9 @@ export * from './SwitchIcon';
export * from './CollaboratorsIcon';
export * from './ChevronUpSmallIcon';
export * from './ChevronDownSmallIcon';
export * from './EditBigIcon';
export * from './ShowEyeIcon';
export * from './HideEyeOffIcon';
// Templates
export * from './templates';

View File

@ -0,0 +1,29 @@
import { Meta, StoryObj } from '@storybook/react';
import { EditBigIcon } from 'components/shared/CustomIcon';
const meta: Meta<typeof EditBigIcon> = {
title: 'Icons/EditBigIcon',
component: EditBigIcon,
tags: ['autodocs'],
argTypes: {
size: {
control: 'text',
},
name: {
control: 'text',
},
},
};
export default meta;
type Story = StoryObj<typeof EditBigIcon>;
export const Default: Story = {
render: ({ size, name }) => <EditBigIcon size={size} name={name} />,
args: {
size: '24px',
name: 'plus',
},
};

View File

@ -0,0 +1,29 @@
import { Meta, StoryObj } from '@storybook/react';
import { HideEyeOffIcon } from 'components/shared/CustomIcon';
const meta: Meta<typeof HideEyeOffIcon> = {
title: 'Icons/HideEyeOffIcon',
component: HideEyeOffIcon,
tags: ['autodocs'],
argTypes: {
size: {
control: 'text',
},
name: {
control: 'text',
},
},
};
export default meta;
type Story = StoryObj<typeof HideEyeOffIcon>;
export const Default: Story = {
render: ({ size, name }) => <HideEyeOffIcon size={size} name={name} />,
args: {
size: '24px',
name: 'plus',
},
};

View File

@ -0,0 +1,29 @@
import { Meta, StoryObj } from '@storybook/react';
import { ShowEyeIcon } from 'components/shared/CustomIcon';
const meta: Meta<typeof ShowEyeIcon> = {
title: 'Icons/ShowEyeIcon',
component: ShowEyeIcon,
tags: ['autodocs'],
argTypes: {
size: {
control: 'text',
},
name: {
control: 'text',
},
},
};
export default meta;
type Story = StoryObj<typeof ShowEyeIcon>;
export const Default: Story = {
render: ({ size, name }) => <ShowEyeIcon size={size} name={name} />,
args: {
size: '24px',
name: 'plus',
},
};

View File

@ -5,6 +5,12 @@ import {
Role,
OrganizationMember,
ProjectMember,
EnvironmentVariable,
Deployment,
DeploymentStatus,
DomainStatus,
Domain,
Environment,
} from 'gql-client';
export const user: User = {
@ -17,7 +23,7 @@ export const user: User = {
gitHubToken: 'GitHub Token',
};
const organizationMember: OrganizationMember = {
export const organizationMember: OrganizationMember = {
id: '1',
member: user,
role: Role.Owner,
@ -35,7 +41,7 @@ export const organization: Organization = {
projects: [],
};
const member: ProjectMember = {
export const member: ProjectMember = {
id: '1',
member: user,
permissions: [],
@ -44,18 +50,61 @@ const member: ProjectMember = {
updatedAt: '2021-08-01T00:00:00.000Z',
};
export const environmentVariable0: EnvironmentVariable = {
id: '1',
key: 'API_KEY',
value: '123456',
environment: Environment.Development,
createdAt: '2021-08-01T00:00:00.000Z',
updatedAt: '2021-08-01T00:00:00.000Z',
};
export const environmentVariable1: EnvironmentVariable = {
id: '2',
key: 'API_KEY_2',
value: '123456',
environment: Environment.Development,
createdAt: '2021-08-01T00:00:00.000Z',
updatedAt: '2021-08-01T00:00:00.000Z',
};
export const domain0: Domain = {
id: '1',
name: 'Domain',
createdAt: '2021-08-01T00:00:00.000Z',
updatedAt: '2021-08-01T00:00:00.000Z',
branch: 'Branch',
status: DomainStatus.Live,
redirectTo: null,
};
export const deployment0: Deployment = {
id: '1',
url: 'https://deployment.com',
status: DeploymentStatus.Ready,
createdAt: '2021-08-01T00:00:00.000Z',
updatedAt: '2021-08-01T00:00:00.000Z',
branch: 'Branch',
environment: Environment.Development,
isCurrent: true,
commitHash: 'Commit Hash',
domain: domain0,
commitMessage: 'Commit Message',
createdBy: user,
};
export const project: Project = {
id: '1',
name: 'Project',
name: 'GithubUsername-ProjectName',
owner: user,
deployments: [],
deployments: [deployment0],
repository: 'Repository',
prodBranch: 'Prod Branch',
description: 'Description',
createdAt: '2021-08-01T00:00:00.000Z',
updatedAt: '2021-08-01T00:00:00.000Z',
framework: 'NextJS',
environmentVariables: [],
environmentVariables: [environmentVariable0, environmentVariable1],
organization: organization,
template: 'Template',
members: [member],

View File

@ -0,0 +1,28 @@
import { Meta, StoryObj } from '@storybook/react';
import EditEnvironmentVariableRow from 'components/projects/project/settings/EditEnvironmentVariableRow';
import { environmentVariable0 } from '../../MockStoriesData';
const meta: Meta<typeof EditEnvironmentVariableRow> = {
title: 'Project/Settings/EditEnvironmentVariableRow',
component: EditEnvironmentVariableRow,
argTypes: {
variable: {
control: {
type: 'object',
},
},
onUpdate: {
action: 'update',
},
},
args: {
variable: environmentVariable0,
},
};
export default meta;
type Story = StoryObj<typeof EditEnvironmentVariableRow>;
export const Default: Story = {};