mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 01:52:22 +00:00
Added tags to secrets in the dashboard
This commit is contained in:
@@ -18,19 +18,24 @@ import { useToggle } from '@app/hooks';
|
||||
import {
|
||||
useCreateServiceToken,
|
||||
useCreateWsEnvironment,
|
||||
useCreateWsTag,
|
||||
useDeleteServiceToken,
|
||||
useDeleteWorkspace,
|
||||
useDeleteWsEnvironment,
|
||||
useDeleteWsTag,
|
||||
useGetUserWsKey,
|
||||
useGetUserWsServiceTokens,
|
||||
useGetWsTags,
|
||||
useRenameWorkspace,
|
||||
useUpdateWsEnvironment
|
||||
} from '@app/hooks/api';
|
||||
|
||||
import { SecretTagsSection } from './components/SecretTagsSection';
|
||||
import {
|
||||
CopyProjectIDSection,
|
||||
CreateServiceToken,
|
||||
CreateUpdateEnvFormData,
|
||||
CreateWsTag,
|
||||
EnvironmentSection,
|
||||
ProjectNameChangeSection,
|
||||
ServiceTokenSection
|
||||
@@ -61,6 +66,11 @@ export const ProjectSettingsPage = () => {
|
||||
const createServiceToken = useCreateServiceToken();
|
||||
const deleteServiceToken = useDeleteServiceToken();
|
||||
|
||||
// tag
|
||||
const { data: wsTags } = useGetWsTags(workspaceID);
|
||||
const createWsTag = useCreateWsTag();
|
||||
const deleteWsTag = useDeleteWsTag();
|
||||
|
||||
// get user subscription
|
||||
const { subscriptionPlan } = useSubscription();
|
||||
const host = window.location.origin;
|
||||
@@ -207,6 +217,44 @@ export const ProjectSettingsPage = () => {
|
||||
return '';
|
||||
};
|
||||
|
||||
const onCreateWsTag = async ({ name }: CreateWsTag) => {
|
||||
try {
|
||||
const res = await createWsTag.mutateAsync({
|
||||
workspaceID,
|
||||
tagName: name,
|
||||
tagSlug: name.replace(" ", "_")
|
||||
});
|
||||
createNotification({
|
||||
text: 'Successfully created a tag',
|
||||
type: 'success'
|
||||
});
|
||||
return res?.data?.name;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to create a tag',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const onDeleteTag = async (tagID: string) => {
|
||||
try {
|
||||
await deleteWsTag.mutateAsync({ tagID });
|
||||
createNotification({
|
||||
text: 'Successfully deleted tag',
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
createNotification({
|
||||
text: 'Failed to delete the tag',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteServiceToken = async (tokenID: string) => {
|
||||
try {
|
||||
await deleteServiceToken.mutateAsync(tokenID);
|
||||
@@ -224,7 +272,7 @@ export const ProjectSettingsPage = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex flex-col px-8 text-mineshaft-50">
|
||||
<div className="container mx-auto flex flex-col px-8 text-mineshaft-50 dark dark:[color-scheme:dark]">
|
||||
{/* TODO(akhilmhdh): Remove this right when layout is refactored */}
|
||||
<div className="relative right-5">
|
||||
<NavHeader pageName={t('settings-project:title')} isProjectRelated />
|
||||
@@ -256,6 +304,12 @@ export const ProjectSettingsPage = () => {
|
||||
workspaceName={currentWorkspace?.name || ''}
|
||||
onCreateToken={onCreateServiceToken}
|
||||
/>
|
||||
<SecretTagsSection
|
||||
tags={wsTags || []}
|
||||
onDeleteTag={onDeleteTag}
|
||||
workspaceName={currentWorkspace?.name || ''}
|
||||
onCreateTag={onCreateWsTag}
|
||||
/>
|
||||
<div className="mb-6 mt-4 flex w-full flex-col items-start rounded-md border-l border-red bg-white/5 px-6 pl-6 pb-4 pt-4">
|
||||
<p className="text-xl font-bold text-red">{t('settings-project:danger-zone')}</p>
|
||||
<p className="text-md mt-2 text-gray-400">{t('settings-project:danger-zone-note')}</p>
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ export const EnvironmentSection = ({
|
||||
colorSchema="primary"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
>
|
||||
Add New Env
|
||||
Add New Environment
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import * as yup from 'yup';
|
||||
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
FormControl,
|
||||
IconButton,
|
||||
Input,
|
||||
Modal,
|
||||
ModalClose,
|
||||
ModalContent,
|
||||
ModalTrigger,
|
||||
Table,
|
||||
TableContainer,
|
||||
TBody,
|
||||
Td,
|
||||
Th,
|
||||
THead,
|
||||
Tr,
|
||||
} from '@app/components/v2';
|
||||
import { usePopUp } from '@app/hooks';
|
||||
import { WorkspaceTag } from '@app/hooks/api/types';
|
||||
|
||||
const createTagSchema = yup.object({
|
||||
name: yup.string().required().label('Tag Name'),
|
||||
});
|
||||
|
||||
export type CreateWsTag = yup.InferType<typeof createTagSchema>;
|
||||
|
||||
type Props = {
|
||||
tags: WorkspaceTag[];
|
||||
workspaceName: string;
|
||||
onDeleteTag: (tagID: string) => Promise<void>;
|
||||
onCreateTag: (data: CreateWsTag) => Promise<string>;
|
||||
};
|
||||
|
||||
type DeleteModalData = { name: string; id: string };
|
||||
|
||||
export const SecretTagsSection = ({
|
||||
tags = [],
|
||||
onDeleteTag,
|
||||
workspaceName,
|
||||
onCreateTag
|
||||
}: Props): JSX.Element => {
|
||||
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
|
||||
'CreateSecretTag',
|
||||
'deleteTagConfirmation'
|
||||
] as const);
|
||||
|
||||
const {
|
||||
control,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting }
|
||||
} = useForm<CreateWsTag>({
|
||||
resolver: yupResolver(createTagSchema)
|
||||
});
|
||||
|
||||
const onFormSubmit = async (data: CreateWsTag) => {
|
||||
console.log(19191, data);
|
||||
await onCreateTag(data);
|
||||
handlePopUpClose('CreateSecretTag');
|
||||
};
|
||||
|
||||
const onDeleteApproved = async () => {
|
||||
await onDeleteTag((popUp?.deleteTagConfirmation?.data as DeleteModalData)?.id);
|
||||
handlePopUpClose('deleteTagConfirmation');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-4 mb-4 flex w-full flex-col items-start rounded-md bg-white/5 p-6">
|
||||
<div className="flex w-full flex-row justify-between">
|
||||
<div className="flex w-full flex-col">
|
||||
<p className="mb-3 text-xl font-semibold">Secret Tags</p>
|
||||
<p className="text-sm text-gray-400">Every secret can be assigned to one or more tags. Here you can add and remove tags for the current project.</p>
|
||||
</div>
|
||||
<div>
|
||||
<Modal
|
||||
isOpen={popUp?.CreateSecretTag?.isOpen}
|
||||
onOpenChange={(open) => {
|
||||
handlePopUpToggle('CreateSecretTag', open);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
<ModalTrigger asChild>
|
||||
<Button color="mineshaft" leftIcon={<FontAwesomeIcon icon={faPlus} />}>
|
||||
Add New Tag
|
||||
</Button>
|
||||
</ModalTrigger>
|
||||
<ModalContent
|
||||
title={`Add a tag for ${ workspaceName}`}
|
||||
subTitle='Specify your tag name, and the slug will be created automatically.'
|
||||
>
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="name"
|
||||
defaultValue=""
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label='Tag Name'
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
>
|
||||
<Input {...field} placeholder="Type your tag name" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
type="submit"
|
||||
isDisabled={isSubmitting}
|
||||
isLoading={isSubmitting}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
<ModalClose asChild>
|
||||
<Button variant="plain" colorSchema="secondary">
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalClose>
|
||||
</div>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
<TableContainer className='mt-4'>
|
||||
<Table>
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Tag</Th>
|
||||
<Th>Slug</Th>
|
||||
<Th aria-label="button" />
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
{tags?.length > 0 ? (
|
||||
tags.map(({ _id, name, slug }) => (
|
||||
<Tr key={name}>
|
||||
<Td>{name}</Td>
|
||||
<Td>{slug}</Td>
|
||||
<Td className="flex items-center justify-end">
|
||||
<IconButton
|
||||
onClick={() =>
|
||||
handlePopUpOpen('deleteTagConfirmation', {
|
||||
name,
|
||||
id: _id
|
||||
})
|
||||
}
|
||||
colorSchema="danger"
|
||||
ariaLabel="update"
|
||||
>
|
||||
<FontAwesomeIcon icon={faTrashCan} />
|
||||
</IconButton>
|
||||
</Td>
|
||||
</Tr>
|
||||
))
|
||||
) : (
|
||||
<Tr>
|
||||
<Td colSpan={4} className="py-6 text-center text-bunker-400">
|
||||
No tags found for this project
|
||||
</Td>
|
||||
</Tr>
|
||||
)}
|
||||
</TBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteTagConfirmation.isOpen}
|
||||
title={`Delete ${
|
||||
(popUp?.deleteTagConfirmation?.data as DeleteModalData)?.name || ' '
|
||||
} api key?`}
|
||||
onChange={(isOpen) => handlePopUpToggle('deleteTagConfirmation', isOpen)}
|
||||
deleteKey={(popUp?.deleteTagConfirmation?.data as DeleteModalData)?.name}
|
||||
onClose={() => handlePopUpClose('deleteTagConfirmation')}
|
||||
onDeleteApproved={onDeleteApproved}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export {SecretTagsSection} from './SecretTagsSection'
|
||||
@@ -2,5 +2,6 @@ export { CopyProjectIDSection } from './CopyProjectIDSection';
|
||||
export { EnvironmentSection } from './EnvironmentSection';
|
||||
export type { CreateUpdateEnvFormData } from './EnvironmentSection/EnvironmentSection';
|
||||
export { ProjectNameChangeSection } from './ProjectNameChangeSection';
|
||||
export type { CreateWsTag } from './SecretTagsSection/SecretTagsSection';
|
||||
export { ServiceTokenSection } from './ServiceTokenSection';
|
||||
export type { CreateServiceToken } from './ServiceTokenSection/ServiceTokenSection';
|
||||
|
||||
Reference in New Issue
Block a user