forked from cerc-io/snowballtools-base
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:
parent
bab09bd858
commit
642ba72b8f
14
packages/frontend/src/assets/environment-variables.json
Normal file
14
packages/frontend/src/assets/environment-variables.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"key": "ABC",
|
||||||
|
"value": "ABC",
|
||||||
|
"environments": ["production", "preview"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"key": "XYZ",
|
||||||
|
"value": "abc3",
|
||||||
|
"environments": ["preview"]
|
||||||
|
}
|
||||||
|
]
|
@ -93,10 +93,11 @@ const DeploymentDetailsCard = ({
|
|||||||
</div>
|
</div>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
dialogTitle="Change to production?"
|
dialogTitle="Change to production?"
|
||||||
handleOpen={() => setChangeToProduction(!changeToProduction)}
|
handleOpen={() => setChangeToProduction((preVal) => !preVal)}
|
||||||
open={changeToProduction}
|
open={changeToProduction}
|
||||||
confirmButtonTitle="Change"
|
confirmButtonTitle="Change"
|
||||||
color="blue"
|
color="blue"
|
||||||
|
handleConfirm={() => setChangeToProduction((preVal) => !preVal)}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Typography variant="small">
|
<Typography variant="small">
|
||||||
@ -116,10 +117,11 @@ const DeploymentDetailsCard = ({
|
|||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
dialogTitle="Redeploy to production?"
|
dialogTitle="Redeploy to production?"
|
||||||
handleOpen={() => setRedeployToProduction(!redeployToProduction)}
|
handleOpen={() => setRedeployToProduction((preVal) => !preVal)}
|
||||||
open={redeployToProduction}
|
open={redeployToProduction}
|
||||||
confirmButtonTitle="Redeploy"
|
confirmButtonTitle="Redeploy"
|
||||||
color="blue"
|
color="blue"
|
||||||
|
handleConfirm={() => setRedeployToProduction((preVal) => !preVal)}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Typography variant="small">
|
<Typography variant="small">
|
||||||
@ -140,10 +142,11 @@ const DeploymentDetailsCard = ({
|
|||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
dialogTitle="Rollback to this deployment?"
|
dialogTitle="Rollback to this deployment?"
|
||||||
handleOpen={() => setRollbackDeployment(!rollbackDeployment)}
|
handleOpen={() => setRollbackDeployment((preVal) => !preVal)}
|
||||||
open={rollbackDeployment}
|
open={rollbackDeployment}
|
||||||
confirmButtonTitle="Rollback"
|
confirmButtonTitle="Rollback"
|
||||||
color="blue"
|
color="blue"
|
||||||
|
handleConfirm={() => setRollbackDeployment((preVal) => !preVal)}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Typography variant="small">
|
<Typography variant="small">
|
||||||
|
@ -5,17 +5,17 @@ import { Typography, Input, IconButton } from '@material-tailwind/react';
|
|||||||
|
|
||||||
import { EnvironmentVariablesFormValues } from './EnvironmentVariablesTabPanel';
|
import { EnvironmentVariablesFormValues } from './EnvironmentVariablesTabPanel';
|
||||||
|
|
||||||
interface EnvironmentVariableProps {
|
interface AddEnvironmentVariableRowProps {
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
register: UseFormRegister<EnvironmentVariablesFormValues>;
|
register: UseFormRegister<EnvironmentVariablesFormValues>;
|
||||||
index: number;
|
index: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EnvironmentVariable = ({
|
const AddEnvironmentVariableRow = ({
|
||||||
onDelete,
|
onDelete,
|
||||||
register,
|
register,
|
||||||
index,
|
index,
|
||||||
}: EnvironmentVariableProps) => {
|
}: AddEnvironmentVariableRowProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-1 p-2">
|
<div className="flex gap-1 p-2">
|
||||||
<div>
|
<div>
|
||||||
@ -41,4 +41,4 @@ const EnvironmentVariable = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EnvironmentVariable;
|
export default AddEnvironmentVariableRow;
|
@ -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, they’ll show up here.
|
||||||
|
</Typography>
|
||||||
|
</Card>
|
||||||
|
) : (
|
||||||
|
variables.map((variable: EnvironmentVariable, index: number) => {
|
||||||
|
return (
|
||||||
|
<EditEnvironmentVariableRow key={index} variable={variable} />
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</Collapse>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DisplayEnvironmentVariables;
|
@ -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;
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { useFieldArray, useForm } from 'react-hook-form';
|
import { useFieldArray, useForm } from 'react-hook-form';
|
||||||
|
import { Toaster } from 'react-hot-toast';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Typography,
|
Typography,
|
||||||
@ -9,7 +10,10 @@ import {
|
|||||||
Checkbox,
|
Checkbox,
|
||||||
} from '@material-tailwind/react';
|
} 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 = {
|
export type EnvironmentVariablesFormValues = {
|
||||||
variables: {
|
variables: {
|
||||||
@ -43,6 +47,12 @@ export const EnvironmentVariablesTabPanel = () => {
|
|||||||
control,
|
control,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getEnvironmentVariable = useCallback((environment: Environments) => {
|
||||||
|
return (environmentVariablesData as EnvironmentVariable[]).filter((item) =>
|
||||||
|
item.environments.includes(environment),
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography variant="h6">Environment variables</Typography>
|
<Typography variant="h6">Environment variables</Typography>
|
||||||
@ -61,13 +71,12 @@ export const EnvironmentVariablesTabPanel = () => {
|
|||||||
<form onSubmit={handleSubmit(() => {})}>
|
<form onSubmit={handleSubmit(() => {})}>
|
||||||
{fields.map((field, index) => {
|
{fields.map((field, index) => {
|
||||||
return (
|
return (
|
||||||
<div key={field.id}>
|
<AddEnvironmentVariableRow
|
||||||
<EnvironmentVariable
|
key={field.id}
|
||||||
index={index}
|
index={index}
|
||||||
register={register}
|
register={register}
|
||||||
onDelete={() => remove(index)}
|
onDelete={() => remove(index)}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<div className="flex gap-1 p-2">
|
<div className="flex gap-1 p-2">
|
||||||
@ -116,6 +125,21 @@ export const EnvironmentVariablesTabPanel = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
</div>
|
</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" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -66,7 +66,6 @@ const GeneralTabPanel = () => {
|
|||||||
App name
|
App name
|
||||||
</Typography>
|
</Typography>
|
||||||
<Input
|
<Input
|
||||||
id="input"
|
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
// TODO: Debug issue: https://github.com/creativetimofficial/material-tailwind/issues/427
|
// TODO: Debug issue: https://github.com/creativetimofficial/material-tailwind/issues/427
|
||||||
crossOrigin={undefined}
|
crossOrigin={undefined}
|
||||||
@ -77,7 +76,6 @@ const GeneralTabPanel = () => {
|
|||||||
Description (Optional)
|
Description (Optional)
|
||||||
</Typography>
|
</Typography>
|
||||||
<Input
|
<Input
|
||||||
id="input"
|
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
crossOrigin={undefined}
|
crossOrigin={undefined}
|
||||||
size="md"
|
size="md"
|
||||||
@ -87,7 +85,6 @@ const GeneralTabPanel = () => {
|
|||||||
Project ID
|
Project ID
|
||||||
</Typography>
|
</Typography>
|
||||||
<Input
|
<Input
|
||||||
id="input"
|
|
||||||
crossOrigin={undefined}
|
crossOrigin={undefined}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={PROJECT_ID}
|
value={PROJECT_ID}
|
||||||
|
@ -35,3 +35,16 @@ export enum Status {
|
|||||||
READY = 'Ready',
|
READY = 'Ready',
|
||||||
ERROR = 'Error',
|
ERROR = 'Error',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum Environments {
|
||||||
|
PRODUCTION = 'production',
|
||||||
|
PREVIEW = 'preview',
|
||||||
|
DEVELOPMENT = 'development',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnvironmentVariable {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
id: number;
|
||||||
|
environments: Environments[];
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user