Add section for displaying environment variables in project settings (#27)

* Implement functionality to display environment variables

* Handle confirm of confirm dialogs

* Reset to default value on cancel button click

* Rename delete dialog open variable

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
Nabarun Gogoi 2023-12-27 11:29:31 +05:30 committed by GitHub
parent bab09bd858
commit 642ba72b8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 265 additions and 19 deletions

View File

@ -0,0 +1,14 @@
[
{
"id": 1,
"key": "ABC",
"value": "ABC",
"environments": ["production", "preview"]
},
{
"id": 2,
"key": "XYZ",
"value": "abc3",
"environments": ["preview"]
}
]

View File

@ -93,10 +93,11 @@ const DeploymentDetailsCard = ({
</div>
<ConfirmDialog
dialogTitle="Change to production?"
handleOpen={() => setChangeToProduction(!changeToProduction)}
handleOpen={() => setChangeToProduction((preVal) => !preVal)}
open={changeToProduction}
confirmButtonTitle="Change"
color="blue"
handleConfirm={() => setChangeToProduction((preVal) => !preVal)}
>
<div className="flex flex-col gap-2">
<Typography variant="small">
@ -116,10 +117,11 @@ const DeploymentDetailsCard = ({
</ConfirmDialog>
<ConfirmDialog
dialogTitle="Redeploy to production?"
handleOpen={() => setRedeployToProduction(!redeployToProduction)}
handleOpen={() => setRedeployToProduction((preVal) => !preVal)}
open={redeployToProduction}
confirmButtonTitle="Redeploy"
color="blue"
handleConfirm={() => setRedeployToProduction((preVal) => !preVal)}
>
<div className="flex flex-col gap-2">
<Typography variant="small">
@ -140,10 +142,11 @@ const DeploymentDetailsCard = ({
</ConfirmDialog>
<ConfirmDialog
dialogTitle="Rollback to this deployment?"
handleOpen={() => setRollbackDeployment(!rollbackDeployment)}
handleOpen={() => setRollbackDeployment((preVal) => !preVal)}
open={rollbackDeployment}
confirmButtonTitle="Rollback"
color="blue"
handleConfirm={() => setRollbackDeployment((preVal) => !preVal)}
>
<div className="flex flex-col gap-2">
<Typography variant="small">

View File

@ -5,17 +5,17 @@ import { Typography, Input, IconButton } from '@material-tailwind/react';
import { EnvironmentVariablesFormValues } from './EnvironmentVariablesTabPanel';
interface EnvironmentVariableProps {
interface AddEnvironmentVariableRowProps {
onDelete: () => void;
register: UseFormRegister<EnvironmentVariablesFormValues>;
index: number;
}
const EnvironmentVariable = ({
const AddEnvironmentVariableRow = ({
onDelete,
register,
index,
}: EnvironmentVariableProps) => {
}: AddEnvironmentVariableRowProps) => {
return (
<div className="flex gap-1 p-2">
<div>
@ -41,4 +41,4 @@ const EnvironmentVariable = ({
);
};
export default EnvironmentVariable;
export default AddEnvironmentVariableRow;

View File

@ -0,0 +1,53 @@
import React, { useState } from 'react';
import { Card, Collapse, Typography } from '@material-tailwind/react';
import HorizontalLine from '../../../HorizontalLine';
import EditEnvironmentVariableRow from './EditEnvironmentVariableRow';
import { Environments, EnvironmentVariable } from '../../../../types/project';
interface DisplayEnvironmentVariablesProps {
environment: Environments;
variables: EnvironmentVariable[];
}
const DisplayEnvironmentVariables = ({
environment,
variables,
}: DisplayEnvironmentVariablesProps) => {
const [openCollapse, setOpenCollapse] = useState(false);
return (
<>
<div
className="flex gap-4 p-2 "
onClick={() => setOpenCollapse((cur) => !cur)}
>
<div>^</div>
<div className="grow capitalize">{environment}</div>
<div>{variables.length} variables</div>
<HorizontalLine />
</div>
<Collapse open={openCollapse}>
{variables.length === 0 ? (
<Card className="bg-gray-300 flex items-center p-4">
<Typography variant="small" className="text-black">
No environment variables added yet.
</Typography>
<Typography variant="small">
Once you add them, theyll show up here.
</Typography>
</Card>
) : (
variables.map((variable: EnvironmentVariable, index: number) => {
return (
<EditEnvironmentVariableRow key={index} variable={variable} />
);
})
)}
</Collapse>
</>
);
};
export default DisplayEnvironmentVariables;

View File

@ -0,0 +1,142 @@
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import { IconButton, Input, Typography } from '@material-tailwind/react';
import ConfirmDialog from '../../../shared/ConfirmDialog';
import { EnvironmentVariable } from '../../../../types/project';
const ShowPasswordIcon = ({
handler,
isVisible,
}: {
handler: () => void;
isVisible: boolean;
}) => {
return (
<span
onClick={() => {
handler();
}}
className="cursor-pointer"
>
{isVisible ? '-' : '+'}
</span>
);
};
const EditEnvironmentVariableRow = ({
variable,
}: {
variable: EnvironmentVariable;
}) => {
const { handleSubmit, register, reset } = useForm({
defaultValues: {
key: variable.key,
value: variable.value,
},
});
const [showPassword, setShowPassword] = useState(false);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [edit, setEdit] = useState(false);
return (
<>
<div className="flex gap-1 p-2">
<div>
<Typography variant="small">Key</Typography>
<Input
crossOrigin={undefined}
disabled={!edit}
{...register(`key`)}
/>
</div>
<div>
<Typography variant="small">Value</Typography>
<Input
crossOrigin={undefined}
disabled={!edit}
type={showPassword ? 'text' : 'password'}
icon={
<ShowPasswordIcon
handler={() => {
setShowPassword((prevShowPassword) => !prevShowPassword);
}}
isVisible={showPassword}
/>
}
{...register(`value`)}
/>
</div>
{edit ? (
<>
<div className="self-end">
<IconButton
onClick={handleSubmit(() => {
toast.success('Variable edited');
setEdit((preVal) => !preVal);
})}
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>
</>
)}
</div>
<ConfirmDialog
dialogTitle="Delete variable"
handleOpen={() => setDeleteDialogOpen((preVal) => !preVal)}
open={deleteDialogOpen}
confirmButtonTitle="Yes, Confirm delete"
handleConfirm={() => {
setDeleteDialogOpen((preVal) => !preVal);
toast.success('Variable deleted');
}}
color="red"
>
<Typography variant="small">
Are you sure you want to delete the variable
<span className="bg-blue-100">{variable.key}</span>?
</Typography>
</ConfirmDialog>
</>
);
};
export default EditEnvironmentVariableRow;

View File

@ -1,5 +1,6 @@
import React, { useState } from 'react';
import React, { useCallback, useState } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { Toaster } from 'react-hot-toast';
import {
Typography,
@ -9,7 +10,10 @@ import {
Checkbox,
} from '@material-tailwind/react';
import EnvironmentVariable from './EnvironmentVariable';
import AddEnvironmentVariableRow from './AddEnvironmentVariableRow';
import DisplayEnvironmentVariables from './DisplayEnvironmentVariables';
import environmentVariablesData from '../../../../assets/environment-variables.json';
import { EnvironmentVariable, Environments } from '../../../../types/project';
export type EnvironmentVariablesFormValues = {
variables: {
@ -43,6 +47,12 @@ export const EnvironmentVariablesTabPanel = () => {
control,
});
const getEnvironmentVariable = useCallback((environment: Environments) => {
return (environmentVariablesData as EnvironmentVariable[]).filter((item) =>
item.environments.includes(environment),
);
}, []);
return (
<>
<Typography variant="h6">Environment variables</Typography>
@ -61,13 +71,12 @@ export const EnvironmentVariablesTabPanel = () => {
<form onSubmit={handleSubmit(() => {})}>
{fields.map((field, index) => {
return (
<div key={field.id}>
<EnvironmentVariable
index={index}
register={register}
onDelete={() => remove(index)}
/>
</div>
<AddEnvironmentVariableRow
key={field.id}
index={index}
register={register}
onDelete={() => remove(index)}
/>
);
})}
<div className="flex gap-1 p-2">
@ -116,6 +125,21 @@ export const EnvironmentVariablesTabPanel = () => {
</Card>
</Collapse>
</div>
<div className="p-2">
<DisplayEnvironmentVariables
environment={Environments.PRODUCTION}
variables={getEnvironmentVariable(Environments.PRODUCTION)}
/>
<DisplayEnvironmentVariables
environment={Environments.PREVIEW}
variables={getEnvironmentVariable(Environments.PREVIEW)}
/>
<DisplayEnvironmentVariables
environment={Environments.DEVELOPMENT}
variables={getEnvironmentVariable(Environments.DEVELOPMENT)}
/>
</div>
<Toaster position="bottom-center" />
</>
);
};

View File

@ -66,7 +66,6 @@ const GeneralTabPanel = () => {
App name
</Typography>
<Input
id="input"
variant="outlined"
// TODO: Debug issue: https://github.com/creativetimofficial/material-tailwind/issues/427
crossOrigin={undefined}
@ -77,7 +76,6 @@ const GeneralTabPanel = () => {
Description (Optional)
</Typography>
<Input
id="input"
variant="outlined"
crossOrigin={undefined}
size="md"
@ -87,7 +85,6 @@ const GeneralTabPanel = () => {
Project ID
</Typography>
<Input
id="input"
crossOrigin={undefined}
variant="outlined"
value={PROJECT_ID}

View File

@ -35,3 +35,16 @@ export enum Status {
READY = 'Ready',
ERROR = 'Error',
}
export enum Environments {
PRODUCTION = 'production',
PREVIEW = 'preview',
DEVELOPMENT = 'development',
}
export interface EnvironmentVariable {
key: string;
value: string;
id: number;
environments: Environments[];
}